libjgroups-java-2.12.2.Final.orig/0000755000175000017500000000000011647260573016557 5ustar moellermoellerlibjgroups-java-2.12.2.Final.orig/.classpath0000644000175000017500000000200311647260573020535 0ustar moellermoeller libjgroups-java-2.12.2.Final.orig/.gitignore0000644000175000017500000000017211647260573020547 0ustar moellermoeller*~ *.iws *.ipr *.iml .project .classpath .idea/ .idea classes/ build.properties dist/ atlassian* keystore/ tmp/ bla*.java libjgroups-java-2.12.2.Final.orig/.project0000644000175000017500000000057711647260573020237 0ustar moellermoeller JGroups org.eclipse.jdt.core.javabuilder org.eclipse.jdt.core.javanature libjgroups-java-2.12.2.Final.orig/CREDITS0000644000175000017500000000241511647260573017601 0ustar moellermoeller 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) - AUTHlibjgroups-java-2.12.2.Final.orig/INSTALL.html0000644000175000017500000003377611647260573020573 0ustar moellermoeller JGroups 2.8.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. jgroups.bat/jgroups.sh: convenience script to start demo programs (set the CLASSPATH etc) - see below
  3. Some sample configuration files, udp.xml, mping.xml etc
  4. CREDITS: list of contributors
  5. 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. log4j.jar
    6. 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.11 2009/12/02 13:00:10 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:
 
 




libjgroups-java-2.12.2.Final.orig/LICENSE0000644000175000017500000006347611647260573017604 0ustar moellermoeller 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! libjgroups-java-2.12.2.Final.orig/README0000755000175000017500000000534611647260573017452 0ustar moellermoeller 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 library 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 developer high-level abstractions (e.g. ReplicatedHashMap, an implementation of java.util.Map. The API (a channel) looks like a socket: there are methods for joining and leaving groups, sending and receiving messages, 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 list of protocols, through which each message passes. Each protocol implements an up() and down() method, and may modify, reorder, encrypt, fragment/unfragment, drop, or pass a message up/down. 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: ReplicatedHashMap implements java.util.Mapand implements all methods that change the map (clear(), put(), remove()). Those methods are invoked on all hashmap instances in the same group simultaneously, so that all hashmaps have the same state. A new hashmap uses a state transfer protocol to initially obtain the shared group state from an existing member. This allows for replication of data structures across processes. 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. 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. libjgroups-java-2.12.2.Final.orig/bin/0000755000175000017500000000000011647260573017327 5ustar moellermoellerlibjgroups-java-2.12.2.Final.orig/bin/concurrent.sh0000755000175000017500000000036711647260573022056 0ustar moellermoeller#!/bin/bash count=0 while [ $count -lt 20 ] do echo "Starting Draw instance #$count" # change the IP address to your system jgroups.sh org.jgroups.demos.Draw -props /home/bela/udp.xml -name $count & # sleep 1 count=$(($count+1)) donelibjgroups-java-2.12.2.Final.orig/bin/draw.bat0000644000175000017500000000200511647260573020751 0ustar moellermoeller@rem Convenience launcher for the Draw demo (contributed by Laran Evans lc278@cornell.edu) @echo off set CPATH=../classes;../conf; 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;print_local_addr=true) @echo on java -classpath %CPATH% %JAVA_OPTS% org.jgroups.demos.Draw -props %PROPS% libjgroups-java-2.12.2.Final.orig/bin/draw.sh0000755000175000017500000000230411647260573020622 0ustar moellermoeller#!/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" 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;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 $* libjgroups-java-2.12.2.Final.orig/bin/gaps.sh0000755000175000017500000000006011647260573020614 0ustar moellermoeller#!/bin/bash java org.jgroups.tests.CheckGaps $* libjgroups-java-2.12.2.Final.orig/bin/gossiprouter.sh0000755000175000017500000000075211647260573022437 0ustar moellermoeller#!/bin/sh CP=../classes:../conf for i in ../lib/*.jar do CP=$CP:$i done OPTS="-Dlog4j.configuration=file:$HOME/log4j.properties -Djava.net.preferIPv4Stack=true" OPTS="$OPTS -Dcom.sun.management.jmxremote" ## Uncomment for remote JMX access. Also modify JAVA_HOME/jre/lib/management/jmxremote.passwords # OPTS="$OPTS -Dcom.sun.management.jmxremote.port=7000 -Dcom.sun.management.jmxremote.ssl=false" java $OPTS -classpath $CP $JAVA_OPTS org.jgroups.stack.GossipRouter -port 12001 $* libjgroups-java-2.12.2.Final.orig/bin/jg0000755000175000017500000000012311647260573017651 0ustar moellermoeller # Author: Bela Ban #!/bin/bash JG=$HOME/JGroups jgroups.sh org.jgroups.demos.$*libjgroups-java-2.12.2.Final.orig/bin/jgroups.bat0000644000175000017500000000167011647260573021514 0ustar moellermoeller@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%\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.1.5 -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.%* libjgroups-java-2.12.2.Final.orig/bin/jgroups.sh0000755000175000017500000000300211647260573021352 0ustar moellermoeller# Author: Bela Ban #!/bin/bash JG=${JG-$HOME/JGroups} LIB=$JG/lib CP=$JG/classes:$JG/conf # If this is a bin dist, JARs are in the $JG directory. if [ ! -d $LIB ]; then LIB=$JG fi; for i in $LIB/*.jar do CP=$CP:$i done if [ -f $HOME/log4j.properties ]; then LOG="-Dlog4j.configuration=file:$HOME/log4j.properties" fi; JG_FLAGS="-Dresolve.dns=false -Djgroups.bind_addr=$IP_ADDR -Djboss.tcpping.initial_hosts=$IP_ADDR[7800]" JG_FLAGS="$JG_FLAGS -Djava.net.preferIPv4Stack=true -Djgroups.timer.num_threads=4" FLAGS="-server -Xmx600M -Xms600M -Xmn500M -Xss128K" FLAGS="$FLAGS -XX:CompileThreshold=10000 -XX:+AggressiveHeap -XX:ThreadStackSize=64K -XX:SurvivorRatio=8" FLAGS="$FLAGS -XX:TargetSurvivorRatio=90 -XX:MaxTenuringThreshold=31" FLAGS="$FLAGS -Xshare:off" JMX="-Dcom.sun.management.jmxremote" #EXPERIMENTAL="-XX:+UseFastAccessorMethods -XX:+UseTLAB" #EXPERIMENTAL="$EXPERIMENTAL -XX:+DoEscapeAnalysis -XX:+EliminateLocks -XX:+UseBiasedLocking" EXPERIMENTAL="$EXPERIMENTAL -XX:+EliminateLocks -XX:+UseBiasedLocking" #EXPERIMENTAL="$EXPERIMENTAL -XX:+AggressiveOpts -XX:+DoEscapeAnalysis -XX:+EliminateLocks -XX:+UseBiasedLocking -XX:+UseCompressedOops" #EXPERIMENTAL="$EXPERIMENTAL -XX:+UnlockExperimentalVMOptions -XX:+UseG1GC" #java -Xrunhprof:cpu=samples,monitor=y,interval=5,lineno=y,thread=y -classpath $CP $LOG $JG_FLAGS $FLAGS $EXPERIMENTAL $JMX $* #DEBUG="-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5000" java -classpath $CP $DEBUG $LOG $JG_FLAGS $FLAGS $EXPERIMENTAL $JMX $* libjgroups-java-2.12.2.Final.orig/bin/jt0000755000175000017500000000012311647260573017666 0ustar moellermoeller# Author: Bela Ban #!/bin/bash JG=$HOME/JGroups jgroups.sh org.jgroups.tests.$*libjgroups-java-2.12.2.Final.orig/bin/probe.bat0000644000175000017500000000050611647260573021127 0ustar moellermoeller@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.jar; set CP=%CLASSPATH%;%LIBS% java -cp %CP% org.jgroups.tests.Probe %* libjgroups-java-2.12.2.Final.orig/bin/probe.sh0000755000175000017500000000107011647260573020773 0ustar moellermoeller#!/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.jar #echo $CLASSPATH 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 $* libjgroups-java-2.12.2.Final.orig/bin/release_to_local_repo.sh0000755000175000017500000000125511647260573024212 0ustar moellermoeller#!/bin/bash # Uploads the artifacts in ./dist (JAR and src JAR) to the local repo ($HOME/.ms/jboss-repository) # so we can do local testing before uploading to the Nexus maven repo # Author: Bela Ban DIST=../dist POM=../pom.xml JAR=`find $DIST -name "jgroups-*.jar" | grep -v source` SRC_JAR=`find $DIST -name "jgroups-*.jar" | grep source` REPO=file:$HOME/.m2/jboss-repository FLAGS="-Dpackaging=jar -DrepositoryId=jboss-releases-repository" echo "Deploying $JAR to $REPO" mvn deploy:deploy-file -Dfile=$JAR -Durl=$REPO -DpomFile=$POM $FLAGS echo "Deploying $SRC_JAR to $REPO" mvn deploy:deploy-file -Dfile=$SRC_JAR -Durl=$REPO -DpomFile=$POM -Dclassifier=sources $FLAGS libjgroups-java-2.12.2.Final.orig/bin/release_to_nexus.sh0000755000175000017500000000143411647260573023234 0ustar moellermoeller#!/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 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 FLAGS="-Dpackaging=jar -DrepositoryId=jboss-releases-repository" echo "Deploying $JAR to $REPO" mvn deploy:deploy-file -Dfile=$JAR -Durl=$REPO -DpomFile=$POM $FLAGS echo "Deploying $SRC_JAR to $REPO" mvn deploy:deploy-file -Dfile=$SRC_JAR -Durl=$REPO -DpomFile=$POM -Dclassifier=sources $FLAGS libjgroups-java-2.12.2.Final.orig/build.bat0000644000175000017500000000146011647260573020347 0ustar moellermoeller @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.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 libjgroups-java-2.12.2.Final.orig/build.properties.template0000644000175000017500000000107611647260573023612 0ustar moellermoeller# 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],localhost[7801] jgroups.tunnel.gossip_router_hosts=localhost[12001] jgroups.udp.mcast_addr=232.10.10.10 jgroups.udp.mcast_port=45588 jgroups.udp.ip_ttl=5 # use true for IPv6 and false for IPv4 jgroups.useIPv6=false libjgroups-java-2.12.2.Final.orig/build.properties.template.ipv60000644000175000017500000000126211647260573024472 0ustar moellermoeller# Example of IPv6 based build.properties # Replace the IPv6 addresses with your own # 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=fe80::21b:21ff:fe07:a3b0%3 jgroups.tcpping.initial_hosts=fe80::21b:21ff:fe07:a3b0%3[7800],fe80::21b:21ff:fe07:a3b0%3[7801] jgroups.tunnel.gossip_router_hosts=fe80::21b:21ff:fe07:a3b0%3[12001] jgroups.udp.mcast_addr=ff0e::5:6:7 jgroups.udp.mcast_port=45588 jgroups.udp.ip_ttl=5 # use true for IPv6 and false for IPv4 jgroups.useIPv6=true libjgroups-java-2.12.2.Final.orig/build.sh0000755000175000017500000000232411647260573020216 0ustar moellermoeller#!/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=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 #echo "CP is ${CP}" libjgroups-java-2.12.2.Final.orig/build.xml0000644000175000017500000005354311647260573020412 0ustar moellermoeller build.xml file for JGroups. Needs Ant (jakarta.apache.org) to run libjgroups-java-2.12.2.Final.orig/conf/0000755000175000017500000000000011647260573017504 5ustar moellermoellerlibjgroups-java-2.12.2.Final.orig/conf/EncryptKeyStore.xml0000644000175000017500000000107511647260573023343 0ustar moellermoeller libjgroups-java-2.12.2.Final.orig/conf/EncryptNoKeyStore.xml0000644000175000017500000000225311647260573023637 0ustar moellermoeller libjgroups-java-2.12.2.Final.orig/conf/auth_X509.xml0000644000175000017500000000221011647260573021707 0ustar moellermoeller libjgroups-java-2.12.2.Final.orig/conf/auth_fixedlist.xml0000644000175000017500000000215111647260573023241 0ustar moellermoeller libjgroups-java-2.12.2.Final.orig/conf/auth_md5.xml0000644000175000017500000000204211647260573021732 0ustar moellermoeller libjgroups-java-2.12.2.Final.orig/conf/auth_regex.xml0000644000175000017500000000637411647260573022373 0ustar moellermoeller libjgroups-java-2.12.2.Final.orig/conf/auth_simple.xml0000644000175000017500000000160411647260573022541 0ustar moellermoeller libjgroups-java-2.12.2.Final.orig/conf/bare-bones.xml0000644000175000017500000000165411647260573022251 0ustar moellermoeller libjgroups-java-2.12.2.Final.orig/conf/bsh.xml0000644000175000017500000000203011647260573020775 0ustar moellermoeller libjgroups-java-2.12.2.Final.orig/conf/compress.xml0000644000175000017500000000247511647260573022071 0ustar moellermoeller libjgroups-java-2.12.2.Final.orig/conf/config.txt0000755000175000017500000000347611647260573021527 0ustar moellermoeller# # This file contains configuration for the JGroups performance tests (org.jgroups.tests.perf package) # # 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/testTopiclibjgroups-java-2.12.2.Final.orig/conf/encrypt-entire-message.xml0000644000175000017500000000232511647260573024622 0ustar moellermoeller libjgroups-java-2.12.2.Final.orig/conf/encrypt.xml0000644000175000017500000000222011647260573021706 0ustar moellermoeller libjgroups-java-2.12.2.Final.orig/conf/execution-service.xml0000644000175000017500000000474011647260573023674 0ustar moellermoeller libjgroups-java-2.12.2.Final.orig/conf/fast-local.xml0000644000175000017500000000536411647260573022263 0ustar moellermoeller libjgroups-java-2.12.2.Final.orig/conf/flush-tcp-nio.xml0000644000175000017500000000561211647260573022722 0ustar moellermoeller libjgroups-java-2.12.2.Final.orig/conf/flush-tcp.xml0000644000175000017500000000514411647260573022137 0ustar moellermoeller libjgroups-java-2.12.2.Final.orig/conf/flush-udp.xml0000644000175000017500000000436611647260573022146 0ustar moellermoeller libjgroups-java-2.12.2.Final.orig/conf/gossip-service.xml0000644000175000017500000001155211647260573023174 0ustar moellermoeller 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 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. GossipRequestTimeout long 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. RoutingClientReplyTimeout 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 Dumps the gossip table. dumpGossipTable java.lang.String 12000 30000 1000 120000 libjgroups-java-2.12.2.Final.orig/conf/jboss-service.xml0000644000175000017500000000357311647260573023014 0ustar moellermoeller jgroups:name=DemoChannel true true true DemoChannel libjgroups-java-2.12.2.Final.orig/conf/jg-magic-map.xml0000644000175000017500000000725611647260573022471 0ustar moellermoeller libjgroups-java-2.12.2.Final.orig/conf/jg-protocol-ids.xml0000644000175000017500000000677511647260573023261 0ustar moellermoeller libjgroups-java-2.12.2.Final.orig/conf/log4j.properties0000644000175000017500000000231011647260573022635 0ustar moellermoeller ## Root logger log4j.rootLogger=warn,console ## ConsoleAapender log4j.appender.console=org.apache.log4j.ConsoleAppender log4j.appender.console.Follow=true # 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=DEBUG log4j.logger.org.jgroups.protocols.MERGE2=DEBUG #log4j.logger.org.jgroups.protocols.pbcast.GMS=WARN #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 libjgroups-java-2.12.2.Final.orig/conf/manifest.mf0000644000175000017500000000016711647260573021642 0ustar moellermoellerManifest-Version: 1.0 Created-By: Apache Ant 1.6.5 Main-Class: org.jgroups.Version Implementation-Version: 2.12.2.Finallibjgroups-java-2.12.2.Final.orig/conf/mping.xml0000644000175000017500000000256011647260573021343 0ustar moellermoeller libjgroups-java-2.12.2.Final.orig/conf/multiplexer-service.xml0000644000175000017500000000124411647260573024237 0ustar moellermoeller jgroups.mux stacks.xml true true libjgroups-java-2.12.2.Final.orig/conf/multiplexer-xmbean.xml0000644000175000017500000001122411647260573024050 0ustar moellermoeller 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 libjgroups-java-2.12.2.Final.orig/conf/performancetestconfig.txt0000644000175000017500000000256211647260573024641 0ustar moellermoeller# 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=100000libjgroups-java-2.12.2.Final.orig/conf/persist.properties0000644000175000017500000000212611647260573023314 0ustar moellermoeller#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 =libjgroups-java-2.12.2.Final.orig/conf/sequencer.xml0000644000175000017500000000465311647260573022230 0ustar moellermoeller libjgroups-java-2.12.2.Final.orig/conf/settings.xml0000644000175000017500000000602511647260573022071 0ustar moellermoeller /home/bela/.m2/jboss-repository jboss-releases-repository USERNAME PASSWORD jboss-public-repository jboss-public-repository-group JBoss Public Maven Repository Group https://repository.jboss.org/nexus/content/groups/public/ default true never true never jboss-public-repository-group JBoss Public Maven Repository Group https://repository.jboss.org/nexus/content/groups/public/ default true never true never jboss-deprecated-repository jboss-deprecated-repository JBoss Deprecated Maven Repository https://repository.jboss.org/nexus/content/repositories/deprecated/ default true never false never jboss-public-repository libjgroups-java-2.12.2.Final.orig/conf/sfc.xml0000644000175000017500000000436211647260573021006 0ustar moellermoeller libjgroups-java-2.12.2.Final.orig/conf/smack.xml0000644000175000017500000000157511647260573021334 0ustar moellermoeller libjgroups-java-2.12.2.Final.orig/conf/smack_tunnel.xml0000644000175000017500000000114111647260573022706 0ustar moellermoeller libjgroups-java-2.12.2.Final.orig/conf/stacks.xml0000644000175000017500000004111111647260573021514 0ustar moellermoeller libjgroups-java-2.12.2.Final.orig/conf/tcp-nio.xml0000644000175000017500000000553011647260573021602 0ustar moellermoeller libjgroups-java-2.12.2.Final.orig/conf/tcp.xml0000644000175000017500000000536211647260573021022 0ustar moellermoeller libjgroups-java-2.12.2.Final.orig/conf/tcp_nio.xml0000644000175000017500000000577711647260573021701 0ustar moellermoeller libjgroups-java-2.12.2.Final.orig/conf/tcpgossip.xml0000644000175000017500000000410111647260573022235 0ustar moellermoeller libjgroups-java-2.12.2.Final.orig/conf/testng/0000755000175000017500000000000011647260573021010 5ustar moellermoellerlibjgroups-java-2.12.2.Final.orig/conf/testng/functional.xml0000644000175000017500000000121111647260573023667 0ustar moellermoeller libjgroups-java-2.12.2.Final.orig/conf/testng/stack-independent.xml0000644000175000017500000000132411647260573025132 0ustar moellermoeller libjgroups-java-2.12.2.Final.orig/conf/testng/testng-tcp-flush.xml0000644000175000017500000000164511647260573024747 0ustar moellermoeller libjgroups-java-2.12.2.Final.orig/conf/testng/testng-tcp-stress.xml0000644000175000017500000000107611647260573025147 0ustar moellermoeller libjgroups-java-2.12.2.Final.orig/conf/testng/testng-tcp.xml0000644000175000017500000000151611647260573023625 0ustar moellermoeller libjgroups-java-2.12.2.Final.orig/conf/testng/testng-udp-flush.xml0000644000175000017500000000163311647260573024746 0ustar moellermoeller libjgroups-java-2.12.2.Final.orig/conf/testng/testng-udp.xml0000644000175000017500000000146311647260573023630 0ustar moellermoeller libjgroups-java-2.12.2.Final.orig/conf/testng/time-sensitive.xml0000644000175000017500000000122411647260573024476 0ustar moellermoeller libjgroups-java-2.12.2.Final.orig/conf/tunnel.xml0000644000175000017500000000254111647260573021535 0ustar moellermoeller libjgroups-java-2.12.2.Final.orig/conf/udp-largecluster.xml0000644000175000017500000000542311647260573023514 0ustar moellermoeller libjgroups-java-2.12.2.Final.orig/conf/udp.xml0000644000175000017500000000505011647260573021016 0ustar moellermoeller libjgroups-java-2.12.2.Final.orig/doc/0000755000175000017500000000000011647260573017324 5ustar moellermoellerlibjgroups-java-2.12.2.Final.orig/doc/ENCRYPT.html0000644000175000017500000002357611647260573021353 0ustar moellermoeller

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. libjgroups-java-2.12.2.Final.orig/doc/ENCRYPT1_4.html0000644000175000017500000001504311647260573021645 0ustar moellermoeller

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.
     
     
     
     
libjgroups-java-2.12.2.Final.orig/doc/JmxSupport.txt0000644000175000017500000000635111647260573022225 0ustar moellermoeller // Author: Bela Ban 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 receivedlibjgroups-java-2.12.2.Final.orig/doc/PerformanceNotes.txt0000644000175000017500000000400011647260573023331 0ustar moellermoeller Check list for performance tuning (mainly gige) =============================================== 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). libjgroups-java-2.12.2.Final.orig/doc/README0000644000175000017500000000113211647260573020201 0ustar moellermoeller JGroups ships as a single JAR file: jgroups-x.y.z.jar. Simply add it to your classpath and start coding ! The JGroups version can be printed with java -jar jgroups-x.y.z.jar. There's a simple draw demo, start 2 or more instances: java -cp jgroups-x.y.z.jar org.jgroups.demos.Draw, and see if the members find each other. The sources for the core classes are in jgroups-sources.jar. If you want the complete sources, including unit tests, go to http://www.github.com/belaban/jgroups. JGroups is hosted at www.jgroups.org. For questions use either the dev or users mailing list. Enjoy, Bela Ban libjgroups-java-2.12.2.Final.orig/doc/RELEASE_INSTRUCTIONS0000644000175000017500000000443111647260573022355 0ustar moellermoeller Things to do before packaging a release --------------------------------------- Pre-release: - Do a cvs update -dP and commit all pending changes - Run all unit tests: ./build clean all-tests reports - 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 pom.xml * Note that the version needs to conform to the version standards, e.g. 2.6.2.GA or 2.6.4.CR2, 2.5.4.Beta1 - 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 jar - the distribution files are dist/jgroups-x.y.z.jar, dist/jgroups-sources.jar - 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. libjgroups-java-2.12.2.Final.orig/doc/RULES0000644000175000017500000000402211647260573020137 0ustar moellermoeller 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, GMS, FC, UNICAST, NAKACK, STABLE, CoordGmsImpl etc) before consulting with one of the project administrators. - 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 TestNG (www.testng.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 libjgroups-java-2.12.2.Final.orig/doc/ReleaseNotes-2.12.txt0000644000175000017500000000405711647260573023044 0ustar moellermoeller Release Notes JGroups 2.12.1 ============================ Author: Bela Ban JGroups 2.12.1 is API-backwards compatible with previous versions (down to 2.2.7). Below is a summary (with links to the detailed description) of the major new features, optimizations and bug fixes. New features ============ Sample config for large clusters -------------------------------- [https://issues.jboss.org/browse/JGRP-1307] Config udp-largecluster.xml was added, serves as blueprint for large clusters. This is work-in-progress. Optimizations ============= ExecutionService ---------------- [https://issues.jboss.org/browse/JGRP-1308] [https://issues.jboss.org/browse/JGRP-1310] Removed serialization overhead when assigning task to self Bug fixes ========= ENCRYPT: use alg provider for cipher creation ---------------------------------------------------- [https://issues.jboss.org/browse/JGRP-1311] During distribution of the encryption key from the master to each cluster member, use the configured asym_provider and sym_provider when getting Ciphers. TCP SocketFactory ignored ------------------------- [https://issues.jboss.org/browse/JGRP-1312] Despite setting a custom SocketFactory, it was ignored. Configuration sanity check: order of protocols was not enforced --------------------------------------------------------------- [https://issues.jboss.org/browse/JGRP-1305] E.g. STABLE being below NAKACK would not throw an exception Custom thread factories, thread pools and timer overridden in init() -------------------------------------------------------------------- [https://issues.jboss.org/browse/JGRP-1306] When setting a custom thread factory, pool or timer, they would get overwritten in TP.init() 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, Toronto, Canada Sanne Grinovero, Newcastle, Great Britain April 2011 libjgroups-java-2.12.2.Final.orig/doc/design/0000755000175000017500000000000011647260573020575 5ustar moellermoellerlibjgroups-java-2.12.2.Final.orig/doc/design/AUTH.txt0000644000175000017500000002236511647260573022107 0ustar moellermoeller Design of authentication protocol (AUTH) ======================================== Author: Roland Raez, Bela Ban, Chris Mills 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?libjgroups-java-2.12.2.Final.orig/doc/design/AddressTranslation.txt0000644000175000017500000000422711647260573025147 0ustar moellermoeller 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. libjgroups-java-2.12.2.Final.orig/doc/design/Bundling.txt0000644000175000017500000000204111647260573023075 0ustar moellermoeller Message bundling ================ // Author: Bela Ban Init: ----- - set start = 0 - set wait_time = 0 - set count = 0 On Message: ----------- - if start == 0: - set start = current time - if msg.length + count >= 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 libjgroups-java-2.12.2.Final.orig/doc/design/CLOUD_TCP.txt0000644000175000017500000000314411647260573022714 0ustar moellermoeller Changes to TCP to make it scale better in a cloud ================================================= Author: Bela Ban Problem: -------- In many cloud services (EC2, rackspace, GAE), IP multicasting is not allowed, therefore users of JGroups have to switch to TCPGOSSIP:TCP in conjunction with a GossipRouter for initial lookup. With TCP, we send a cluster wide message to N-1 nodes (with UDP, we send it only once). This doesn't scale with increasing clusters. Even if we parallelize this, all the traffic from us to each node has to go through the switch, taxing our full-duplex line to the switch. Solution: --------- Eliminate the N-1 problem by sending a cluster-wide message only to our neighbor. The neighbor then sends it to its neighbor and so on. Each message has a header with a TTL, which is equal to the cluster size. When a (cluster wide) message is received, we decrement the TTL in the header and forward it to our neighbor (unless the TTL is 0, then we discard it). For retransmissions, we send an XMIT-REQ to the original sender the first time, then to its neighbor, then to the next neighbor and so on. Implementation: --------------- This functionality can probably be done in a subclass of TCP (or maybe even be integrated into TP !). Update: IMO a better approach is to create a new protocol layered on top of the transport. This way, we can use this functionality with other transports as well. Take a look at Ron Levy's EPFL paper [1] for a similar approach. [1] http://infoscience.epfl.ch/getfile.py?docid=7701&name=paper&format=pdf&version=1 [2] infoscience.epfl.ch/record/149218/files/paper.pdflibjgroups-java-2.12.2.Final.orig/doc/design/ConcurrentConnectionEstablishment.txt0000644000175000017500000000351411647260573030226 0ustar moellermoeller // 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 CTlibjgroups-java-2.12.2.Final.orig/doc/design/ConcurrentStack.txt0000644000175000017500000001205511647260573024451 0ustar moellermoeller Concurrent stack ================ Author: Bela Ban 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 libjgroups-java-2.12.2.Final.orig/doc/design/ConcurrentStartupTest.txt0000644000175000017500000000514111647260573025704 0ustar moellermoeller Notes regarding ConcurrentStartupTest ===================================== // Author: Bela Ban 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.libjgroups-java-2.12.2.Final.orig/doc/design/DataCenterReplication.fig0000644000175000017500000000571711647260573025502 0ustar moellermoeller#FIG 3.2 Produced by xfig version 3.2.5 Landscape Center Inches Letter 100.00 Single -2 1200 2 6 1575 825 4575 3900 6 1800 1125 4050 3675 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 2925 1425 270 270 2925 1425 3075 1650 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 2100 2250 270 270 2100 2250 2250 2475 1 3 0 3 0 7 50 -1 -1 0.000 1 0.0000 3600 1950 270 270 3600 1950 3870 1950 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 3750 3000 270 270 3750 3000 3900 3225 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 2625 3075 270 270 2625 3075 2775 3300 4 0 0 50 -1 16 16 0.0000 4 240 450 2925 3600 udp\001 -6 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 3069 2364 1479 1479 3069 2364 4344 3114 -6 6 3375 4800 6375 9075 2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 3375 4800 6375 4800 6375 9075 3375 9075 3375 4800 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 3375 5400 6375 5400 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 3375 6000 6375 6000 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 3375 6675 6375 6675 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 3375 7350 6375 7350 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 3375 8025 6375 8025 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 3375 8550 6375 8550 4 0 0 50 -1 16 16 0.0000 4 195 885 4425 5175 RELAY\001 -6 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 10044 2289 1479 1479 10044 2289 11319 3039 1 3 0 3 0 7 50 -1 -1 0.000 1 0.0000 9150 1950 270 270 9150 1950 9420 1950 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 9900 1350 270 270 9900 1350 10050 1575 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 10800 1950 270 270 10800 1950 10950 2175 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 9450 3000 270 270 9450 3000 9600 3225 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 10725 3000 270 270 10725 3000 10875 3225 # tcp 2 4 0 2 0 7 50 -1 -1 0.000 0 0 15 0 0 5 9525 2400 3225 2400 3225 1500 9525 1500 9525 2400 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 2175 9450 8400 9450 2 1 0 2 0 7 50 -1 -1 0.000 0 0 7 1 0 2 1 1 4.00 60.00 120.00 5475 9450 5475 4050 2 1 0 2 0 7 50 -1 -1 0.000 0 0 7 1 0 2 1 1 4.00 60.00 120.00 5475 5175 7425 5175 4 0 0 50 -1 18 18 0.0000 4 225 2445 1800 600 Data Center NYC\001 4 0 0 50 -1 18 18 0.0000 4 225 2415 8775 525 Data Center SFO\001 4 0 0 50 -1 16 16 0.0000 4 240 450 9900 3525 udp\001 4 0 0 50 -1 18 16 0.0000 4 210 180 9075 2100 Y\001 4 0 0 50 -1 18 16 0.0000 4 210 180 3525 2025 X\001 4 0 0 50 -1 16 16 0.0000 4 195 195 2550 3150 C\001 4 0 0 50 -1 16 16 0.0000 4 195 180 2025 2325 B\001 4 0 0 50 -1 16 16 0.0000 4 195 180 3600 1425 E\001 4 0 0 50 -1 16 16 0.0000 4 195 195 10650 3075 U\001 4 0 0 50 -1 16 16 0.0000 4 195 195 3675 3150 D\001 4 0 0 50 -1 16 16 0.0000 4 195 180 2850 1575 A\001 4 0 0 50 -1 16 16 0.0000 4 195 180 9075 1425 S\001 4 0 0 50 -1 16 16 0.0000 4 195 165 9375 3150 T\001 4 0 0 50 -1 16 16 0.0000 4 195 180 10725 2100 V\001 4 0 0 50 -1 16 16 0.0000 4 195 255 9825 1425 W\001 4 0 0 50 -1 16 16 0.0000 4 210 360 6300 2025 tcp\001 4 0 0 50 -1 16 16 0.0000 4 195 990 7350 9300 Network\001 4 0 0 50 -1 16 16 0.0000 4 255 3465 6525 5025 Relaying to other data center\001 4 0 0 50 -1 16 16 0.0000 4 240 1320 4875 3900 Application\001 libjgroups-java-2.12.2.Final.orig/doc/design/DataCenterReplication.png0000644000175000017500000002567311647260573025524 0ustar moellermoeller‰PNG  IHDR—dËq pHYs M MÒέNtEXtSoftwareESP Ghostscript 815.04~8# IDATxœíÝÛ¶ª:¢Ðiµýÿ¿ìy°ŠÃD.d„ÞŸÖš*†$|½ßï? Ð?µ Àš×ëõí¥ºÝ°IÁ¢û„?×ee+,~dþþ‹êç]ïÝ[Ï*mx³'l\—])þíÍWTŽhÚ ßrb{3¾¸¨Ãå/çXyö~ãuKþûµ.Û˰¾½Š¯ÂÊ.€´Ð—¡…òœQj]¾¥õâ)B®‹Dz¿ßŸHx½^ßBâÛßÇYùÔÜJŸr±<ë /íg·øä’ÓÏ3¾9’âÍ^é`ªÅÊ2éÛ›W†¶:¨"à›yã¼%ÀŽ}jÑ–Ia“$^|Ûâ{Š,y}iÃǵëÃæßÎrN*Ü/[¸]ßûí¡Aµ«&¿½ùçl‹è*ÆVNô‡?né òý¶ðÅ&kñ={‹½²ä½gßÞ¼X?[ÑS×Åß#Ãwí¯ÿ9SŒI½Ÿ)Ïöo<_ì-ß²þ†SZþîª"€í®KÁõîõúµƒˆ¶±X_|rU£ŠÉô„êå©âçÎøßϬ"èÒÐãÜu\ûÔ‚ ÿ.øEE–¼>1ïÁ·6xYrDÝT‹âS-~m¹Ðr Š€8Ç2ìºð–\ü+.ZrèåµsÔMµ8<Õâ˜õŽø"á ØÒë(õ©- LoXŽõì×ûEUÎ…)nªÅú’×Ï ¶÷’Çï\yЙűÃc ±ë¼ÿ[Ó4)îqÇ] .¹ƒÓO}YVkªE‘2äîŽÀF»úÙûíÍ+œ¼­à5ø‚Kž¿~þ±·9]¹üzl?]Ø7Õ¢àžº±;þ'¡_‹MÊ·ñÏE¾ u¦`뀊ÿqWÙ¶¿¿øK)™â¦Z\±ÙÎÌo¯"²›ÝfªÅuK.±«€§úâ¦ZŸjq‘íU@Âwš§Z¸B³ñS‹ËY¼Øóó Ðö¯øù‘"KÞõ{—°±ŠHá¹›ÊS_ •€TRRIqH%Å •€TRRIqH%Å •€TRRIqHuê—Ià°]?B=ð#^cR€Ë-ö±<.¸¨øeRÊ›díÕY3ö‡¤› Œq”V—¦ s)ÀqaQÈc¤ø¿˜j°ÅÐZÆ5€Ÿ’Çû›ç¦¸©{å†÷D?+’¾Û™jpXg]ØAzœwžâM] iª0[¤‡Üv¡§)¦xDXFx²ÐT;)n­ûIñÜƸè›F)¨âS<7¼'ºY WPzÝ ¢6‚S<¢~çÀýzmQÏk¼fòRü9!×ø®tãõÊË‚›5Û 'm¹f+ñRÏ\kàZ˜]<Ýi®@‹ìgj(K«rLkõÖzŠ·V_u©  ˆû”YÚiÛÝíÔQkÔ p˜¤ N†ê—`Q UÓ8‡"°W#MëÍÏþTõ*mb‹Ž §]ªï@@ŠºÍÅÊ+ÓC½nl5”òûõ¬«3;¿=´sR«Ø­TVèfk‡,Uï‚øöЭJU×ÏÎЭÕ&'CÀXÅ6á|۞ؠݟhµ/Ë·±‘үʌ5R¥@]ÕGÑ‹|uhƒvg±óFZ ~ûðoS-€žTo] ~» ÿñEu®Æ×>IÜõíö! HO~Ý2opO±k\ŠÜÃB{·¡»>pXõ£þ¢T_¯cn(öÝõbªÅÍBÏ?€ª·Q× úªvuÉo¼_{ÝT‹Ú¥.Tý0¿<®j¯àa—–ü®Ëï£è÷,må[V^=vQ ô~Z9ÀçIÝîòümßúx‹ï mÇ®+ù?W,t¢§ÿûû{¿ß·­QÁo¹³ØÀ~FøøÕ×ëU}Úâ›'\ïÉĹ®þOñ%Nt™ŸíQ»»…X±+‡ÿj îwQ |mŠWðêh ‡žtÓÄõ±?]Ñ_˜âÕw¯K ‡¹%ƶ_‡žüåý~WlœWž²•þÄ­-Š·ÀW¥xõ¿Anæ–øØ{ú£… 9ðjmWÙµ¸dv[ ÞBÎ[ÜÒEÖËd7ȵñà_þÝÔ±ÿ)L ½QÁ¸|ŠË†‚.­IAO0ã“8wì×Uª.<¢nÏÈò¨“_èÃáf¶îåð±Å‹É_)ꥊ´À%S\„'ädïåðù[k¥[+Oœb)ÞT„.ÌÞ }D`kÝ+>‰¬”cmÈ·‡Ê­,ª© )â|ó[&Åû¨Ù¡*i‚ú³Š~!©5'›ß;žÀÊa+·[ÿ.“Ý eÇÏÏqÝà}ØëÑÕB ït¦ù-â=5ýí<›°J• rhÓ™³Ù#z±`Í–¶YgGÔ»iôªë¦™eÅáqõS)Þì¾uÛUÞfkà0Ènh:úko'ŽÕáå¿i¤ïýc;A튮K›Ž z¸Ùñëâ=Õé|êGñŸàËŸC¯n>»iؘœt0Å{ŠðÁ‡JƒSC7êrC–›ÃO{uæéiÍ}Ts´·¤x—:_£ó¡ý(AóÕ¡®ûÀɃQÝY^¼éЭëöºøö+4ëÏ Zÿà·}k¸A³à9<Ó¸á:Ðl:žá»*pwŠÕi­©Ñ]p -´´Õƒ¼…Jhß¾ï¯NW†Œ?‰ýÌÇ[£;÷k§¥:凃üXR|~qœímo·#ê?+b}Ä{¥·½¾{õ´ó ràïhh@> lO­è1«nÇì¶Ð3£;§Zø™à¤[Ú“§òóOV0÷^ž<â×P ù3#Ü|ux¸“OÑÛ×Ùšâéx‘J¯ qidž°ŽElI®M×Åûرn˜jÑA-}ãê8<\Çí[´Îg·M\7Õ¢à͑͆¥ ‡K]Ô_*Ûªhnö³áý=¢ÞGG|¬©©‹›gþÇÎ6p­Êš½–ÿˆÙms-LµØuJÛñÔ9à¯PéêVåirã•­˜ëWǤxñEEÖqûBƒÅOÞY=ËMV‡6å¶*múY-óP¯U‡í‹W´ë nñ ˆrÓª”u 6†QŠ‹ªq¥µ–â¶kYgF`&A¬xú¬;Å>¦úhUÚq¦†ú¼¹Ÿ5G½¢"QÖ/Ì¢U)«È“_ïŸ:ð5Åõ·.rr/iáñÂO›á—:ߨvЪTW0ò.j!¿-V_üÅèlñÊðZ•‚Š÷ZïìêHñË]½-«rºãPQ—­Jg®h$—¹œâ†Ó¯Pü\¯àÒ€*N6¶Z•óÒóN_üZÅG½Æž<¥U)èÒ¿gÌr!ÅÓOLB½þççaP`]ñvr¾@}ñ ]zÊyŽú宎aâGY|^æâ3¼†7¬ü.¡´* Œ¨·erp¶ßæ:Úo6ìÃ󶆴žüåcx¨äbÞSÑ Ÿqsfï´«þWªëgM^ÝHþ+Å §tÿAâ°| ù7¬œ òtZ•Š«¢…úчÞ8ç#qÎ,[Œû÷R¼-ó±¯Z%ÙΠ:з•q¬êçC—§øëß®þºvÜÿCÕw&`£c—/Ï|äXó«UYÑH¢ýÿõ‚ÅWæÄ.N¦åãÛuµÄ.öƆÑ2ÃécŸjÙ^‡¯> ÷Å?î÷Èä ïkä\æR×q~€m<‰~BòÍâ¤ôñæ/iÁ;põQ¯Uù¦ÁŽhÉûÅœq AÞB]pØÞ³ZÎß`6¹\5™—î~qÆ»} ˆê':C«[&ÅÏ$ñЙxø¾µ¸ú¯-qíz?p@›ãÇÿQ?¢Ãú™r´Y;'œWr컀Æioµ*Õµ6ªqöºxÁ>t—A~55”õœVåÌ„µíï¼:ìO¥xñòõä—ž8·vJÜ@«RÐÞЙÏ>^qÏ•âæžúÒ_z]¯Ž7\¨ó×¹hŸƒŽã)n>ÚFÕÒÓN™Á±Ç¿ü<±{f«r ;ÞNGüï“â¾ìÒòõ׫+>ö̃ hU *:÷Dø§Ø×þ¾ø·zù¹zß|þþøÎNq€“´*ç ›ÃëȈúÞ"Nž×ö÷È=fRc‡'Fz‚D+{_ÏdÉÇÊ£Uù(Ò#¿¿ÿym_|Ñ𨩧í.“3˜½§ÏŽ4`B«R֙ljÖzi…¸ÉéÞúµ¨ÅC0¦U)èØãD+öK¥x1Û·ââe…-#9iÏf€ÆõÚªÔ2]_¿~Q±w§øùfzã°C÷‘°kŠ@Çõ”ru«ò¨)MÍÆöÄ?7$å|÷³þÕ}ÛQTpŒVå ÍÖÞ#êó]ª¿_#-u»aOu´ øs² .ó*<µãàQÃMÀ£hßÚÔÜsÔ€v§ømG›ÚöØÚÝëï2h7*ôÅ»ß9ÐmZËî˜Ýöœ9êÃ@EÜ@À:mZ›®MñnuAtÃ]jí3»­¼â¿p?áHŠß0Á-½#;þõ6Yd7\ÑMñQ¿ôñ¨é>®©Z"€¯š}È(+üʵ&£ëOè—?aáŒöùäÔM_Wtš»éˆ?“Íë#ôz½NÍn+~Üþ ôí¶gñgç¨Ü#E8ìRàN³"A.Â`¯2³Û† ?ü³ó"¶ûô~‹ÍQnޕǺàpØ?eïüž®O–ì~D(¥üýâbîqùS_Ä6ÀإϾäiü Åh˜n&Å ÌÐk’âJŠ@ªÿüy®/dÒ¸›¾¥HqÊ0AàãöVŠ@*)©¤8@.SÄSÜþí›LBÒ§SÛª¸ü×P(ůÃwzx›“²úR¼iãÝÈO¾Bgüq³‡·9í¯þ|‡ùÿopz²Ï³²9æ{˜möð6'wõõÅ[t`ÿæ'Þ¿cµ³7CœFºOYmNqÑ«/ÅÛrf·ø|ª…Hñð6'kõ¿«Ÿoä¢ÅEö†FNíö=¼Íécõ§ßµ1ÚŸ‰°]Ùš¿m;fí0Ц*ÇQh›SJâêwÕω°¨xñªŸ-{x›“¸úß–?}êKÄCÜ>+³«¾>ëÕþª±)n<¼Í©µúaÏnû$ñá™mîdA'°¹%‡¦ÜÜ4=üÈílõ“Rü@|®µ ¿tjmeêÞæ„®þJ±R¼ÍÍP°êÛ\Á‹Ü\ù6w»×Åwyø=W žpƒ­)~ÿš‚߸?óéü:þüx‘µáPKÙ¶÷†–¼åæ¢ñÕ?öÙ}ñƧZ,vÇ[.ðÇ™Zýy± åà Ø((ÈÛosš]ýßíjvÛgóŒ·PãûÓ^óÏX:< ä|³{·¸sOºç»Z86®(ƒ±tèIÙƒ±Ù6ç­­þ™Ïv2»-]ñAž Ã Ø¢ñqõ¬6§©Õ?Yu»SüΫãÏD(«àÊÞù£xÀmš òĶ¢›Õ?Òï&È[Û󊬬‡Ž5ä¹mE «¾öÎn3Õâ"Ã^u n=ߨëámNÝÕ/3{©ÖP~õïjü,doñÚŸù”ÒB“ØY+qÿê7­~Qå»"ö¿õç÷^÷t߈ʇ»(ÈÇÿ½­ÍiÄ«_ò§&Û)ÊmßšR·B¡õOsõ¡Ú}l¯Ké)…ÝRÜÎ F¯ÔqÀÆ)¾ÉÂîŸ?m»Ïíñ+ÔdiüÁØÜ uŒúá1® Š ”ƒ7Å%³Š]`¯äãÿ>m"FYZˆænßEÛ¨Ÿ‰ßbû0Ç?tà rîê×mÂË}ÂnÔ› ºÑýášâ—n—³ÛLµ¨E ÜšfU—Ÿ£n7 Òý™;<¸Ÿöê6ö²‘zñÐ<Ûztt/’å_<#¿49kig­oÛWÝ/þóÁÃ7¯Wt{õÑ9½Ò£ú»+©¿;Ø ŸúÒÇnÔ%ÝKoç}ôÅVkþ†êk}s{í³ÛÒw£þx€<Ç™‡]rÌý ìÁßßîí—È›aCÀÓ ÝSÇþÕjÝwyŠÿ¥ù©“>|ªКÄF8KÅê½#ÅÿÒö¡o&¿¸«W³áëFŠÔ2t04eU¯Õû~Ó,ý¹©@´ÏDîê-Òº¡xóöó[Çiòï;›»{î_wS_üÃÉàýD80ÖZ;<™‚7>ÏXyiòññ¯/òß_]ðÁ­)þgªÅÚÙÉ€¦´ÖOб2äùsôíTÝßý)þ‘u™¼ñ¨EAÕ TÑZ§è k]øÌŸ} YlÙŠÑÚÌ¿RücÒ5ÿKغAE­erœÕןZ¿ý™ö“±œ•á¸ogýã1ÿñ—n_f­gïCEöóR:IñÁbÓÙÎîÒf©rMr}rí|ýÕÅ¥ýý{>2¿p3¼ºþäà•e.'À ·˜jчõ¿úIÓq®¿:1Oâqù؆û¶ÌI1ìÀaݦø˜©‰æ'@÷÷S×# ºG¤ø„©AÚÉËvJ0xbŠ/À šo”ZQºx]¼JIÆþS»°`%#ïŒOQ 4NŠlœ²ëÝåŸOšß•~þLâÛ2 ü¥Q§9+Q7¿µzåæ½½÷aèæ™¼4,yñöË8Ï©DZïÔêòaDRIqH%Å •ëâJ_RIqH%Å •€TRRIqøûóÃ'@&)©¤8ü—î8GŠƒüRIqH%Å •‡ÿghÈ"Åy:É ä’âJŠ@*)ÿb€"Åy4™ D“âJŠ@*)S†ÙRœç’Ö@º×ûý®]¨ïõr,yôÅ •€TRRIqH%Å •€TRRIqH%Å •€TRRIqH%Å •€TRRIqH%Å •€TRRIqH%Å •€TRRIqH%Å •€TRRIqH%Å •€TRRIqHõz¿ßµËп×ëU»ôÉñ ÷Oí<…Ö¶q¯WÞ­³CÀˆ:¤Ò‡ž­ô×7Ž=|–pr ¢ÈBj¹¹ðÑuÅý¤8ôo C®'^D˜¸(ðRr´µr¶Vž‡âð,C#ûz½n r-;\ÄuqH¥/L/Ÿ¯tçÚÇûɇ¿ŒÇZ‡µ8æ?~Û¼HãK“…X‘ñZÌ»X°Ÿ‹ú›­ìbyÖï5øVá[VÿÛ7n)óÊK‹ÛnïæØR3l!Åá‰cuxéÛHûb°­¿y¥‹ß8/Ì·%ÌàðŠŒßù-W¶xålfKIV^/ÿçê¯ÔÀJ™÷®Îöòì­62¢ý{Íüýê½-Æç§±^ÿ¢EÚx–°½‰ÿöÙy‘¶¿scçoÛ8$ðs}Vø7¯|éÉÕ™K‘E±N_ú7ïýý»t¬1·ÈÏl£o¾ íŠ7;sÚµX."Åáqæq»½ÅßøÎ]’îÒ•½.¹/]HñEñŽ?fq¼z¸À\®€M»nM·Tø±7¯ÐÏ"Å9n}æÑÏ÷üýûRÙ±KzÇþκ“õöðXâ^·}þÝÉo)µ(f·qÖ{døãüúè¢]_4,óá!QÖ|ÎÑÞ“ªùÒŠæÌgçE:³Êeµw}‹Œ®¯|i;5Ãvúâ”´~WÉ¥ß;ÿF­ÆOã{„Æ î·J›¼s¸¾>ßÖ‡«ýÛW¬¯ÅßÿέÈâ;7î?Û¿tËÇß³[±Vø·Õ_ß:ßÊ|ruVÊs`Qlá^½;Ü™gwZi憗¶4…›Ë×è–Ö-mî®OÜFm–¹øÉ“³1XaD “Ù·±Üɨ»¦?ŽË%°‹§¼Z : r~³R5ïb*lçº8g-vž&Íî–÷ü´ýýúsµœäw1v‘✵ּؓ`çìïË\¶ñKç¿‘JÕ¹mÛIq Û2©ø=l€ )N†o“Òiœ±q¸”§u+iÝæ­V|#Ñ¡8)T Ñ¡)ÎUæ—š[yhë߯©jÅ/À'Ñ'–yN¢Ãa$ï`à·}‰Û(¢ÌÏ3Ú_h“¾8P‡ä†ó¤8pÉ eIqàZ’®ã9êÀ…D8\JŠ@*)©¤8¤’âJŠ@*)©¤8¤’âJŠ@*O`½I¿ Ù7Ûˆ#Åoâ9”‹ø•Ï §€uH%Å •€TRRIqH%Å •€TRRIqH%Å •€TRRIqH%Å •€TRRIqH%Å •€TRRIqH%Å •€TRRIqH%Å •€TRRIqH%Å •€TRRIqH%Å •€TRRIqH%Å Õ?µ ð¯×«vøÁ6âHñ›¼ßïÚE`ÍëõŠÛFN;#êJŠ@*)©¤8¤’âJŠ@*)©¤8¤’âJŠ@*)©¤8¤’âJŠ@*)©¤8¤’âJŠ@*)©¤8¤’âJŠ@*)©¤8¤’âJŠ@*)©¤8¤’âJŠ@*)©¤8¤’âJŠ@*)©¤8¤’âJŠ@*)©¤8¤’âJŠ@*)©þ©]€§x½^µ‹À¶GŠßäý~×.k^¯WÜ6rÚQ€TRRIqH%Å •€TRRIqH%Å •€TRRIqH%Å •€TRRIqH%Å •€TRRIqH%Å •€TRRIqH%Å •€TRRIqH%Å •€TRRIqH%Å •€TRRIqH%Å •€TRRIqH%Å •€TRRIqHõOí<Åëõª]~°€8Rü&ï÷»vXóz½â¶‘ÓÀˆ:¤’âJŠ@*)©¤8¤’âJŠ@*)©¤8¤’âJŠ@*)©¤8¤’âJŠ@*)©¤8¤’âJŠ@*)©¤8¤’âJŠ@*)©¤8¤’âJŠ@*)©¤8¤’âJŠ@*)©¤8¤’âJŠ@*)©¤8¤’âJŠ@*)©¤8¤’âJŠ@ªjà)^¯Wí"ðƒmÄ‘â7y¿ßµ‹Àš×ë·œvFÔ •€TRRIqH%Å •€TRRIqH%Å •€TRRIqH%Å •€TRRIqH%Å •€TRRIqH%Å •€TRRIqH%Å •€TRRIqH%Å •€TRRIqH%Å Õ?µ ð¯×«vøÁ6âHñ›¼ßïÚE`ÍëõŠÛFN;#êJŠ@*)©¤8¤’âJŠ@*)©¤8¤’âJŠ@*)©¤8¤’âJŠ@*)©¤8¤’âJŠ@*)©¤8¤’âJŠ@*)©¤8¤’âJŠ@*)©¤8¤’âJŠ@*)©¤8¤’âêŸÚxŠ×ëU»ü`q^ï÷»v ¾×˱ä1¢©¤8PÙëõúv9cå¥û5Uøâ@Τ|層8Ð I {Iq  fÂî4²r³À¤§>¼mÞ­L`IDATøûø “WÇËÅ·eŽ?8_·%ÃýôÅV|qq\}ˆÌñÛ†¿ºøñíË¿gq™"œFHq !3x%ï×M:îã¯[\æJyD8-â@sãyWj.Ž®a Mq]hËçRôüù™ž÷dùÇ ÷¿%Èr!Åæ,ÆäöôüÛD¶3ehPꌨZÏÝŸOz¹nPýŠÂ1RhÑú¼³ÃóËÆÚÏ/Ó¸:ÕIq Q‹A>tÁïþš¤ÿ\þú2·||ûG 8×uàïÏ5N “¾8¤’âJŠ@*)©¤8¤’âJŠ@*)©ê<éÂÓŽ€^y|wò¼*Hõè’‰v`&IEND®B`‚‰PNG  IHDR—dËq pHYs M MÒέNtEXtSoftwareESP Ghostscript 815.04~8# ®IDATxœíÕA À0À¿çò¤U°ßöÌ, èü¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T¹8T]ºyÅÝ»¹_IEND®B`‚libjgroups-java-2.12.2.Final.orig/doc/design/DataCenterReplication.txt0000644000175000017500000001361211647260573025545 0ustar moellermoeller Replication between data centers ================================ Author: Bela Ban We have data centers in New York (NYC) and San Francisco (SFO). The idea is to replicate traffic from NYC to SFO asynchronously. In case of a site failure of NYC, all clients can be switched over to SFO and continue working with (almost) up-to-date data. The failing over of clients to SFO is outside the scope of this proposal, and could be done for example by changing DNS entries, load balancers etc. There is no replication going on from SFO to NYC by default, only when SFO becomes the primary site. The assumption is that there is no message between data centers which requires a response. This would require NAT functionlity, which we may provide in a future version. For the example, we assume that each site uses a UDP based stack, and replication between the sites use a TCP based stack, see figure DataCenterReplication.png. There is a local cluster, based on UDP, at each site and one global cluster, based on TCP, which connects the two sites. Each coordinator of the local cluster is also a member of the global cluster, e.g. member E in NYC (assuming it is the coordinator) is also member X of the TCP cluster. This is called a *relay* member. A relay member is always member of the local and global cluster. A relay member has a UDP stack which additionally contains a protocol RELAY at the top (shown in the bottom part of the figure). RELAY has a JChannel which connects to the TCP group, but *only* when it is (or becomes) coordinator of the local cluster. The configuration of the TCP channel is done via a property in RELAY. Any *multicast* message (we don't relay unicast messages) that is received by RELAY traveling up the stack is sent via the TCP channel to the other site. When received there, the corresponding RELAY protocol changes the destination of the message to null (those are multicast messages after all) and leaves the src (which might point to X if sent from NYC), then it sends the message down the stack, where it will get multicast to all members of the local cluster (including the sender). When a response is received which points to any non-local address (e.g. X), RELAY simply drops it. When forwarding a message to the local cluster, RELAY adds a header. When it receives the multicast message it forwarded itself, and a header is present, it does *not* relay it back to the other site but simply drops it. Otherwise, we would have a cycle. When a coordinator crashes or leaves, the next-in-line becomes coordinator and activates the RELAY protocol, connecting to the TCP channel and starting to relay messages. However, if we receive messages from the local cluster while the coordinator has crashed and the new one hasn't taken over yet, we'd lose messages. Therefore, we need additional functionality in RELAY which buffers the last N messages (or M bytes, or for T seconds) and numbers all messages sent. This is done by the second-in-line. When there is a coordinator failover, the new coordinator communicates briefly with the other site to determine which was the highest message relayed by it. It then forwards buffered messages with lower numbers and removes the remaining messages in the buffer. During this replay, message relaying is suspended. Therefore, a relay has to handle 3 types of messages from the global (TCP) cluster: (1) Regular multicast messages (2) A message asking for the highest sequence number received from another relay, and the response to this (3) A message stating that the other side will go down gracefully (no need to replay buffered messages) Example walkthrough ------------------- - C (in the NYC cluster, with coordinator E) multicasts a message - A, B, C, D and E receive the multicast - D (second-in-line) buffer the message (bounded buffer) - E is the relay. The byte buffer is extracted and a new message M is created. M's source is C, the dest is null (= send to all). Note that the original headers are *not* sent with M. If this is needed, we need to revisit. - X receives M, drops it (because it is the sender, determined by the header). - Y receives M, adds a RELAY header and sends it down the stack - T, U, V, W and S receive M and deliver it - Y does not relay M because M has a header - Should some member reply (to X), then RELAY at Y will drop the message Issues ------ Cluster RPCs in JBossCache -------------------------- - When we invoke a cluster RPC in JBossCache, the destination list is (for example) {A,C,D,E} - This list is sent with the RPC (as a header) - The receiver drops the request if its local address is not part of the destination list ==> This would cause all nodes in the SFO cluster to drop the cluster RPC request ! Buddy Replication ----------------- - When we replicate from B to C, the relay (e.g. E) doesn't know about this and will not replicate ! - How do we determine to whom we should replicate in SFO if we use buddy replication ? Identity -------- - What if we have members that have the same address in NYC and SFO ? - If we use TCP plus bin_port (7800), then every member starts at 7800 - If we happen to use IP local addresses in both SFO and NYC (e.g. 192.168.1.1), then we run into this issue Reception of message sent by self --------------------------------- - Because we relay messages only on *reception*, NOT on sending, a relay might not receive it own message - This is possible when the relay (e.g. E) calls RpcDispatcher.callRemoteMethods() with a target list which excludes itself ==> Solution: catch the message when sending, *not* receiving ! No, doesn't work ! The relay would have to be active on every node ! This is not feasible as this would require every node to have a TCP connection to NYC ! We still need to do it on reception not sending. However, messages cannot exclude the sender ***************************************************** *** UPDATE: this design is continued in RELAY.txt *** ***************************************************** libjgroups-java-2.12.2.Final.orig/doc/design/FILE_PING.txt0000644000175000017500000000302111647260573022666 0ustar moellermoeller Design of FILE_PING =================== Author: Bela Ban Goal ---- FILE_PING is a shared storage based discovery; during the discovery process, new nodes read the addresses of existing members from a shared store. Members write their addresses to the store and remove them on leaving the cluster. Design ------ FILE_PING takes as property a 'location' which is the location of a directory in a shared store, e.g. /share/jgroups. Each cluster X creates a subdirectory X, where all files for cluster X reside. A member writes its local address (plus physical address mappings) to a file which is named after the local address. Example: /share/jgroups/DemoCluster/linux-433234.dat. (Maybe we should use the UUID !) A new member reads all files in DemoCluster and sends the discovery request to all addresses resulting from this. When a member leaves, it removes its file from DemoCluster. We could use File.removeOnExit() to do this automatically. (However, kill -9 doesn't remove the file). We still need to periodically clean up members who crashed (kill -9). This could be done via an age out cache. Notes ----- In 2.6.x, we don't have logical addresses, therefore we don't need the discovery messages to ship logical-physical address mappings around. As an optimization, we could read all files and see if we have an element tagged as coordinator. If so, we could directly send a JOIN request to the coord, rather than sending discovery messages. If there is no coordinator, we go through the regular discovery message sending process.libjgroups-java-2.12.2.Final.orig/doc/design/FLUSH.txt0000644000175000017500000001702511647260573022224 0ustar moellermoeller FLUSH protocol for default stack ================================ Author: Bela Ban See http://jira.jboss.com/jira/browse/JGRP-82 Overview -------- Flushing means that all members of a group flush their pending messages. 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. When is it needed ? ------------------- (1) On 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. (2) On 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. Design ------ As another protocol FLUSH which handles a FLUSH event. FLUSH sits 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 protocol. 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. FLUSH protocol (state transfer) ------------------------------- - State requester sends SUSPEND event: multicast START_FLUSH to everyone (including self) - On reception of START_FLUSH: - send BLOCK event up the stack to application level - once BLOCK returns flip latch on FLUSH.down(), all threads invoking FLUSH.down() will block on mutex - get DIGEST from NAKACK below and append it to FLUSH_COMPLETED message - send FLUSH_COMPLETED back to flush requester - On reception of FLUSH_COMPLETED at flush coordinator: - Record FLUSH_COMPLETED in vector (1 per member) - Once FLUSH_COMPLETED messages have been received from all members: - allow thread that passed SUSPEND event to return - State requester sends RESUME event: multicast STOP_FLUSH message - On reception of STOP_FLUSH message: - flip latch off at FLUSH.down() and send UNBLOCK event up to application level FLUSH protocol (view installation for join/crash/leave) ------------------------------------------------------- - Coordinator initiates FLUSH prior to view V2 installation - Coordinator sends SUSPEND event: multicast START_FLUSH to every surviving/existing member in previous view V1 (including self) - On reception of START_FLUSH (existing members in V1): - send BLOCK event up the stack to application level - once BLOCK returns flip latch on FLUSH.down(), all threads invoking FLUSH.down() will block on mutex - get DIGEST from NAKACK below and append it to FLUSH_COMPLETED message - send FLUSH_COMPLETED back to flush requester - On reception of FLUSH_OK at each existing member in V1: - On reception of FLUSH_COMPLETED at flush coordinator: - Record FLUSH_COMPLETED in vector (1 per member) - Once FLUSH_COMPLETED messages have been received from all members: - allow thread that passed SUSPEND event to return - Coordinator stops FLUSH after view V2 installation - On RESUME event: multicast STOP_FLUSH message - On reception of STOP_FLUSH message: - flip latch off at FLUSH.down() and send UNBLOCK event up to application level Crash handling during FLUSH ---------------------------- - On suspect(P) - Do not expect any FLUSH message exchange from P (effectively exclude from FLUSH) - If P is the flush requester and Q is next member in view - Run the FLUSH protocol from scratch with Q as flush requester Notes ----- We are only concerned with multicast message in FLUSH. We always let unicast messages down the latch at FLUSH.down() since virtual synchrony is only relevant to multicasts. FLUSH on view change -------------------- Whenever there is a view change, GMS needs to use FLUSH to acquiesce the group, before sending the view change message and returning the new view to the joiner. Here's pseudo code of the coordinator A in group {A,B,C}: - On reception of JOIN(D): - FLUSH(A,B,C) - wait until flush returns, we flush A, B and C, but onviously not D - Compute new view V2={A,B,C,D} - Multicast V2 to {A,B,C}, wait for all acks - Send JOIN_RSP with V2 to D, wait for ack from D - RESUME sending messages This algorithm also works for a number of JOIN and LEAVE requests (view bundling): - On handleJoinAndLeaveRequests(M, S, L, J), where M is the current membership, S the set of suspected members, L the set of leaving members and J the set of joined members: - FLUSH(M) - Compute new view V2=M - S - L + J - Multicast V2 to M-S, wait for all acks - Send LEAVE_RSP to L, wait for all acks - Send JOIN_RSP to J, wait for all acks - Resume sending messages FLUSH with join & state transfer -------------------------------- http://jira.jboss.com/jira/browse/JGRP-236 (DONE) Needs description (TODO) Description below is outdated - needs revisiting (TODO) Design change: flushing and block() / unblock() callbacks --------------------------------------------------------- The change consists of moving the blocking of message sending in FLUSH.down() from the first phase to the second phase: Phase #1: send START_FLUSH across the cluster, on reception everyone invokes block(). We do *not* (as previously done) block messages in FLUSH.down(). Method block() might send some messages, but when block() returns (or Channel.blockOk() is called), FLUSH_OK will be broadcast across the cluster. Because FIFO is in place, we can be sure that all *multicast* (group) messages from member P are received *before* P's FLUSH_OK message. Phase #2: send FLUSH_OK across the cluster. When every member has received all FLUSH_OKs from all other member, every member blocks messages in FLUSH.down(). Phase #3: send FLUSH_COMPLETED to the initiator of the flush Phase #4: send STOP_FLUSH across the cluster, e.g. when state has been transferred, or member has joined Use case: block() callback needs to complete transactions which are in the 2PC (two-phase commit) phase - Members {A,B,C} - C just joined and now starts a state transfer, to acquire the state from A - A and B have transactions in PREPARED or PREPARING state, so these need to be completed - C initiates the flush protocol - START_FLUSH is broadcast across the cluster - A and B have their block() method invoked, they broadcast PREPARE and/or COMMIT messages to complete existing prepared or preparing transaction, and blocks or rolls back other transactions - A and B have to wait for PREPARE or COMMIT acks from all members, they block until these are received (inside of the block() callback) - Once all acks have been received, A and B return from block(), they now broadcast FLUSH_OK messages across the cluster - Once everyone has received FLUSH_OK messages from A, B and C, we can be assured that we have also received all PREPARE/COMMIT/ROLLBACK messages and their acks from all members - Now FLUSH.down() at A,B,C starts blocking sending of messages Issues: - What happens is a member sends another message M *after* returning from block() but *before* that member has received all FLUSH_OK messages and therefore blocks ? - This algorithm only works for multicast messages, not unicast messages because multicast and unicast messages are not ordered with respect to each other libjgroups-java-2.12.2.Final.orig/doc/design/FLUSH2.txt0000644000175000017500000001152511647260573022305 0ustar moellermoeller FLUSH2 design ============= Author: Bela Ban 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 account Concurrent flushes ------------------ - Concurrent flushes to overlapping member sets will cause only 1 flush to succeed, and all others to fail - A failed flush set the flushInProgress flag back to false on all members on which it successfully set it - Algorithm: - Send a START-FLUSH to all members in the target set - Every member sets the flushInProgress flag to true - If this succeeds, the member sends back an OK - Else a FAIL is sent back - If we received OKs from all members, startFlush() succeeded and returns true - If 1 FAIL is received: - Send an ABORT-FLUSH to all members which took part in the flush, causes flushInProgress to be set to false - startFlush() failed and returns false Concurrent total flushing (members={A,B,C,D}) --------------------------------------------- - In general, concurrent total flushes are not allowed - Concurrent flushes on the same member: - The first thread to call Channel.startFlush() runs the flush phase - Subsequent threads to call Channel.startFlush() block until the current flush phase has completed (Channel.stopFlush()) - Concurrent flushes on different members: - A.startFlush() and B.startFlush() are called concurrently - A sends a START-FLUSH to all members, and so does B - When a START-FLUSH is received, a flag 'flushing-in-progress' is set - If flushing-in-progress cannot be set (because another flush protocol is running), we send back a START-FLUSH-FAIL. (We might add a small timeout to block until flushing-in-progress can be acquired) - On reception of a START-FLUSH-FAIL, we 'roll back' the flush, setting all of our acquired 'flushing-in-progess' flags back to false - SUMMARY: either A fails, or B fails, or both A and B fail in a concurrent flush phase (similar to concurrent transactions) Concurrent partial flushing (members={A,B,C,D}) ----------------------------------------------- - In general, concurrent partial flushes are not allowed (same as for concurrent total flushes) libjgroups-java-2.12.2.Final.orig/doc/design/FlowControl.txt0000644000175000017500000000417511647260573023615 0ustar moellermoeller// 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 mutexlibjgroups-java-2.12.2.Final.orig/doc/design/GossipRouterChanges-1.8.txt0000644000175000017500000001075111647260573025604 0ustar moellermoeller Changes to GossipRouter in 2.8 ============================== Author: Bela Ban Motivation ---------- The changes are mainly due to logical addresses and shared transport. TCPGOSSIP currently doesn't work, as the GossipRouter doesn't return logical addresses *and* their associated physical addresses, but only returns logical addresses (UUIDs) [1]. In TUNNEL, RouterStubs are conceptually associated with a single channel, and cannot process traffic from multiple channels on top of a shared transport. [1] https://jira.jboss.org/jira/browse/JGRP-1005 [2] https://jira.jboss.org/jira/browse/JGRP-924 Design overview --------------- A RouterStub will simply establish a TCP connection with a GossipRouter, but doesn't send the CONNECT message on socket establishment. This is typically done in init() of TUNNEL (or TCPGOSSIP). On channel connection, a CONNECT(groupname, logical_addr, logical_name, physical_addresses) is sent to the GossipRouter. On a GET-MEMBERS(groupname), the GR returns a list of elements. On a ROUTE(groupname, logical_addr, message), the GR pick the single destination (if logical-addr is not null) and routes the message. If logical_addr is null, then the GR routes the message to all members keyed by groupname. The GossipRouter maintains the following data structures: - RoutingTable: - A hashmap with groupnames as keys and hashmaps as values - The 2nd level hashmaps have logical addresses as keys and ConnectionHandlers as values - A ConnectionHandler represent a physical connection to a client. It has a thread listening for messages on the input stream, and it also has a list of logical_addrs from which it received CONNECT messages. - When the socket in the ConnectionHandler is closed by the peer, we remove all entries associated with all logical_addrs of ConnectionHandler from the other data structures. - AddressMappings: - Maintains a hashmap with logical_addrs as keys and the physical_addrs as value - This is used to map logical addresses to their physical addresses, and is only used by TCPGOSSIP Implementation -------------- RouterStub ---------- - When started, the RouterStub establishes a socket to the GossipRouter's address:port given - When the connection is closed by the GR, the stub goes into reconnecting mode. When reconnected, it issues a notification so registered listeners can send a CONNECT (see below) to the GR again - When the CONNECT event is received by TUNNEL / TCPGOSSIP, RouterStub.connect() is called with - the groupname - the logical address - the logical name - a list of physical addresses (might be null, only used by TCPGOSSIP) - This generates a CONNECT message which is sent to the GossipRouter GossipRouter ------------ - On accept(): - Create a new ConnectionHandler on a new thread (from the thread pool) - This stores the client socket, input and output stream, then listens on the input stream [- We might add the ConnectionHandler to a separate list, just to keep track of open connections] - On peer (RouterStub) closing the socket: - The ConnectionHandler gets an exception when listening on the input stream - We remove the entries in connection-list from all tables - On CONNECT(groupname, logical_addr, logical_name, physical_addrs) [received by ConnectionHandler.run()]: - The ConnectionHandler adds the logical address to its connection-list - An entry is added to RoutingTable under groupname, and the value (hashmap) is updated: - key is the logical addr, value the ConnectionHandler (this) - If CONNECT ships with non-null physical_addrs, then an entry is added to AddressMappings - On DISCONNECT(logical_addr): - Remove the entry for logical_addr from all tables (RoutingTable, AddressMappings) - Do *not* close the socket in ConnectionHandler: others may still be connected through the same connection ! - On GET_MEMBERS(groupname): - Grabs the members for groupname (from RoutingTable and AddressMappings) and passes them back, for each member: - The logical address - The logical name - (if available) a list of physical addresses - On ROUTE(groupname, dest, message): - If dest == null (multicast): - Grab all ConnectionHandler's from RoutingTable for groupname and send the message to all - Else (unicast) - Find the ConnectionHandler keyed by 'dest' and send the message to it TUNNEL ------ - On stub reconnect: - Send a CONNECT message. With a shared transort, this has to be done for all channels sharing the transportlibjgroups-java-2.12.2.Final.orig/doc/design/JDBC_PING.txt0000644000175000017500000000424111647260573022656 0ustar moellermoeller Design of JDBC_PING =================== Author: Sanne Ginovero Goal ---- JDBC_PING is a discovery protocol making use of a shared database; during the discovery process, new nodes read the addresses of existing members from a JDBC connection. The design is derived from FILE_PING, and the implementation extends it as there are many similarities. As with FILE_PING, also with JDBC_PING members write their addresses to the store and remove them on leaving the cluster. Usage ------ A database is needed, and a single table will be used. Something like the following is expected: CREATE TABLE JGROUPSPING ( own_addr varchar(200) NOT NULL, cluster_name varchar(200) NOT NULL, ping_data varbinary(5000) DEFAULT NULL, PRIMARY KEY (own_addr, cluster_name) ) It's possible to change the table definitions, make sure to update the three SQL statements accordingly using the appropriate configuration properties: initialize_sql - to customize the table creation SQL instructions insert_single_sql - to insert a new row delete_single_sql - to delete a single row select_all_pingdata_sql - to load all ping_data having a specific cluster_name Connection properties to be set are either: 1) JDBC direct connection connection_url connection_username connection_password connection_driver 2) via a JNDI registered DataSource datasource_jndi_name Design ------ Each node connects to the same database, and reads all Addresses related to his same cluster to find it's peers, and adds himself to the list. At stop(), a node attempts to cleanup the table by removing himself. Members who crashed should be removed by the same strategy of FILE_PING, actually it should inherit each eventual improvement. Warning: While the node stores the PingData instance of himself, to be retrieved by other nodes, it will nullify the View, as there's no interest in restoring the View instance and this could potentially not fit in the fixed column dimension allocated for a serialized PingData, or at least this variable is not fixed so storing View might make it hard to select a proper size. So consider that a restored PingData will have lost the information about the original View. libjgroups-java-2.12.2.Final.orig/doc/design/JoinAndStateTransfer.txt0000644000175000017500000000267311647260573025376 0ustar moellermoellerImplementation 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? libjgroups-java-2.12.2.Final.orig/doc/design/LargeClusters.txt0000644000175000017500000000221711647260573024117 0ustar moellermoeller Considerations for large clusters ================================= Author: Bela Ban JIRA: https://jira.jboss.org/browse/JGRP-100 Discovery --------- ConcurrentHashMap ----------------- CCHMs have a default initial capacity (16), load factor (0.75) and concurrency level (16). These are OK for most scenarios, but we have to investigate whether these values are sufficient for 1000 node clusters. When for example 1000 threads from different senders access the same CCHM, we need to make sure we don't have high contention, ie. by spreading a 1000 senders over 16 buckets. Investigate whether we should add CCHM initial sizes, load factors and concurrency levels as properties. With ergonomics [1], we could for example set bucket sizes for CCHMs dynamically, e.g. based on cluster size. [1] https://jira.jboss.org/jira/browse/JGRP-1037 Misc ---- - MERGE2: num_initial_members in Discovery should be large, so that we detect partitions sooner - GMS: view bundling should be enabled, and max_bundling_time should be a bit larger than the default, as this will allow for more concurrent joins and leaves before starting the view installation processlibjgroups-java-2.12.2.Final.orig/doc/design/LargeMessages.txt0000644000175000017500000000544311647260573024066 0ustar moellermoeller Large messages in large clusters ================================ Author: Bela Ban Requirements ------------ - Large cluster - 1 sender, many receivers - Sender sends messages between 10MB and 200MB, in bursts Goals: ------ - Receivers discard message (purge it from memory) as soon as it has been delivered to the application - Sender discards message as soon as it has received acks from all receivers or a timeout has elapsed --> We don't want to keep 200MB messages in buffers longer than necessary - Don't allow single receiver to bog down the whole cluster (in times and memory foot print) Issues with existing protocols: ------------------------------- - We cannot use STABLE for agreement, so that a sender can purge a message, because one or more slow receivers could prevent stability messages to be sent. This is because STABLE requires agreement from all group members, and slow members won't be able to agree, at least not fast enough. - We therefore wait for agreement from all members, but if a timeout elapses, the sender will purge the message anyway - Receivers are not guaranteed to receive all messages: if the sender purged a message after not having received acks from all members, the receiver will not receive that message - However, even with message loss, all *received* messages will be delivered in order - We cannot use SMACK, which uses positive acks for each message, because that would lead to too many acks. If we for example send a 200MB message and it is fragmented into 2000 fragments, we don't want to send an ack / fragment, but we only want to send an ack per message, so after the entire 200MB message has been received Design: ------- - A new protocol ACK, layered above FRAG2 (or any other fragmentation protocol) - When sending a message, ACK creates a list of members from which it needs to receive acks and starts a timer - When ACK receives a message it sends an ack back to the sender - When all acks have been received, ACK sends down a STABLE event, which will cause the sender's NAKACK protocol to purge the message - When the timer kicks in, it returns if all acks have been received or (if not) sends a stability message around, which causes all receivers to ask for retransmission of messages they haven't received yet. The timer cancels itself after N attempts or when all acks have been received - The receivers also start a timer when the first retransmission of a message occurs - If a message has not been received after a timeout, the receivers will flag that message as not-received and stop retransmission requests for it. When the special not-received message is removed, it won't be passed up Flow control: ------------- - We cannot have a sender block on a credit missing from a slow member - Solution: use max_block_times in FC, which is already implemented libjgroups-java-2.12.2.Final.orig/doc/design/LogicalAddresses.txt0000644000175000017500000003316411647260573024555 0ustar moellermoeller Logical addresses ================= Author: Bela Ban JIRA: https://jira.jboss.org/jira/browse/JGRP-129 The address chosen by each node is essentially the IP address and port of the receiver socket. However, for the following reasons, this is not good enough: - Reincarnation: if we use fixed ports (bind_port is set), then a restarted (or shunned) node will have the same address. If other nodes in the cluster don't clear their states before the reincarnated node comes up again, we'll have issues (see JGRP-130 for details) - NIC failover: a NIC goes down, we want to continue sending/receiving on a different NIC - The sender sends on all available NICs (send_on_all_interfaces="true"). This means that -if we take the receiver's datagram packet's address to be the identity of the sender - we get N different identities; 1 for each interface the message is sent on - Network Address Translation: the sender's address might get changed by the NAT DESIGN: - A logical address consists of a unique identifier (UUID) and a logical name. The name is passed to JGroups when a channel is created (new JChannel(String logical_name, String props)). If logical_name is null, JGroups picks a logical name (which is not guaranteed to be unique though). The logical name stays with the channel until the latter is destroyed - A UUID is represented by org.jgroups.util.UUID, which is a subclass of Address and consists of the least and most significant bits variables copied from java.util.UUID. All other instance variables are omitted. - The isMulticastAddress() method uses 1 bit out of the 128 bits of UUID (Maybe we can remove this method and always represent multicast addresses as nulls, so UUIDs would only be used to represent non multicast addresses) - All UUIDs have a reference to a static table which contains the mappings between logical names and UUIDs (classloader issues ?) - The logical name is used in UUID.toString(), and the least and most significant bits are used for equals() and hashCode() - A UUID is created on channel connect, deleted on channel disconnect and re-created on channel connect. Since it is re-created on every connect(), it will prevent reincarnation issues Transport (TP) -------------- TP maintains a cache of mappings between UUIDs and physical addresses. Whenever a message is sent, the physical address of the receiver is looked up from the cache. A UUID can have more than 1 physical address. We envisage providing a pluggable strategy which picks a physical address given a UUID. For example, a plugin could load balance between physical addresses. To exchange cache information, we'll use the existing Discovery protocol (see below). When a physical address for a given UUID is not present, a sender discards (or queues, TBD) the message and broadcasts a WHO-HAS message. Receivers then broadcast (or unicast) the UUID-physical address mapping. The discovery phase needs to return logical *and* physical addresses: on reception of a discovery request, we return our logical address (UUID) and the physical address(es) associated with it, plus the logical name. On reception of a discovery response, the transport places the physical addresses returned into its cache (if not yet present). See the scenarios below for details as to why this is needed. UDP startup ----------- - The joiner multicasts a discovery request with its UUID, logical name and physical address(es) - The receivers' transports add this information into their caches - Each receiver unicasts a discovery response, containing the coordinator's address, plus its own UUID, logical name and physical address(es) - On reception of a discovery response, the transport adds this information to its cache if not yet there TCPPING:TCP startup ------------------- - The problem is that TCPPING has as initial_hosts the *physical* (not logical) addresses listed ! - The joiner sends a discovery request to all physical addresses listed in initial_hosts - The dest_addr field of a discovery request message is the physical address - The transport usually expects UUIDs as addresses and finds the associated physical address in the cache - However, in this case, the transport re-uses the physical address (dest_addr) in the message, bypasses the translation UUID --> physical address, and nulls dest_addr in Message - The destination address is now *null*. This works because Discovery/PING/TCPPING don't check the messages's dest_addr field ! (Otherwise 'null' would be interpreted as a multicast destination !) - On the receiver side, the destination is not used for discovery - The response is sent back to the sender (Message.getSrc()), this is a UUID and will be translated back into a physical address TCPGOSSIP:TCP startup --------------------- - The joiner asks the GossipRouter for a list of nodes for a given cluster name - The information returned contains a list of nodes, for each node: - The logical address (UUID) - The logical name - The physical address(es) associated with the UUID - The joiner's transport adds this information to its cache - Then each node of the initial membership is sent a discovery request - The rest is the same as for UDP TCPGOSSIP:TUNNEL startup ------------------------ - Same as for TCPGOSSIP:TCP, but here we don't really need the physical addresses, because we send every request to the GossipRouter anyway (via our TCP connection to it) - The physical address will simply be ignored ARP-like functionality ---------------------- - This is handled by Discovery - We'll add WHO-HAS, I-HAVE and INVALIDATE message handling to Discovery - The Discovery and transport protocols communicate via events - When the transport wants to send a message whose destination UUID is not in its cache, it sends a (non-blocking) WHO-HAS up the stack which is handled by Discovery. Meanwhile the message is queued (bounded queue). - Discovery sends a WHO-HAS(UUID) message (multicast if PING, sent to initial_hosts in TCPPING, or sent to the GossipRouter if TCPGOSSIP) - On reception of I-HAVE, Discovery sends the result down the stack via an I-HAVE event - When the transport receives an I-HAVE event, it updates its local cache and then tries to send the queued messages - A discovery request also ships the logical name and physical address(es) - A discovery response also contains these items - When a discovery request or response is received, the cache is updated if necessary - When a channel is closed, we send an INVALIDATE(UUID) message around, which removes the UUID from UUID.cache and the transport's cache in all cluster nodes Runtime scenarios ----------------- Startup ------- - The transport stores the logical name (generated if user didn't pass one to the JChannel() constructor) - On connect: - The UUID is generated (local address) and associated with the logical name (in UUID.cache) - The local socket is created and associated with the UUID in the transport's cache - On disconnect: the local_addr (UUID) is nulled and removed from the transport's cache and UUID.cache - On close: an INVALIDATE(UUID) message is broadcast so every node can remove UUID from the transport's cache and from UUID.cache Discovery --------- - Discovery fetches the local_addr (UUID), logical name and physical address(es) from the transport via a GET_PHYSICAL_ADDRESS - A discovery request containing this info is sent out (multicast, unicast via TCP or sent to the GossipRouter) - The receivers (Discovery protocols) fetch this info from the message and send it down to the transport (via a SET_PHYSICAL_ADDRESS), which adds it to its cache and to UUID.cache - The receivers then fetch their own local information from the transport and send it along with the discovery response - On reception of the discovery response, the requester extracts this info from the message and sends it down to its transport, which adds it to its own local cache Sending of a message with no physical address available for UUID ---------------------------------------------------------------- - The transport queues the message - The transport sends a GET_PHYSICAL_ADDRESS *event* up the stack - The Discovery protocol sends out a GET_MBRS_REQ *message* (via multicast, TCP unicast, or to the GossipRouter) - The receivers fetch their local information from the transport (exception: the GossipRouter has this information in its local loookup cache), and return it with a GET_MBRS_RSP message - On reception of the GET_MBRS_RSP message, a SET_PHYSICAL_ADDRESS event is sent down the stack to the transport - The transport updates its local cache from the information - The transport sends all queued messages if the UUID are now available On view change -------------- - The coordinator's Discovery protocol broadcasts an INVALIDATE(UUID) message for each node which left the cluster - On reception, every receiver sends down a REMOVE_PHYSICAL_ADDRESS(UUID) - The transport then removes the mapping for the given UUID IDs instead of UUIDs -------------------- - Implemented in TP or a separate protocol (ID ?) - The coordinator dishes out IDs (shorts) for new nodes - The IDs are always increasing - Every new ID is broadcast across the cluster, so everyone knows the highest IDs - An ID is associated with a UUID, and UUIDs are also associated with physical addresses (2 tables) - When we send a message to dest UUID, we lookup the ID associated with UUID. If found, we send the ID (a short) rather than the UUID - On reception, if an ID is found, we create an IdAddress, which work on the short field for equals() and hashCode() - IdAddress.equals() and IdAddress.compareTo() can compare to both IdAddress *and* UUIDs: in the latter case, they fetch the ID from the table given the UUID as key and do the comparison. UUIDs can also compare to IdAddresses - We should probably either add a PhysicalAddress interface, which inherits from Address, and have physical addresses implement PhysicalAddress (so we can do an instanceof PhysicalAddress), or have an isPhysicalAddress() method - ID canonicalization should be configurable: we can enable or disable it - This could be used by an ID protocol (sitting on top of the transport), which maintains a UUID-ID table and *replaces* dest and src addresses for messages coming in and going out - This protocol would replace an UUID dest with an IdAddress and provide the physical address as well (?) Shared transport and UUIDs -------------------------- - With a shared transport, every channel has a local_addr: the transport itself cannot have a local_addr anymore. The reason is that a channel could get shunned, leaves the cluster and then reconnects. However, because the address of the shared transport hasn't changed, the rejoined member has the same address, thus chances of reincarnation are higher - When a channel connects, it creates a new local_addr (UUID) and sends it down via SET_LOCAL_ADDRESS. The transport then adds the UUID/physical address mapping to its cache - When we send a multicast message (dest == null), but don't have a multicast capable transport (e.g. UDP.ip_mcast=false) or TCP/TCP_NIO, then we simply send it to all *physical* addresses in the transport's UUID cache. If we don't currently have all physical addresses, that's fine because MERGE2 will eventually fetch all physical addresses in the cluster TODOs ----- - Multicast messages should always be null, so Address.isMulticastAddress() should not be needed anymore - GossipRouter: we need to register logical *and* physical addresses, plus the logical name - Find all uses of IpAddress and change them to SocketAddress (if possible), or try to use Address rather than IpAddress - UUID: generate a nice name from UUID if the logical name is not defined. Alternative: don't use the UUID to generate the logical name, but maybe the host name and a random short - Util.writeAddress()/writeAddresses(): currently optimized for IpAddress, change to UUID. Example: View.writeTo()/readFrom() - How are we going to associate logical names to channels when a shared transport is used ? - Marshalled size of UUID: 18 bytes, IpAddress: 9 bytes. Size in memory (without superclass, additional_data, on 64 bit machine): 16 bytes for UUID, 32 bytes for IpAddress ! So while marshalling takes more space for UUID, it is quite compact in memory ! If it turns out that UUIDs generate too much overhead on the wire (bandwidth), we should think about canonicalizing UUIDs to shorts: run an agreement protocol which assigns cluster-wide unique shorts to UUIDs, and send the shorts rather than the UUIDs around ! - UDP.initial_hosts also needs to send to physical addresses - Implement getPhysicalAddress() in all transports - BasicTCP.handleDownEvent(): view has to be handled in ProtocolAdapter and connections have to be closed there, too --> The semantics of retainAll() have to be inspected: do we handle UUIDs or PhysicalAddresses ? --> Should we switch to a model where ConnectionTable never reaps connections based on view changes, but based on idle time ? Ie. reap a connection that hasn't been used for more than 30 seconds - TCPPING.initial_hosts="A,B": how will we find out about C, D and E ? - Shared transport - Move setSourceAddress() from TP to JChannel ? - Check whether thread names in a shared transport are still correct. - Do thread names use the logical or UUID name of a channel ? - Is Global.DUMMY still needed when we set the local address top down ? - The 'members' variable in TP is a union of all memberships in the shared transport case - In BasicTCP, we need to change retainAll() to use physical addresses rather than UUIDs stored ib TP.members - Dito for suspected_mbrs - In general, change all addresses in TCP to PhysicalAddresses. Or should we use logical addresses ? libjgroups-java-2.12.2.Final.orig/doc/design/MERGE.new.txt0000644000175000017500000000573711647260573023001 0ustar moellermoeller New merging algorithm ===================== Author: Bela Ban JIRAs: https://jira.jboss.org/jira/browse/JGRP-948, https://jira.jboss.org/jira/browse/JGRP-940 Goal ---- The digests on a merge should not include 'hearsay' information, e.g. if we have subpartitions {A,B,C} and {D,E,F}, then B (for example) should only return its own digest, and not the digests of A and C. Design ------ On a merge, the merge leader sends a MERGE_REQ to all subpartition coordinators (in the example, this would be A and D). On reception of a MERGE_REQ, every coordinator multicasts a GET_DIGEST to its subpartition and waits N ms for all replies. Example: D wait for replies from itself, E and F. It returns the aggregated information for D, E and F after N milliseconds. A (the merge leader) waits for responses from itself and D. If it doesn't receive them within N ms, it cancels the merge. After reception of responses from itself and D, it makes sure it has digests for 6 nodes A, B, C, D, E and F. It not, it cancels the merge. Otherwise, A computes a MergeView and consolidates the digests, then unicasts the MERGE_VIEW to itself and D. Each coordinator then multicasts the new view in its subpartition (same as now). Consolidating the digests should be simple because we only have 1 entry for each member. However, in the following case we could have multiple overlapping entries: A: {A} B: {A,B} Here's B's view includes A, so B will return digests for A and B, and A will return the digest for itself (A). In this case, let's log a warning and make the digest for A be the maximum of all sequence numbers (seqnos). Example: A's digest: A: 7 20 (20) B's digest: A: 2 10 (10) B: 5 25 (25) The merged digest for A would then be A: 7 20 (20). This should actually not happen because: - B's digest is a result of contacting every member of its subpartition (A and B) - If A is reachable from B, and B gets a response, the response will actually contain the correct seqnos 7 20 (20) - However, if A's digest (for itself) contains 7 21 (21), because a message was sent in the meantime, then the maximum would be 7 21 (21) which is correct Merging of local digests (NAKACK.mergeDigest()) ----------------------------------------------- - We have {A,B} and {C,D} - The digest is A:15, B:7, C:10, D:9 - Before receiving the merge digest A and B multicast more messages, A's seqno is now #20 and B's #10 - A receives the digest: A:20, B:10, ... + A:15, B:7, ... = A:20, **B:10** ================ ==> We currently do NOT overwrite our own digest entry, but overwrite all others. This is incorrect: we need to not overwrite our own digest (as is done currently), but for all other members P, we need to: - If P's seqno is higher than the one in the digest: don't overwrite - Else: reset P's NakReceiverWindow and overwrite it with the new seqno ==> If we didn't do this, in the example above, B's seqno would be #7 whereas we already received seqnos up to #10 from P, so we'd receive seqnos #7-#10 from P twice ! libjgroups-java-2.12.2.Final.orig/doc/design/MERGE4.txt0000644000175000017500000000330311647260573022260 0ustar moellermoeller MERGE4 ------ Author: Bela Ban JIRA: https://jira.jboss.org/jira/browse/JGRP-937 Goal ---- To merge asymmetric partitions, e.g. A: {A,B,C}, B: {A,B}, C: {A,C}. The current merge algorithm wouldn't merge these partitions because B and C are not coordinators and therefore don't participate in the merge. The implementation involves creating a new protocol (MERGE4) and modifying GMS and CoordGmsImpl. MERGE4 ------ - Periodically runs a discovery - The Discovery protocol is changed such that each participant additionally returns its view - If the consolidated discovery responses result in more than 1 view, send up a MERGE event with a list of all (different) views GMS / CoordGmsImpl ------------------ - On MERGE(V1,V2,V3,...): - Determine all coordinators, e.g. A for V1 and V2, D for V3 - Determine the membership for each coord, e.g. {A,B,C} for A and {D,E,F} for D - Send a MERGE-REQ to A and D - The MERGE-REQ for A includes {A,B,C}, the MERGE-REQ for D includes {D,E,F} - A and D fetch digest information from {A,B,C} (A) and {D,E,F} (D) respectively - This information is consolidated in A (merge leader) and installed in both partitions - Example: - A: V3 {A,B,C} - B: V1 {A,B} - C: V2 {A,C} - D: V7 {D,E,F} - E: V6 {D,E} - F: V5 {D,F} - MERGE4 sends up a MERGE(V1,V2,V3,V5,V6,V7) - CoordGmsImpl determines that the coords are A and D and the merge leader is A - A sends a MERGE-REQ(A,B,C} to A and a MERGE-REQ(D,E,F} to D - A fetches the digest and view for A,B,C and returns it to A - D fetches the digest and view for D,E,F and returns it to A - A consolidates the digests and views (into a MergeView) and tells A and D to install the new MergeView plus digests libjgroups-java-2.12.2.Final.orig/doc/design/MERGE_View_Separation.txt0000644000175000017500000000735011647260573025361 0ustar moellermoeller Separation of merges from view handling ======================================= Author: Bela Ban JIRA: https://jira.jboss.org/jira/browse/JGRP-1009 Goal: ----- We don't want concurrent merges and view changes (join or leave processing). During a merge, join and leave requests should be discarded. Likewise, during a join/leave processing, merge requests should be discarded. We already do discard join or leave requests during a merge (ViewHandler is suspended), but not the other way round. JGRP-1009 leads to spurious merges: when a join or leave request is being processed and the view is being disseminated, if a merge is permitted to occur during this, the merge leader might detect different views (due to them arriving at different members at different times, maybe a few millisconds apart) and initiate a merge. This won't happen when the merge is discarded during view processing / installation. Design: ------- There are 3 types of events we need to take into acccount: - The coord receiving a JOIN/JOIN_WITH_STATE/LEAVE/SUSPECT event (anything which leads to a new view being installed) - The coord receiving a MERGE event (e.g. from MERGE2 somewhere below in the stack) - The coord receiving a MERGE-REQ request (from a coord in a different partition) On reception of a JOIN/JOIN_WITH_STATE/LEAVE/SUSPECT event ---------------------------------------------------------- - If the ViewHandler is suspended --> discard the event - Else, add the event - When starting to process the event(s) in the queue: - Suspend the ViewHandler - Start the Resumer task (which resumes the ViewHandler after N seconds) - Resume the ViewHandler when done processing On reception of a MERGE event ----------------------------- - If the ViewHandler is suspended --> discard the event - Else: - If there are JOIN/LEAVE/etc events in the queue: discard the event and start the processing of the queued events - Else: - Process the MERGE event - Suspend the ViewHandler - Start the Resumer task (which resumes the ViewHandler after N seconds) - Resume the ViewHandler when done processing On reception of a MERGE-REQ --------------------------- - If the ViewHandler is suspended --> reject the MERGE-REQ (send MERGE-RSP with merge_rejected=true) - Else: - Suspend the ViewHandler - Start the Resumer task - When the merge is done --> resume the ViewHandler - On Resumer timeout: resume the ViewHandler (this could happen for instance when a remote coord starts a merge, then crashes before merge completion) Resuming the view handler: -------------------------- The following 4 cases can resume the view handler #1 JOIN/LEAVE ------------- - When the view has been installed by the coord, the view handler is resumed - The view handler needs to be resumed also if the view installation fails, e.g. due to a failed flush #2 MERGE -------- - On competion of the merge (successful or failed), the view handler is resumed #3 MERGE-REQ ------------ - Resume the view handler when getting a MergeView - Special case: if the merge leader crashes before merge completion: - On a MERGE-REQ, record the merge leader's address (when suspending the view handler) - When the merge completes, null the merge leaders address again - When we get a view excluding the merge leader, and the leader's address is non-null, resume the view handler and null the merge leader's address #4 The Resumer kicks in ----------------------- - The Resumer is started whenever the view handler is suspended - It resumes the view handler when run - When the view handler is resumed regularly, the Resumer is stopped Issues: ------- - What if the client sends a JOIN_WITH_STATE, the coord processes the JOIN, but suspends the queue after it and before processing the GET_STATE ?libjgroups-java-2.12.2.Final.orig/doc/design/MarshalingFormat.txt0000644000175000017500000000320111647260573024570 0ustar moellermoeller // Author: Bela Ban 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 (MULTICAST) 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.libjgroups-java-2.12.2.Final.orig/doc/design/Multiplexer.txt0000644000175000017500000003627011647260573023660 0ustar moellermoeller Multiplexing functionality =========================== Author: Bela Ban 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 libjgroups-java-2.12.2.Final.orig/doc/design/NAKACK.txt0000644000175000017500000000603611647260573022273 0ustar moellermoeller 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 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 ! libjgroups-java-2.12.2.Final.orig/doc/design/NullDestAddresses.txt0000644000175000017500000000204311647260573024725 0ustar moellermoeller Nulling of destination addresses for optimized marshalling ========================================================== 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 ! libjgroups-java-2.12.2.Final.orig/doc/design/NullingSrcAddresses.txt0000644000175000017500000000164011647260573025255 0ustar moellermoeller Loopback adaptor issues on Windows ---------------------------------- JIRA: http://jira.jboss.com/jira/browse/JGRP-79 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)libjgroups-java-2.12.2.Final.orig/doc/design/PartialStateTransfer.txt0000644000175000017500000000555411647260573025451 0ustar moellermoeller // 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. libjgroups-java-2.12.2.Final.orig/doc/design/ProbabilisticBroadcast.txt0000644000175000017500000001331111647260573025746 0ustar moellermoeller Probabilistic Broadcast for JGroups =================================== 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 libjgroups-java-2.12.2.Final.orig/doc/design/RELAY.fig0000644000175000017500000000512011647260573022136 0ustar moellermoeller#FIG 3.2 Produced by xfig version 3.2.5 Landscape Center Inches Letter 100.00 Single -2 1200 2 6 3375 4800 6375 9075 2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 3375 4800 6375 4800 6375 9075 3375 9075 3375 4800 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 3375 5400 6375 5400 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 3375 6000 6375 6000 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 3375 6675 6375 6675 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 3375 7350 6375 7350 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 3375 8025 6375 8025 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 3375 8550 6375 8550 4 0 0 50 -1 16 16 0.0000 4 195 885 4425 5175 RELAY\001 -6 6 1575 825 4575 4200 6 3750 1950 4350 2550 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 4050 2250 270 270 4050 2250 4200 2475 4 0 0 50 -1 16 16 0.0000 4 195 180 3975 2400 A\001 -6 6 2100 1500 2700 2100 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 2400 1800 270 270 2400 1800 2550 2025 4 0 0 50 -1 16 16 0.0000 4 195 195 2325 1875 C\001 -6 6 2475 2850 3075 3450 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 2775 3150 270 270 2775 3150 2925 3375 4 0 0 50 -1 16 16 0.0000 4 195 180 2700 3225 B\001 -6 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 3069 2364 1479 1479 3069 2364 4344 3114 4 0 0 50 -1 16 16 0.0000 4 240 450 2850 4125 udp\001 -6 6 10125 2700 10725 3300 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 10425 3000 270 270 10425 3000 10575 3225 4 0 0 50 -1 16 16 0.0000 4 195 165 10350 3075 F\001 -6 6 10200 1200 10800 1800 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 10500 1500 270 270 10500 1500 10650 1725 4 0 0 50 -1 16 16 0.0000 4 195 180 10425 1650 E\001 -6 6 8700 1950 9300 2550 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 9000 2250 270 270 9000 2250 9150 2475 4 0 0 50 -1 16 16 0.0000 4 195 195 8925 2325 D\001 -6 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 9969 2289 1479 1479 9969 2289 11244 3039 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 2175 9450 8400 9450 2 1 0 2 0 7 50 -1 -1 0.000 0 0 7 1 0 2 1 1 4.00 60.00 120.00 5475 9450 5475 4050 2 1 0 2 0 7 50 -1 -1 0.000 0 0 7 1 0 2 1 1 4.00 60.00 120.00 5475 5175 7425 5175 2 4 0 3 0 7 50 -1 -1 0.000 0 0 7 0 0 5 9525 2925 3600 2925 3600 1950 9525 1950 9525 2925 4 0 0 50 -1 18 18 0.0000 4 225 2445 1800 600 Data Center NYC\001 4 0 0 50 -1 18 18 0.0000 4 225 2415 8775 525 Data Center SFO\001 4 0 0 50 -1 16 16 0.0000 4 195 990 7350 9300 Network\001 4 0 0 50 -1 16 16 0.0000 4 255 3465 6525 5025 Relaying to other data center\001 4 0 0 50 -1 16 16 0.0000 4 240 1320 4875 3900 Application\001 4 0 0 50 -1 16 16 0.0000 4 210 360 6300 2325 tcp\001 4 0 0 50 -1 16 16 0.0000 4 240 450 9750 4050 udp\001 4 0 0 50 -1 2 18 0.0000 4 195 225 3975 2850 X\001 4 0 0 50 -1 2 18 0.0000 4 195 225 8925 2850 Y\001 libjgroups-java-2.12.2.Final.orig/doc/design/RELAY.png0000644000175000017500000002434111647260573022163 0ustar moellermoeller‰PNG  IHDR’d-!V5 pHYs M MÒέNtEXtSoftwareGPL Ghostscript 8.62ƒ-ÏEIDATxœíÝÛ¶¢¸PéQÿÿËžOÓ7¹È s>u×V d‘±{¿ß/ ÁŸ» À’®ëæþtï¸kP°èAàÏ}Y8 “o¿¾TýtÑ мåÀ¸¥¯öJb‡•û²)¶ç^\¤rÄ6@Õú ˜ †õÝøä¦v—ç{;ûʳõÏÛòë×¾¬/Ãòñ:¾ ÿ|?׫aÄõ]†ÊsD©}™‹çÉk‚}ÜÛˆô~¿?ÐuÝ\*Ìýû÷[Þ5¶0jœ,ÏòÆ¿·ösà{pËé½=±]í]ÖG,4‰ñŸæ^¼0[Õ@sÆóšÄÚ÷®IkVr ¢wòe“¯)²åå­õoß×7.Ï„Ï]ÖlUx´}°4G>wîC“iSMνøç‰è*¾-\Ù÷ÿ¸æêÿø,îÏ"MvY“¯ÙZì…-o½˜{ñdý\Ù‹º·ýþÒÿã¦CÞýëH1}¤<ë?ñx±×|Êò v¬Cy]UEë{ËèåÛuöÅFÛƒ;·¬)¸½<·ø9KóýßϬ"hR?¦ÜÔ®÷½kGÁúÿ.øAE¶¼<Í0£ß>=Yr’Üúˆâë#~ÎꬹY²£Š€8ûBë¼´î·\ü#NÚrÊä¹+ɭؽ>bŸå¡ö$i X3Ì(õ®5LïXöÝ—B¥*çÄØ¶>byËË—ëÇÁ߯\xИÉÙÁ}‘°éB®k”aÓÌâ&·œxáq+ÓîZQ¤ Aç°Ï¦‘tÁÍνxთ¼^pËãÖÏlíNî¨îÛàØ‰£më# žš+Ü@Ã&›ùÜ÷ç¦vßÊýîpÆoœì×/WZÖ»Ô–—÷eŸ…Ð"cª+&É/xצ-§¬xíš7Ô†çÌÞ nDNÆÆÂÍʹwÍ}îšÈopß*¥Ÿû²o˯™}Ù½µ¹²Üà·³bÛúˆª*·{…é\Žîxב‚-/â)þ›Ê¶þõÅ7¸[ÉØ¶>âŒãt||}P¹bKÒ¬8oË3ueP§C£më#Н8Éú* f…¿ö½>bÇ]–•ïšÜÎä ›ŸwqÖÄÏ·Ùò¦Oܺ…•U@µ<ÒbxÜ ÄÛCl@ ± 1Ä6ÄÛCl@ ± 1Ä6ÄÛCl@ ± 1ýp'¬·éG™{~òê›Ø ¼É„ÞÀ7Õ?Ü @ƒp=;\ÆYþ8Ûìô·§IU…9Ø`ƒˆtŒ(ä>bû/–KLê»Ç¸ïSò¸bÏynl[.ðSnZ´³#é;°žåë56Hí¥çwã±]Õíª 0)=ÕÖ ½.i0¶#Ò1¢À£„ÆØAq{ÝNlç^!Æ4@côBA5Û¹i=ÐÌŽA‚âêµÛõ»ƒü.Ðjz\å5“ÛÏIµÊO W×åuþ«¶N:rÕV⩞¹×ÀIt)›Tx}S]&9ÏÔpndŸÚê­öØ®­¾î¥6€}*5f©§û­÷@ÖSGµQ3ÀzzŒ‚j¸ú¹¿“j¨šÊiŠÀOúÒân¯Òꎨ4Úäö¨–þá$÷æTEU`ï£Þ€ÝÂîº*ª%¶]¤•ºÓËÜRÕ÷]ySæ §¸Øõv÷­ugXiªžÉèFWv¼wvñæ$Z/<îôv—‚›î¨Ë•óiÆð{%®9wÜNw†]EUCó4óª\p8®>Þΰ‹™Ø€†éQ+töA¹ð.ºü¸¶ íÑ®·ê÷ùã¼Ú;õÐ\u Ýéu7‡Z¢E¯ôÕƒ;5ÅÏ;@—Ü?wzÕÁ€6hËklâ-^«'¦óož;½jâp@:­x}µTüfîëä;çN¯ú8(Kûýéxô–­äòƒøÐ{òáÐ@"-÷§RUTsrÿSjCN¯š½ßïÁZ  r:ÕŸ VQÙN²ìÖN‰m§Wý$7Ñ©þT¼ŠªMîò±íôJ!¹ԙ܅c[fg‘ÜP?ýêO)UT¤Ë-Û)Ç7É 5Ó¯þtjUØC‹mçV® ÏKà¥_­CmSåebÛ¹•Nr‰Óç`{ÖÀˆ#¹¡*‰ÄJGúÛ±íÜj†ä†JèW™s4¶[eéWŸ`÷0éÐÉáÜj’à ÷ÒWÚTQ ¹f#gýœ×öÍþ)^Ò}®õp ­ïT ÔíþIrçVÃÜä†[èWŸfGg»3¶[pÜÖäÞ3I~Yföą•L•ÃÅ´8Ö¨îÞöwTÎ`)~1É Ôì²>êìOÙ´#›cû¼ÒRyaãã—(@thטœŽÎªùm±}jfoÝr? «Æ³pô²ËT[Ïë;ÛûnÚuÝîSöý~[ó|65 p•í†Ø>ãzð³Íƒ›•+@.Cm6¹s´]ðd•ܧR½@.èj»®ZÛÅË]|ƒ¢ˆS[$$:µó¿ø­Ù—UKÒRN,ë§Î£nÄÏjg´]$që¼^©­<¯Ê®o šÖÄ&³[‰øúZï™ÏÊx”Ë.qî ‚5;ØÂhû¤gå¸þ£r  Ωê\XwÄRl?ùdzò¾4#¨_:ñÏ$®SáPœ{s¬÷#¶+?™.xVÎI€Þú8káÞöOséû³ŽL•¨ 8‰ÞfËjéâc±éã~O’W;èÜñ¬œo/߇jRmgKUâïmï&¹·2(nk׺*¶]\@g[P?±Z³á玶ÙÄP kc»¶kÀã)²òæµíø-d6\FŸ³ì‚ú¹¬ÇÛ÷AV’§/tlïY9@“Ò;Û³Z?õ×ü#¾ö1þíÚ~¬³NõŸÄÐÉݼÝÇwÛ½í–fo´‡5t@…N £Ê§Ç?,I¨QKä3¯ŸˆÌ~íˆíJΤˊñäáæ“÷jPI[­‚õÔÝím·q&¹± Ô¯þöþY;—¤%.—°’|“¸ã < ó“Üüx’Ðß g=ÕO¦(¨†+¡Ëï¥W°Ï¢Å:Þãê©Ã{útYrzN2 :Þݪªºûnª×T íQ½À˜«ù­*¬±ÈŸý`™Ìè{Wª³/½¿LuÖK(­XIß» æ¾´ŠÃVs¥P‡ÀVý5è:zõ÷¥UÄö‡K¿ÝTpDýYu”J¨«»O©µz¨1 ”Çö'Y;^WldÕà ²âÕ'îl½ý¾LZxªAÚîd¢oêWÑ5{’¶ÛP•Æ:á6v§êØîɪ—JîxÑ…ˈíÇæÖcw¨MPu“¤ØþxT†=jgç¯„ï žQÃŽOЇ⫦³½ÎR•õçîlv^o[V/wè«¢æ%ŠmŸ0éû„ÿË×µmáÆ¾K^l|‡÷«•CÕØîD8£ª>žfp΢tü‚ÝÎÛrÔØþèXtàEþƒK4 r㮬TûÕI¾Òc»—˜ßAE½Ë ©Ï“÷óÒ“·²–ÿ:þˆñù3~ËÜeþxë6×”¢9« j$¶{“}e=gL¥Ê5òÁýïå¿Nníõ÷)4—ÄßíCwåeÁ¸[Ë qðïsS¯ý˾?wP¼õ;2ÞÂäîÌmyy_öby³ãr¾F3% »?÷‰ãWîÛŸåY¿)“1ë‰mx¢A_9èúçn{{ØŸ/^(Àä'Ž 6·…q?þß•;òýʹ Y¨¢Ÿµ·°Œ`y*üçî/ÔÀrEmÚ…ò¬æ˜$‡öu#¯ù¨X¸íýóõrT?eòíßZߧϽw\¤õ¯üYàñ»&7µ2³Ç¯Ü´&`å‹—?ôÈîL~P‘MñMlCûÞûüãÂÜì‘~yÇvÒí«½Ö_m}ñò¥Ãúí,löà¦è™$‡ÇÜ›|méâW¾rSf¤;ugÏ‹êS7R|SôÄ6°ÿÑ+“SÐýMâr¬ÚÙÃëå ß÷âwç9çÀ•Ä6û-/Úúš}·åöý;ËÖÛÃÇX‰gÝúEs?¥Ô¦žÌ½mŽz¼¦îzN¾l“~›O…âsæ[¯¢›:X’Áf·®q[³#ë_¹òswojëþ™0_þÐ#»3þ R›â›Ø¦¼5ër/øDÝÄO}}zØñ"ó¾žÇ_š|ås±r/¾ß»~G&_¹éŠa͇®)Éä®å ŸÛýŸGg®ÌGvç5u°{SLò¹+¬ì}â,4ÅþOkšëÊ&Ý}}µwÍ|ø¦ž"ñÕYæâ´¾m“ar$ým0àÖׇºr’‰mÊ»+2É-³¯4˜-µ)—_0`%9GMýìš×ü´þõFl×[yCdåv ^@cÄ6GM&ôà¶kÁá×ë«gŸ\$l|v—RuîØÁ±MaïѸJ1†Ûd8òýndºÊÛÔn!žëüsD8'¶ˆpØGls–åç4½V¬6_^_Vü&zâ¬{b™ÇD8¬gŽñ ærë—xŒ"ʼò¢þJm÷Õ°ƒØ®#ªá ± œKTCAžIœHfCYbbˆmˆ!¶ †Ø€bbˆmˆ!¶ †Ø€bbx¸éE򿁦9F@ýÄöE<â±r?‚9à:È$9ÄÛCl@ ± 1Ä6ÄÛCl@ ± 1Ä6ÄÛCl@ ± 1Ä6ÄÛCl@ ± 1Ä6ÄÛCl@ ± 1Ä6ÄÛCl@ ± 1Ä6ÄÛCl@ ± 1Ä6ÄÛCl@ ± 1Ä6ÄÛCl@ ± 1Ä6ÄÛãÏÝxŠ®ëî.?8F@ýÄöEÞï÷ÝE`I×uqÇÈu 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 libjgroups-java-2.12.2.Final.orig/doc/design/ReplCache.txt0000644000175000017500000001646311647260573023176 0ustar moellermoeller Replicated Cache ================ Author: Bela Ban Idea ---- To have a big virtual memory by aggregating the physical memory of all nodes in a cluster. E.g. if we have hosts A, B, C and D and each host has 2GB of physical memory, then we can have 4 * 2 GB = 8 GB of virtual memory. ReplCache is a hashmap which distributes its keys and values across the cluster, based on consistent hashing. The difference to PartitionedHashMap is that ReplCache allows a user to define *per data item* whether it should be replicated (for high availability) or not and if yes, how many times. There are 3 methods: put(), get() and remove(). When adding a new key/value pair, put() takes the 'replication count' as argument, along with the key, value and a timeout. A replication count of 1 means the data is stored only on 1 node in the cluster, and there is no replication. The data will be placed on a node in the cluster that corresponds to the consistent hashcode of the data's key. A replication count of -1 means that the data item is replicated to all cluster nodes. A replication count of K means the element is stored K times, e.g. PUT(KEY, VAL, 2, timeout) means that an element will be created on 2 nodes. When the view changes, the cluster makes sure that the above KEY, VAL is always present on 2 nodes. Note that K has to be less than or equal to N (= number of nodes). When K > N, then ReplCache treats K as -1 (replicate to all nodes). K == 0 is invalid and will be ignored; the data will not be stored in the cluster. TBD: a replication count which defines a percentage, e.g. 0.3 means replicate to 30% of all nodes. The advantage of defining replication counts per element is that we can define what reliability we want for individual data items. For example, an element that can easily be retrieved from disk or database probably does fine with a count of 1 (= no replication). Here, we use the cache just as a speedup to prevent DB access. An important item that is costly to recreate, or cannot be recreated at all, should probably have a count of -1. The point of being able to define replication counts per data item is that we can save memory. If we compare this to RAID 1, then - because we're replicating every single data item - we can effectively only use half of the memory (disk space) allocated to the RAID system. With per data replication counts, we can increase the net memory that can be used (unless of course all elements are added with a count of -1 !). Put() always results in a multicast across the cluster. Each node determines by itself whether it will add the KEY|VAL or not. This is done by computing a set of consistent hashes from KEY, mapping them to a set of servers and determining whether the node's address is in that set. If so, a node will add the KEY,VAL to its local cache, else not. Get() first checks the level 1 cache (L1 cache, not mentioned so far), and the regular cache (L2 cache). If the data is found, it will be returned, else we multicast a GET request (bounded with a timeout). Every node returns its key/value from the local cache. Before returning from get(), we add the result to our L1 cache. (The L1 cache will independently evict timed-out items, or evict items when it needs more space. Items with a timeout of -1 are never placed into the L1 cache). Remove() simply multicasts the KEY across the cluster. Every node removes KEY from its local cache, and the L1 cache if enabled. Design decisions ---------------- There are a few considerations and assumptions that influenced the design: - Keys and values must be small. We do *not* provide technology which breaks large data items into multiple chunks and distributes or replicates these chunks individually - IP multicasting should be used in the transport. If we used TCP, communication would get costly (N-1 issue) - K cannot be reduced for the same key, e.g. if K == 3 and then K == 2 for the same key, we'll have some leftover data with K == 3. If this is necessary, remove the key first before calling put() again. API --- put(KEY, VAL, K, TIMEOUT): -------------------------- Places KEY,VAL into the hashmaps of selected cluster nodes. Existing data will be overwritten. KEY and VAL have to be serializable. K is the replication count and can be: -1: replicate everywhere 1: create only on 1 node > 1 <= N: store on K nodes TIMEOUT (ms): -1: no caching 0: cache until removed > 0: cache for TIMEOUT milliseconds The put() method creates a message with KEY, VAL, K and TIMEOUT and multicasts it. Each node which receives the message does the following: - If K == -1: add it to the local cache and return - If K == 1: compute the server based on the consistent hashcode for KEY and see whether local_addr == server. If so, add the KEY, VAL to the local cache and return. Else, drop the message. - If K > 0: compute K consistent hashes from KEY. If local_addr is part of the set of server addresses, add KEY,VAL to the local cache. Else, drop the message. VAL get(KEY): ------------- - Look up KEY in the L1 cache. If found,and not expired, return it - Multicast a GET request. - If a non-null response has been received: add it to the L1 cache (if not -1) and return - Else return null void remove(KEY): ----------------- - Multicast a REMOVE(KEY) message - On reception, every node removes KEY from its local cache View change: ------------ - The handling of view changes needs to be done in a separate thread. Suggestion: do this in a timer task, and start the task 100ms after the view change callback. This is to ensure that every cluster node installed the view. - Old view is V, new view is V-NEW - For a new node P - For each key KEY - If K == -1: copy KEY to P (only the coord does this) - If K == 1: compute new hash (based on V-NEW), if not same as local node -> move KEY to P - If K > 1: re-balance - For each left node Q - For each key KEY: - If K == -1: no-op - If K == 1: compute new hash (based on V-NEW), if not same as local node -> move KEY to P - If K > 1: re-balance Re-balance (K > 1): ------------------- - Old view is V, new view is V-NEW - move() installs a KEY regardless of whether it would be accepted in the current view - For each key KEY: - If K == -1: - For each newly joined node P: - The coord sends KEY to P (move()) - If K == 1: - Compute 1 hashcode (based on V-NEW) and determine the new node N - If N != local node --> move KEY to N - If K > 1: - Compute hashes for V (NODES) - Compute hashes for V-NEW (NEW-NODES) - If NODES == NEW-NODES --> return - Else - Multicast KEY (make sure everyone has new view installed, to compute correct acceptance) - If local node is not in NEW-NODES --> remove KEY from local node - Compute nodes for V (NODES) - Compute nodes for V-NEW (NEW-NODES) - If NODES == NEW-NODES --> return - Else - Multicast KEY (every node will check the hash against itself and store if needed) - If the local node is not in NEW-NODES --> remove KEY Stopping the cache: ------------------- - For each key KEY: - If K is -1: no-op (every node already has KEY) - If K == 1: compute new hash (based on the view excluding the current node) and copy KEY to the new node - If K > 1: no-op (somebody else has KEY, view change will copy to more nodes if needed) libjgroups-java-2.12.2.Final.orig/doc/design/SCOPE.txt0000644000175000017500000000355011647260573022212 0ustar moellermoeller Implementation of the SCOPE protocol ==================================== Author: Bela Ban Scopes allow for concurrent delivery of messages sent by the same sender, without having to resort to OOB. The implementation is done by a protocol called SCOPE, which has to be somewhere above NAKACK and UNICAST. Scopes do apply to both multicast and unicast messages. A message is tagged as scoped by calling Message.setScope(short). This will add a SCOPE.ScopeHeader to the message. A scope is always a short, so we can have ca 32'000 scopes. The scope can be set *per message*. It should be more or less unique, but doesn't need to be. All messages with the same scope are delivered in the order in which they were received by the SCOPE protocol. Because SCOPE resides above NAKACK and UNICAST, all multicast and unicast messages are guaranteed to be received by SCOPE (a) in order and (b) once and only once. Messages without a scope header are passed up the stack. When a message is received by SCOPE, it is examined for a scope header. If none is present, the message is passed up. When a scope header is present, we grab the scope and fetch the associated queue (MessageQueue). If no queue exists yet, one will be created. The message is then added to the end of the queue. If no thread is currently processing the queue, one will be created (from a thread pool) and assigned to processing the queue. The thread (QueueThread) will then continually remove messages from the head of the queue and pass them up the stack. When no messages are available, the thread is terminated and is placed back to the thread pool. The thread pool is configurable (min and max threads, plus idle time). Unused scopes are periodically removed by the ExiryTask, which is run every expiration_interval milliseconds and removes scopes which have been idle for more than expiration_time milliseconds. libjgroups-java-2.12.2.Final.orig/doc/design/SEQUENCER.txt0000644000175000017500000001466711647260573022706 0ustar moellermoeller Design of SEQUENCER, a total order protocol using a sequencer ============================================================= Author: Bela Ban Date: Dec 29 2005 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. It is the coordinator who decides on the ordering of messages from different senders. When receiving FORWARD messages from A, B and C, the coordinator establishes an ordered sequence, ordered by the seqno assigned by the coordinator when sending the message. This sequence is delivered in order at all receivers, therefore establishing total ordering. Note that there can be cases where a sender P sends message M1 and M2, but M2 is received first by the coordinator, then M1. In this case, M2 would be *before* M1 in the global sequence established by the coordinator. If this is not desired, then use a group RPC which gets acked by every node before a new message is sent. 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. SEQUENCER requires NAKACK and UNICAST. The latter is used for retransmission of unicast FORWARD requests, and NAKACK is used to deliver all messages from the coordinator in order and without gaps. Since we only have 1 member multicasting with FIFO properties, we essentially provide global order. libjgroups-java-2.12.2.Final.orig/doc/design/STABLE.txt0000644000175000017500000000223611647260573022313 0ustar moellermoeller STABLE ====== Author: Bela Ban Date: May 29 2007 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 libjgroups-java-2.12.2.Final.orig/doc/design/SimpleFlowControl.txt0000644000175000017500000000765111647260573024771 0ustar moellermoeller // 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)libjgroups-java-2.12.2.Final.orig/doc/design/StreamingStateTransfer.txt0000644000175000017500000001713011647260573025777 0ustar moellermoeller Streaming state transfer ======================== Author: Vladimir Blagojevic Date: July 2006 Overview ----------------------------------------- In order to transfer application state to a joining member of a group we currently have 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, 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. libjgroups-java-2.12.2.Final.orig/doc/design/TestNG.txt0000644000175000017500000002005111647260573022500 0ustar moellermoeller Migration from JUnit to TestNG ============================== Author Bela Ban Goals ----- 1. Reduce unnecessary channel creations, cluster joins and cluster leaves - Example: if we have 10 methods in a test class, we create and connect to a channel in setup() and disconnect and tear down the channel in tearDown() for *each* test method - Unless channel creation/tear down is tested (like in CloseTest), we can create a connect to the channel before the test, and disconnect and tear down the channel after all 10 methods have been executed - This will save us the cost of 9 channel creations/connects/disconnects 2. Run all the tests in separate threads - A thread pool (say 20 threads, depending on how many core we have) is configured - All tests are processed by different threads in that pool - The first 20 tests to be run are processed: each test is run by a separate thread - When a thread is done, it grabs the next test, until no more tests are available - All methods inside a given test are still executed *sequentially*, this is necessary as different methods within a test modify shared state, e.g. a view or the number of messages received, and are therefore dependent on running sequentially - We need to make sure we dynamically change mcast addresses / ports so that we don't have overlapping traffic - Later, we might be able to take advantage of annotations which mark an entire test, such that it can be executed concurrently - This is similar to TestNG's parallel="methods" | "tests", but TestNG doesn't currently have a mode which allows for concurrent execution of different tests (with sequential execution of methods inside those tests) - We probably have to extend TestNG to do this: - parallel="tests", but provide a list of tests (one for each test class) to TestNG *programmatically* - The dependOn{Methods,Groups} option allows us to define an ordering between methods of a test. However, this requires detailed dependency analysis between the methods of a test. While this probably makes parallel processing even faster (as we don't need sequential processing for methods inside a test), we should do it in a second stage. This would allow us to use parallel="tests" or even "methods" - Use @Test(sequential=true) at the *class level*, so all methods within a class are executed on the same thread - Move instance variables into methods (unless they are channels) x. Different stack configs (udp, tcp, mux-udp, mux-tcp etc) - As data providers ? - @Parameter annotation, e.g. @Parameter(name="channel.conf") // defined in the ChannelTestsBase (or whichever) super class public void setupStack(String config) { channel=createChannel(config); } - The @Parameter annotation is probably better than data providers, as we can run the stack dependent test suites as separate tests, with different parameters, e.g.: ... x. Distributed execution across a cluster - Same as #2, but different tests are run on different hosts - The thread pool consists of different threads pools, hosted by different boxes - http://beust.com/weblog/archives/000362.html ? Conversion ---------- #1 Change ANT build for tests - Provide a bunch of TestNG XML files, one for each config, e.g. testng-functional.xml, testng-udp.xml, testng-mux-udp.xml and so on - Change ANT targets to invoke the right TestNG XML file - All TestNG XML files have junit="true", so the unchanged tests can be run - Output uses the ReportNG reporter - [tbd] Convert into the old XML format, so JUnitReport can process those files #2 Change unit test to TestNG - Tag @Test with group "functional" or "stack" - Modify ChannelTestBase, rename setUp() and teardown() and add @BeforeMethod and @AfterMethod to them - Remove 'extends TestCase' - Add @BeforeMethod to setUp() and @AfterMethod to tearDown() - Replace assertXXX() with org.testng.Assert.assertXXX() - OR: replace assertXXX() with assert Todos: ------ - Convert all tests to TestNG (use TestNG's automatic converter or use awk.sed ?) - Remove junit.jar, add testng.jar - Write correct TestNG XML file - Integrate with build.xml Questions, issues ----------------- - Identify useful group names and use those consistently - udp, mux-udp, tcp, mux-tcp ? - functional vs. stack-dependent ? - How do we generate mcast addresses and ports such that traffic doesn't overlap ? --> Possibly a @BeforeTest annotation which modifies the stack config ? - We're using system properties (mux.on, channel.conf etc) to configure stacks. This means we *cannot* run 2 different stacks at the same time because the system properties are singleton wrt the JVM ! --> Change from system properties to TestNG parameters ? Or DataProviders ? Solution: parameters: ChannelTestBase: public class ChannelTestBase { String channel_conf=null; @BeforeClass @Parameters(value="channel.conf") public void initialize(String channel_conf) { this.channel_conf=channel_conf; } protected Channel createChannel() throws ChannelException { return new JChannel(channel_conf); } Test (subclass of ChannelTestBase): @BeforeClass public void setUp() throws ChannelException { ch=createChannel(); ch.connect("demo"); } @AfterClass public void destroy() { ch.close(); } Update 1: use of thread locals instead of instance variables: ------------------------------------------------------------- Since the same thread invokes the before (@BeforeMethod) and after (@AfterMethod) methods, we can move boilerplate code to before- or after-methods, e.g. // instance variables private final ThreadLocal ch=new ThreadLocal(); private final ThreadLocal PROPS=new ThreadLocal(); private final ThreadLocal GROUP=new ThreadLocal(); @BeforeMethod void init() throws Exception { String cluster_name=getUniqueClusterName("ChannelTest"); GROUP.set(cluster_name); Channel tmp=createChannel(true, 2); String tmp_props=tmp.getProperties(); PROPS.set(tmp_props); tmp.connect(GROUP.get()); ch.set(tmp); } @AfterMethod void cleanup() { Channel tmp_ch=ch.get(); Util.close(tmp_ch); ch.set(null); GROUP.set(null); PROPS.set(null); } @Test public void testViewChange() throws Exception { ViewChecker checker=new ViewChecker(ch.get()); ch.get().setReceiver(checker); Channel ch2=createChannelWithProps(PROPS.get()); try { ch2.connect(GROUP.get()); assertTrue(checker.getReason(), checker.isSuccess()); ch2.close(); assertTrue(checker.getReason(), checker.isSuccess()); } finally { Util.close(ch2); } } Update 2: use a unique channels ------------------------------- In the above example, cluster names were unique for any given test (getUniqueClusterName()), and channels were also unique (createChannel(true, 2)). Unique channels are created by the superclass ChannelTestBase: it simply creates a normal channel, but then (if unique is true in createChannel()), looks at the config and changes it, e.g. mcast_addr and mcast_port, or bind_port for TCP. To join another channel to the unique channel for the test, we get the modified properties (Channel.getProperties()), store them in the PROPS thread local, and have the new channels use PROPS. libjgroups-java-2.12.2.Final.orig/doc/design/TransportNextGen0000644000175000017500000000374011647260573024011 0ustar moellermoeller Transport Next Generation ------------------------- Author: Bela Ban The next version of the transport (org.jgroups.protocols.TP) should be NIO based: TP has an NIO selector, and subclasses such as TCP or UDP only register NIO channels with TP. For example, UDP would create 2 NIO channels, a unicast and a multicast channel and register them with TP. TCP would do an accept() in a loop and, whenever a new peer connects, register the client's NIO socket channel with TP's selector. TP would therefore be a multiplexer which handles a number of connections, be they TCP or UDP connections. Therefore, the connection table functionality of TCP would be largely removed, because this is now handled by TP itself. This requires JDK 7, because NetworkChannel.open() does not yet exist in prior JDKs. To be more precise, it does exist, but only for datagram sockets (DatagramChannel.open()), not for multicast sockets (no MulticastChannel.open()). Transport NetGen should be combined with the copyless stack (https://jira.jboss.org/jira/browse/JGRP-809). On the receive side, this is done by passing the selection key to a thread from the thread pool. If the key has an attachment, it is the ByteBuffer previously created by a (possibly different) thread to receive the message. The receiver thread will then simply call read() (or receive()) on the input NIO channel and - when all bytes have been received (defined by the initial length field of a message) - unmarshall the buffer and pass the resulting message up the stack. When there is no attachment, the receiver thread creates one (according to the length field which prefixes each message) and reads the bytes available from the NIO channel into it. If the number of bytes read is equals to the expected length, the thread proceeds to unmarshalling and passing the message up. Otherwise, it attaches the ByteBuffer to the selection key and returns. Later, a (potentially) different thread will complete reading the full message and then finish the job. libjgroups-java-2.12.2.Final.orig/doc/design/UNICAST.txt0000644000175000017500000000206511647260573022447 0ustar moellermoeller UNICAST and membership ====================== Author: Bela Ban Date: Aug 10 2006 Sending a unicast message to P ------------------------------ - When sending the first unicast message to a member P, its seqno is 1 (meaning this is the first message) - The message is added to the AckSenderWindow, which keeps resending the message until it is acked by P, or P leaves the group (or crashes) View change ----------- When a member P leaves, the connection for P is removed from the connection table and added to previous_members Receiving a unicast message from P ---------------------------------- - When receiving a message from P: - If there is no entry for P in the connection table: - If the message has seqno=1: - Create a new entry for P in the connection table - Add the message to the entry for P (AckReceiverWindow) and ack it back to P - Else: - Discard the message and do *not* ack it ! This is necessary, so P keeps retransmitting it ! - Else: - Add the message to the entry for P (AckReceiverWindow) and ack it back to P libjgroups-java-2.12.2.Final.orig/doc/design/UNICAST2.txt0000644000175000017500000001665411647260573022542 0ustar moellermoeller UNICAST2 design =============== (see UNICAST.txt for the old design) Author: Bela Ban 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) libjgroups-java-2.12.2.Final.orig/doc/design/UNIFORM.txt0000644000175000017500000001163311647260573022461 0ustar moellermoeller Uniform message delivery ======================== Author: Bela Ban 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, 1996libjgroups-java-2.12.2.Final.orig/doc/design/ViewHandling.txt0000644000175000017500000000550011647260573023715 0ustar moellermoeller New view handling ================= Author: Bela Ban 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: libjgroups-java-2.12.2.Final.orig/doc/design/flush-coord-leave.fig0000644000175000017500000001005011647260573024577 0ustar moellermoeller#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 libjgroups-java-2.12.2.Final.orig/doc/design/flush-coord-leave.png0000644000175000017500000003301211647260573024621 0ustar moellermoeller‰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Ÿà’<£(€Ð­ŸÅ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`‚libjgroups-java-2.12.2.Final.orig/doc/design/flush-leave.fig0000644000175000017500000000741111647260573023502 0ustar moellermoeller#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 libjgroups-java-2.12.2.Final.orig/doc/design/flush-leave.png0000644000175000017500000003133111647260573023517 0ustar moellermoeller‰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Æ­ÈÈ•äȰ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`‚libjgroups-java-2.12.2.Final.orig/doc/design/varia1.txt0000644000175000017500000003102611647260573022523 0ustar moellermoeller 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. libjgroups-java-2.12.2.Final.orig/doc/design/varia2.txt0000644000175000017500000005265711647260573022541 0ustar moellermoeller 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 libjgroups-java-2.12.2.Final.orig/doc/history.txt0000644000175000017500000023344311647260573021577 0ustar moellermoeller History List ============ [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) libjgroups-java-2.12.2.Final.orig/doc/manual/0000755000175000017500000000000011647260573020601 5ustar moellermoellerlibjgroups-java-2.12.2.Final.orig/doc/manual/.cvsignore0000644000175000017500000000000611647260573022575 0ustar moellermoellerbuild libjgroups-java-2.12.2.Final.orig/doc/manual/build.xml0000644000175000017500000000112111647260573022415 0ustar moellermoeller libjgroups-java-2.12.2.Final.orig/doc/manual/en/0000755000175000017500000000000011647260573021203 5ustar moellermoellerlibjgroups-java-2.12.2.Final.orig/doc/manual/en/images/0000755000175000017500000000000011647260573022450 5ustar moellermoellerlibjgroups-java-2.12.2.Final.orig/doc/manual/en/images/Architecture.eps0000644000175000017500000001116011647260573025602 0ustar moellermoeller%!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 libjgroups-java-2.12.2.Final.orig/doc/manual/en/images/Architecture.fig0000644000175000017500000000340611647260573025564 0ustar moellermoeller#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 libjgroups-java-2.12.2.Final.orig/doc/manual/en/images/Architecture.png0000644000175000017500000000677011647260573025612 0ustar moellermoeller‰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`‚libjgroups-java-2.12.2.Final.orig/doc/manual/en/images/ChannelStates.fig0000644000175000017500000000235311647260573025676 0ustar moellermoeller#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 libjgroups-java-2.12.2.Final.orig/doc/manual/en/images/ChannelStates.png0000644000175000017500000001150711647260573025716 0ustar moellermoeller‰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`‚libjgroups-java-2.12.2.Final.orig/doc/manual/en/images/ConcurrentStack.fig0000644000175000017500000001111511647260573026246 0ustar moellermoeller#FIG 3.2 Produced by xfig version 3.2.5 Landscape Center Inches Letter 100.00 Single -2 1200 2 6 3312 5775 3816 6225 3 2 0 1 0 7 50 -1 -1 0.000 0 0 0 4 3648 5775 3816 5925 3648 6075 3816 6225 0.000 -1.000 -1.000 0.000 3 2 0 1 0 7 50 -1 -1 0.000 0 0 0 4 3312 5775 3480 5925 3312 6075 3480 6225 0.000 -1.000 -1.000 0.000 3 2 0 1 0 7 50 -1 -1 0.000 0 0 0 4 3480 5775 3648 5925 3480 6075 3648 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 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 2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 675 5625 1725 5625 1725 6750 675 6750 675 5625 2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 3975 5625 6450 5625 6450 6750 3975 6750 3975 5625 2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 1800 5625 3900 5625 3900 6750 1800 6750 1800 5625 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 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 180 1845 3075 8175 Unicast receiver\001 4 0 0 50 -1 18 14 0.0000 4 180 1650 975 8175 Mcast receiver\001 4 0 0 50 -1 18 14 0.0000 4 180 1965 5250 8175 ConnectionTable\001 4 0 0 50 -1 18 14 0.0000 4 225 1125 7575 6750 Transport\001 4 0 0 50 -1 18 14 0.0000 4 225 960 7575 7005 protocol\001 4 0 0 50 -1 18 14 0.0000 4 225 2475 7575 7260 (UDP, TCP, TCP_NIO)\001 4 0 0 50 -1 18 14 0.0000 4 180 960 7875 8775 Network\001 4 0 0 50 -1 18 14 0.0000 4 225 450 2925 3975 up()\001 4 0 0 50 -1 18 14 0.0000 4 225 795 6750 3975 down()\001 4 0 0 50 -1 18 14 0.0000 4 180 960 7650 900 Channel\001 4 0 0 50 -1 18 14 0.0000 4 225 975 2325 825 receive()\001 4 0 0 50 -1 18 14 0.0000 4 225 720 6300 825 send()\001 4 0 0 50 -1 18 14 0.0000 4 180 645 825 6000 Timer\001 4 0 0 50 -1 18 14 0.0000 4 180 735 825 6300 thread\001 4 0 0 50 -1 18 14 0.0000 4 225 510 825 6600 pool\001 4 0 0 50 -1 18 14 0.0000 4 240 2265 4125 6525 Regular thread pool\001 4 0 0 50 -1 18 14 0.0000 4 225 1935 1875 6525 OOB thread pool\001 libjgroups-java-2.12.2.Final.orig/doc/manual/en/images/ConcurrentStack.png0000644000175000017500000002314211647260573026270 0ustar moellermoeller‰PNG  IHDR“=ébd4 pHYs M MÒέNtEXtSoftwareESP Ghostscript 815.04~8#^IDATxœíÝÙ–ƒ8@3§ÿÿ—™‡šfh–DìJñTe°‘è"¡ëûþwZ×uç?  —´«°æŸ3oÛf 0Ð6r«îð†ÕuÇß ðÚIîð?ÇÞfsØÔ÷½“‰\î`rPBxs¹#É­Ã oÑç€L$7À½ ˜s-É ™HnÈDr@&’2‘܉ä€L$7d"¹ É ™HnÈDr@&’2‘ÜßÒuÝ%O>>ÄshÃâ®a;§N’›Ý&­˜F&ÙΩ–äþ–¾ïû¾¿äs&@{lçÔ韷 Àü×Oóýk/Æûódmêâë‹O >vü¢¦»M¶½Í­ý7Û¡~³=h­'m;§fúÜ5·ó!»xjÉ»þmPÉÛáIå'¡ç›ëÚ.c«&)}îÍ3uÒ-ÿ½8)xךɧí};< 8ÜÜ»#lî>)l6¼P¦ÿª§$-‘ÜUÛuië¼ *lŒ&c€®§¥Zóøìæº6!#¿@rçploÜ|×øüßU …;ŒLOZssݼ…&IîªÅÉ:1qïz×ä½{ ›__ùÁÍ5è^‡Ð‚‹[×.÷ ®éÛu¹ß7¤#¹sXÜÍþv¡ Ù ®Å¿Xxy­]‘mÆóÞÍus÷I'þÊk‰>Ÿ:™¡pÒâU›HîÚM’u²'¬Mßur¡ð¼ù!æù­}üóË–"|°«›?;|Ýkö+þ²ÜuYëï}ˮڵÐÅ6N´ó€’ Ëã׃_mÄÇÄ›Hêª|ýìu‚o‘Ü1Œ’ÙQiX®í|íôs0óåß®½ƒ›:¹ »möc 5oçAâîí ËÃcô¹9Âýœù‚ú·ó3Ýå;\Öú }n e)ºøûýgüJðÞÅ¿O–çXa8àÈ ô;·Ý¯@\ g¦Ò˜Ww%_¿’bÐ}nÈDr§ž™JcT7´Gr@&’;1=o ©nh‰ä€L$w>ºÚRÝÐ$É ™HîÜô¿)¤º¡î~šÏpËâà_ǦÒÕ MÒç€L$7d"¹ É ™Hn€{¹HkInÈäHr÷}ï·¡%t¸¹ÜÁ>·ð€W-Þ®ët¸¹ÃÙ­Jx,’ÙÜÄñ`Vñ±ü™©4æãÕýñ¯O“\[™HnÈDr@&’2‘܉ä€L$7d"¹ É ™HnÈDr@&’2‘܉ä€L$7d"¹ É ™HnÈDr@&’2‘܉ä€L$7d"¹ É ™HnÈDr@&’2éú¾ßýž®»£( ;ÐØÂ¢޽Í&øº®‹ŽºÎL¥1¯îJ¾¾2Z™HnÈDr@&’2‘܉ä€L$7d"¹ É ™HnÈDr@&’2‘܉ä€L$7d"¹ É ™HnÈDr@&’×÷ýÛE®$¹ É ™HnÈDr@&’2‘܉ä€L$7d"¹ É 5êºîª™ßú(à&’2‘ÜPn ¹ É •ÒóInÈDrC]tµ˜ä€L$7ÔKÿ˜ûçíÿÑ÷ýï÷ëºîï33¿õQÀ­ô¹ É ™HnÈDr@&’2‘܉ä€L$7d"¹ É ™HnÈDr@&’2‘ÜÉ‘göy:/À^ÊUlÒç¦Ô¸•1¦GKÊcû÷ß^‹ÞßzBÛw8Or³Cß÷qoÒ‚v¼yO¦þÂ󳵇c$7¥œã³lüTErœ¥KÍ“$7¥üê”ÏZÜøOuÏOrÃå\[ÎÿÛŒäy³Ùý]øœ¢þ›dã§úÜü~ÿm‰ÖZ¥’y £ø4öäE?¯Óçæÿì ïá•ygEÓF:»Â{íõÅ$þ×îÃúÜ\À)p8ÌîÃ^’›³Œ%Âav0ZζÍÖį]á0»{és@&’2‘܉ä€L$7dräî~t°—KǹÊÁ_…Ù_×uÑQ×™©Ô`W]XÝím9•”Y‡‡ -€L$7d"¹ É ™HnÈDr@&’2‘܉ä€L$7d"¹ É ™HnÈDr@&’2‘܉ä€L$7d"¹ É ™HnÈDr@&’2‘܉ä€L$7d"¹ É ™HnÈDr@&’2‘܉ä€L$7d"¹ É ™HnÈDr@&’2‘܉ä€L$7d"¹ É ™HnÈDr@&’2‘܉ä€L$7d"¹ É ™HnȤëû~÷{ºîŽ¢4ì@c ‹Ž$75躨îÎL¥»êèÂênoËÉXfˆ-€L$7d"¹ É ™HnÈDr@&’2‘܉ä€L$7d"¹ É ™HnÈDr@&’2‘܉ä€L$7d"¹ É ™HnÈDr@&’2éú¾ßýž®»£( ;ÐØÂ¢޽Í&øº®‹ŽºÎL¥»êèÂênoË©¤Ì:<\Èh9d"¹ É ™HnÈDr@&’2‘܉ä€L$7d"¹ É ™HnÈDr@&’29ø|îžš°©’öª’bÐ}nÈDr@&’2ùçüGt]wþCj3>#Õäl*û2§ù” ’û×Ün3o÷û‚íQe_æ@¯1Z™HnÈDr@&’2‘܉ä€L$7d"¹ ÉÍÅ6o‹q~bñ <3¨äæz]×mÆC0Cß÷›Ÿ@,^g¦¯»æî§0n;ºö@âñ ¿•Û”þ½Ì@`X{‹Upf*PÉÍ]þºÎ¿õèBbs†àCX¯½3Sw-çF}ßo}—Ìðsþõ¨xí™ ¼åö>wa‹ì¸¾aå}ëÃ3ˆ×Þ™©À+ô¹yÈ0x~r]ÀcâÕ{f*ð°Û“»™¼2þ÷îbPƒKÂûêB}ˆð†6¼…Úx´|ø{h#&íŸáŽ_ß¼<м Þ_Vãhù8’'‡ùÿó×׿¤*ºÝïÒ톼ßçž[û½ï¼Õ(™-©1¹7­õ¼ y)“{ cŽƒ­w¹¥94 ÆóÜ›vjqõm^}æò´“âµwf*ðŒÜ}îI'@³R³ÍKýK~  ÛwF|Ðsf*ð¤¬ÉüZŒzlZŸX¼›œ™ ¼åÑ䎟5Ÿa׿›¯ó¤Âv0Ïæ Äâ*83xWÖ>75;®ÚØìIÆ¡a)¯P£fRùubÚ&¹©ŽðHnÈDr@&’2‘Ü<íüã¾Lrs1¹û:ò„¶½Üógi··Ä»ä!ܪì á {"¹ç]Þ¶œÃ„7´Êh9wٌޒDÈštûÝO‡r'䵇 ³çž|<‘B»óºqõw­ï’=ŽwhÙ+^{g¦o¹½Ï=ìðý§Åyæ;~e2uÜK Þ(¶ë1t×*¥p†Ÿj=$^{g¦¯xí‰#‹ïùÔxþµþwпç-ãl>Öÿ.é ³&^ÿg¦«ýYa…}hÙœBI£¿9ä8,^ug¦Oª=¹%–ß4¯ökË7Ÿ¹9´ä¹>÷É”Ýu ¹ŸЪ'úÜ'ûÄ“·¯QOšÏvÉÒ *õ¹K"vñßàÅòOÞ4£öóÜÀ˜ä€L$7d’æ÷Üu nèV2Oáýãâ¥?yÿá%Þ·¸à6«—/k²ÐËÑL¥Ô³D¨œä>hí–éñÏÕJÚ `MØÝº®«|õÚÉ}JÉ£É6ç)éˆs—Ò‘ÜG,ö{†Û¿ƘÌs`¡óO¾äy©ÁnæSƒâÏ…ŒÏ,”œwxXI'«÷·R;ÁÊ|kŸü[ßÖJûð3s7·ò¥W² @\¡ö¾î¿~[?jß‹fþQÁ+“©Ãâv½+¶öÆù¢Ëÿ}Æf‘¿Îá¢Æë|í“K¶ø£‚ïé’m`oÁÊ—_“¸Ïí„ߟóÏK º>Aÿ>.ÏpL0ï'Í‹1™z·x°añ‹/x¯ÍQ½_?¨£’m (Ò%ÛÀ/¬èw·H'qr7cÞbÞÑf•ôŸÎwtÆÂµPœOÚœú˜I%¹ª‚žü²…}è“E:\Ñ•lP?Éý9›W­§ÑÜ{ñàµ^\çñÙ–¯mÎç¹ÇçS'ç 'ó,N &½e±i› *–Ìó¤ÍÓ¢—”êðIÓógŽO n€‡W¶„Ã?GÜåpE¿¾ @O?åsø;è“jPÒÄlγ÷çÉvmˆr­Ó¹÷˜i~êtü\<«O}×âº*Y'…kìpDÝ´ ,é’màVtÍÛTèÑkËÇÄίçSƒI¯›_Ü{ì•ÅO>0©ÄZaâîæd¡|ÁÇîú÷*nþÆÂªÜõ}oÚâ"]² ÄÿV¸ @µþ°xr¾·8?¶8uﯲ÷ ò{ûRÿ«ìÝ/^•·VE\ݕ쿕ƒ6ø=7ìSÕ%À=—Ü®[¡ ÃÏáúRÀ“ýU˜ëVö©zùÔ—-gµÀ3žKî~ýnÆýo¼0¹nEl_ 7j¿éÑ>÷±«¦×&ÙdÒqŽé<÷P¨ÚÚïkø,É Ûâ/iï·3™ó7;ˆïÔTøÆàW¾ó×÷}U~¿ß3×–—ßÅ€c&÷©,œZþ®ò7Ÿ3¾6Y.¦Ï Ђàžt{ïy¼¹”áW?ó^øxRág²—;±4ëÀƒ‚·÷ÁÝu‹\NÒçhPpÎ{͸/¾v>›ès´æäõ_k'¡Ýì²úÜm:0N¾kž37»tmù×$wóYÍÁö¨2¾lráXùhyðìöùÇ®½qó7»çI<å Z•´W•ã0]äª8Ï ™HnÈÄjl0N^ÉÍ'̯ˆÑIIîe¿&¥m®¸ù_å{fýØþá&’›ö¸9ów,þDÇÊá ‡Åw“ÜwñƒÅwM~]ºë±yoê;>œ×‰ÕHî[h¶^±ÏZ«ßÒóÝ¡r‘Ü×ûx;µ\¾Bâû=ݺè\âcšûVŽSÝW:Ê‹ÍËdêâC<'Ùœs¾ôÅ©F³^!¹/–´·}áÚÝ·6ÔR²¢Ú3®ÓùQÑd¿›ÿû÷‘ùξ8gÉÔøÜGr_)ilÿþÛ«Úúµ »ª¼cM%iwG¨ßxçš$åbÜó—ÌtÍËÁå$÷•RßIRøó½ðI~Ç J¢!Øôñ4ß1ÔoaSS>áœÁ©–Ô­_:’ûz[pÆ6ôŽ}oó©A,Ëÿ͸>o•åà&é¾5“Ü·ÈÛT=yà|ài¾y×ó1YÒzn©¹M±/œ¿pÎÅs今Ä%$÷•ÚhªÆ×°\Þß=œ=»Û¬fÃîAÃλ6ÿ°kÇsί€)_÷‘Ü,ï·¿sG$·^_Öpœk ™[ûUØæÌ¿Y—̹9Õ%)o‘Ü—i£Ã½hoçûÝ9oœkÙ´¸a¬m-å¯^r²<\ErSdñ'"ƒú¸UmœKk`/É}†;ÜcãÁó"çÅ8wê8LrSêÚ_{Wë¾8oàX‡·Ø`“Üh¸Ã]r7ãV¿ûØá8—ÖÀå$7SåaóñŸíV{îh›ä>«Ü:y}Ù?ûÎ(8âç\bívâÃÔñ‹›[ݱͲðÞ¨—ï'—{x\¼$eqU—·O ÉýQ׎âÆWž7¬ü¶ñ{K>„»ä&e›™q~/ž—³ä3_YîMMVIIN†·ä>%Q‡ûn}dðüªƒqÎ^¶„½ýÎÍムëYÿr÷.t2ó®õ”d~3œ½$wË^¹<ªÕÁóg~Ç%ÎYtI‡{¸•é}O°ï7°Ü’Z,Éâš?S0É}\…ËJîˆÒÌày%W†‹sR{·!ºê£.9xºŠäN¯’t™K:x^íúœç\bsH¹¥åž_h% šä>èÝúË’.¿<ƒçmÜÔLœsRýã›ZU·[rçPÉ0ø1Õž'::Cœx²§;^Ö+Ë=¹Ð~ý™§“ÜG<Óán,W*f~kI$÷n×ö?* ž·qêúuâ¼Nó)YÿÔÑxl¡O.w3¼(É‘z}Àó]'¿þ:Öå.ß–>x$T·®RŽUÒ^/Æó_ä­UWI•ÝÊ=ÔžvìXçÌÛvÉà¹Õ[ +ÿn_ˆ´/8?.¹ï"N <7Χ<ü«¤Ou¸ƒ{ßû™·KîË?£ðÊsÇC|Ù“Û¼v×¼8É}Š ¹Öâ๕ 0&¹w$·zëÂT€,$÷n‚äV2Àšÿy»À’2‘ÜÉW’»†§»SG‰+TuÃa_IîßïWÉÓÙ¨£ÆÄªºá˜¯$wß÷óŸQuÔ˜¸BU7ö­_…=ö”*SGõÛU;q…ªn8à+}î1GúõSG5ûë.ïëŽ+TuÃ._Lîߨéy» ¬RG•â¶°Žâ UÝPî£ÉýGKQ?uT³çªã UÝPâÓÉýÓRd Ž*··»,¼á¤¯'7p ‰ ‘ÜZœÔQ åÕ¤Û gHnà2B ¹ ÉíW¤ ¨£Æ¸¥9œ!¹?7oÊ@5&®PÕ ¯'w×uڈʩ£ÆÄªºaÓ·î[þg<§¨“:jL\¡ªvùVr{°AýÔQjós\¡ªøPr…«Ÿ:Êk1ƒ ŒÃ¾’ÜÚˆú©£t6ÇÀÅ6Üá+É \KôÂ[¾~m9p€Ø†ésûd¹SÊPN×ÁÑ}n`7)/’Ü@ûjÐ’¯$·GÕO5Æ£<á&_I"u”B–çpëgÓª%÷O0d Ž£ç —ûVrÿ´¨£úíª£×Ã[ç›Æ|ñWaã–Â.]'uT¿¿:*¬¸BU7ìò¹>÷Ÿ¾ïÿ}»j©£úý%nù9ï BoªnÇ4éÈZº}’#ýú©£Êí½ÏI\¡­VwKÍ&¯ûzr—Ð,Ĭ.ôÅóÜÀ圫†Ç|ô<7p¹á\5p+É \IxÃÝ$7d"¹ É ™HnÈDr@&ïÿž{|³ÃáªÔÅ›±÷žS¯[,pù·H÷} ¥ø^év¥KÖjü€“cKOQÝ|ÄûÉý ;aÔc`ž^u®®:K5ûhr¿ëkT«ß7Å÷2ų[.Y«óo¢² P²ä^ú›4I‹£îÃëËk»ôðúx†`Ô±¤T‹MÉün‘“"§&Å{Wð~Këyo=.NRxÔwó´TÉW[»×i<>?™:¯ÄàÔÌÞ’‚-mó+ì .WËjãV5nˆçÿ® nŽÆ7k¿}¾èµ¥¯ÏËSrÊmí*ì?oo=®MR{õÿZ+ØdþxÕÎO-©Ä¸œVfüM '•lp“4}î’NÌ|Ò|j¹ùçKJµö–Àâ!K<~P•’/{¸ç3ì-UPÎÖ㤟:ìŽËsx…,NÝ\ÉÇʹ÷[Z°¸µÛ<)MrO.¾x¦} z~k¥*Ë0ÛZkÒv‹P^¿ÓU©çÆùw¾„»¾ì®z¼¶œƒµÚ\\åÕJó²&÷ĸMŸ¤ÿþ;u&¼wó-›-Q…}ëû,Öãïêªül=^²”¨áËÆÖ¶´½êÿ¦4©–óÜ¿ÿî›ûÃü eð–ø<讂–êÀâ&o¬°E¸ãìì®zü«ÊOÕcIã"]ÛwÜõew¾¶œ›•»¹¸ wO>¥º>w°«Ä»ú®«NÆ3œ¹uo©—w×j8-:”ð|aÔãÚ‹“©ê±d¹ãÌëôªR-~Ùx%¯Uâ}å,Ù¨6WÃîÉUÔçÞ´vÉÏZg=¸4iï6_ôÚG¯_¸Ðç+óäGmÖc°tõ/7^ôæJ>¶Ð½K,9ü{²ka›‹«j÷äkV¯™ŒÞ³~¥%sšM.”©Ï HnÈDr@&’2‘܉ä€L$7d"¹ “ƒw?õ„x…Ûú@&FË É ™HnÈDr@&’{› ék £Bá0É ™Hî z5P Q¡p†ä€L$w]„¨…ƨP8Fr@&’;¢OPµÐ 'InÈDr—ÒQ¨ZhŒ …<+l[×YKïS Q¡p˜>7d"¹ É ™HnÈ䟷 À† øÇ—þ½XÃE‚õ”$}n–u]ç—{’ÜŸ ƒ›a´à+Ö~F?õa†áÅ`jüïä-ãÙÉö™Óçh_…“¾ø0B>¼¥ÿ×|æñ',Æöâ[‚‰‹ÊÉ ð!kcæãx.ÿ •ÇŸ6™m¾,±½‹äø„xœ¼û׵˒Äwpžà+þN0/ÆóÞˆ Ϲ›>7Àwí ìŴޝ5+Œöø<:úÜ2\×=1?=™T˜ñÃç¯]^¾Ésä6és|Ë$çÿž²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`‚libjgroups-java-2.12.2.Final.orig/doc/manual/en/images/Message.fig0000644000175000017500000000201611647260573024522 0ustar moellermoeller#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 libjgroups-java-2.12.2.Final.orig/doc/manual/en/images/Message.png0000644000175000017500000000336011647260573024544 0ustar moellermoeller‰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`‚libjgroups-java-2.12.2.Final.orig/doc/manual/en/images/PullPushAdapter.fig0000644000175000017500000000401611647260573026215 0ustar moellermoeller#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 libjgroups-java-2.12.2.Final.orig/doc/manual/en/images/PullPushAdapter.png0000644000175000017500000002662311647260573026244 0ustar moellermoeller‰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`‚libjgroups-java-2.12.2.Final.orig/doc/manual/en/images/RELAY.fig0000644000175000017500000000512011647260573024011 0ustar moellermoeller#FIG 3.2 Produced by xfig version 3.2.5 Landscape Center Inches Letter 100.00 Single -2 1200 2 6 3375 4800 6375 9075 2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 3375 4800 6375 4800 6375 9075 3375 9075 3375 4800 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 3375 5400 6375 5400 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 3375 6000 6375 6000 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 3375 6675 6375 6675 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 3375 7350 6375 7350 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 3375 8025 6375 8025 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 3375 8550 6375 8550 4 0 0 50 -1 16 16 0.0000 4 195 885 4425 5175 RELAY\001 -6 6 1575 825 4575 4200 6 3750 1950 4350 2550 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 4050 2250 270 270 4050 2250 4200 2475 4 0 0 50 -1 16 16 0.0000 4 195 180 3975 2400 A\001 -6 6 2100 1500 2700 2100 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 2400 1800 270 270 2400 1800 2550 2025 4 0 0 50 -1 16 16 0.0000 4 195 195 2325 1875 C\001 -6 6 2475 2850 3075 3450 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 2775 3150 270 270 2775 3150 2925 3375 4 0 0 50 -1 16 16 0.0000 4 195 180 2700 3225 B\001 -6 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 3069 2364 1479 1479 3069 2364 4344 3114 4 0 0 50 -1 16 16 0.0000 4 240 450 2850 4125 udp\001 -6 6 10125 2700 10725 3300 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 10425 3000 270 270 10425 3000 10575 3225 4 0 0 50 -1 16 16 0.0000 4 195 165 10350 3075 F\001 -6 6 10200 1200 10800 1800 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 10500 1500 270 270 10500 1500 10650 1725 4 0 0 50 -1 16 16 0.0000 4 195 180 10425 1650 E\001 -6 6 8700 1950 9300 2550 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 9000 2250 270 270 9000 2250 9150 2475 4 0 0 50 -1 16 16 0.0000 4 195 195 8925 2325 D\001 -6 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 9969 2289 1479 1479 9969 2289 11244 3039 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 2175 9450 8400 9450 2 1 0 2 0 7 50 -1 -1 0.000 0 0 7 1 0 2 1 1 4.00 60.00 120.00 5475 9450 5475 4050 2 1 0 2 0 7 50 -1 -1 0.000 0 0 7 1 0 2 1 1 4.00 60.00 120.00 5475 5175 7425 5175 2 4 0 3 0 7 50 -1 -1 0.000 0 0 7 0 0 5 9525 2925 3600 2925 3600 1950 9525 1950 9525 2925 4 0 0 50 -1 18 18 0.0000 4 225 2445 1800 600 Data Center NYC\001 4 0 0 50 -1 18 18 0.0000 4 225 2415 8775 525 Data Center SFO\001 4 0 0 50 -1 16 16 0.0000 4 195 990 7350 9300 Network\001 4 0 0 50 -1 16 16 0.0000 4 255 3465 6525 5025 Relaying to other data center\001 4 0 0 50 -1 16 16 0.0000 4 240 1320 4875 3900 Application\001 4 0 0 50 -1 16 16 0.0000 4 210 360 6300 2325 tcp\001 4 0 0 50 -1 16 16 0.0000 4 240 450 9750 4050 udp\001 4 0 0 50 -1 2 18 0.0000 4 195 225 3975 2850 X\001 4 0 0 50 -1 2 18 0.0000 4 195 225 8925 2850 Y\001 libjgroups-java-2.12.2.Final.orig/doc/manual/en/images/RELAY.png0000644000175000017500000002434111647260573024036 0ustar moellermoeller‰PNG  IHDR’d-!V5 pHYs M MÒέNtEXtSoftwareGPL Ghostscript 8.62ƒ-ÏEIDATxœíÝÛ¶¢¸PéQÿÿËžOÓ7¹È s>u×V d‘±{¿ß/ ÁŸ» À’®ëæþtï¸kP°èAàÏ}Y8 “o¿¾TýtÑ мåÀ¸¥¯öJb‡•û²)¶ç^\¤rÄ6@Õú ˜ †õÝøä¦v—ç{;ûʳõÏÛòë×¾¬/Ãòñ:¾ ÿ|?׫aÄõ]†ÊsD©}™‹çÉk‚}ÜÛˆô~¿?ÐuÝ\*Ìýû÷[Þ5¶0jœ,ÏòÆ¿·ösà{pËé½=±]í]ÖG,4‰ñŸæ^¼0[Õ@sÆóšÄÚ÷®IkVr ¢wòe“¯)²åå­õoß×7.Ï„Ï]ÖlUx´}°4G>wîC“iSMνøç‰è*¾-\Ù÷ÿ¸æêÿø,îÏ"MvY“¯ÙZì…-o½˜{ñdý\Ù‹º·ýþÒÿã¦CÞýëH1}¤<ë?ñx±×|Êò v¬Cy]UEë{ËèåÛuöÅFÛƒ;·¬)¸½<·ø9KóýßϬ"hR?¦ÜÔ®÷½kGÁúÿ.øAE¶¼<Í0£ß>=Yr’Üúˆâë#~ÎꬹY²£Š€8ûBë¼´î·\ü#NÚrÊä¹+ɭؽ>bŸå¡ö$i X3Ì(õ®5LïXöÝ—B¥*çÄØ¶>byËË—ëÇÁ߯\xИÉÙÁ}‘°éB®k”aÓÌâ&·œxáq+ÓîZQ¤ Aç°Ï¦‘tÁÍνxთ¼^pËãÖÏlíNî¨îÛàØ‰£më# žš+Ü@Ã&›ùÜ÷ç¦vßÊýîpÆoœì×/WZÖ»Ô–—÷eŸ…Ð"cª+&É/xצ-§¬xíš7Ô†çÌÞ nDNÆÆÂÍʹwÍ}îšÈopß*¥Ÿû²o˯™}Ù½µ¹²Üà·³bÛúˆª*·{…é\Žîxב‚-/â)þ›Ê¶þõÅ7¸[ÉØ¶>âŒãt||}P¹bKÒ¬8oË3ueP§C£më#Н8Éú* f…¿ö½>bÇ]–•ïšÜÎä ›ŸwqÖÄÏ·Ùò¦Oܺ…•U@µ<ÒbxÜ ÄÛCl@ ± 1Ä6ÄÛCl@ ± 1Ä6ÄÛCl@ ± 1ýp'¬·éG™{~òê›Ø ¼É„ÞÀ7Õ?Ü @ƒp=;\ÆYþ8Ûìô·§IU…9Ø`ƒˆtŒ(ä>bû/–KLê»Ç¸ïSò¸bÏynl[.ðSnZ´³#é;°žåë56Hí¥çwã±]Õíª 0)=ÕÖ ½.i0¶#Ò1¢À£„ÆØAq{ÝNlç^!Æ4@côBA5Û¹i=ÐÌŽA‚âêµÛõ»ƒü.Ðjz\å5“ÛÏIµÊO W×åuþ«¶N:rÕV⩞¹×ÀIt)›Tx}S]&9ÏÔpndŸÚê­öØ®­¾î¥6€}*5f©§û­÷@ÖSGµQ3ÀzzŒ‚j¸ú¹¿“j¨šÊiŠÀOúÒân¯Òꎨ4Úäö¨–þá$÷æTEU`ï£Þ€ÝÂîº*ª%¶]¤•ºÓËÜRÕ÷]ySæ §¸Øõv÷­ugXiªžÉèFWv¼wvñæ$Z/<îôv—‚›î¨Ë•óiÆð{%®9wÜNw†]EUCó4óª\p8®>Þΰ‹™Ø€†éQ+töA¹ð.ºü¸¶ íÑ®·ê÷ùã¼Ú;õÐ\u Ýéu7‡Z¢E¯ôÕƒ;5ÅÏ;@—Ü?wzÕÁ€6hËklâ-^«'¦óož;½jâp@:­x}µTüfîëä;çN¯ú8(Kûýéxô–­äòƒøÐ{òáÐ@"-÷§RUTsrÿSjCN¯š½ßïÁZ  r:ÕŸ VQÙN²ìÖN‰m§Wý$7Ñ©þT¼ŠªMîò±íôJ!¹ԙ܅c[fg‘ÜP?ýêO)UT¤Ë-Û)Ç7É 5Ó¯þtjUØC‹mçV® ÏKà¥_­CmSåebÛ¹•Nr‰Óç`{ÖÀˆ#¹¡*‰ÄJGúÛ±íÜj†ä†JèW™s4¶[eéWŸ`÷0éÐÉáÜj’à ÷ÒWÚTQ ¹f#gýœ×öÍþ)^Ò}®õp ­ïT ÔíþIrçVÃÜä†[èWŸfGg»3¶[pÜÖäÞ3I~Yföą•L•ÃÅ´8Ö¨îÞöwTÎ`)~1É Ôì²>êìOÙ´#›cû¼ÒRyaãã—(@thטœŽÎªùm±}jfoÝr? «Æ³pô²ËT[Ïë;ÛûnÚuÝîSöý~[ó|65 p•í†Ø>ãzð³Íƒ›•+@.Cm6¹s´]ðd•ܧR½@.èj»®ZÛÅË]|ƒ¢ˆS[$$:µó¿ø­Ù—UKÒRN,ë§Î£nÄÏjg´]$që¼^©­<¯Ê®o šÖÄ&³[‰øúZï™ÏÊx”Ë.qî ‚5;ØÂhû¤gå¸þ£r  Ωê\XwÄRl?ùdzò¾4#¨_:ñÏ$®SáPœ{s¬÷#¶+?™.xVÎI€Þú8káÞöOséû³ŽL•¨ 8‰ÞfËjéâc±éã~O’W;èÜñ¬œo/߇jRmgKUâïmï&¹·2(nk׺*¶]\@g[P?±Z³á玶ÙÄP kc»¶kÀã)²òæµíø-d6\FŸ³ì‚ú¹¬ÇÛ÷AV’§/tlïY9@“Ò;Û³Z?õ×ü#¾ö1þíÚ~¬³NõŸÄÐÉݼÝÇwÛ½í–fo´‡5t@…N £Ê§Ç?,I¨QKä3¯ŸˆÌ~íˆíJΤˊñäáæ“÷jPI[­‚õÔÝím·q&¹± Ô¯þöþY;—¤%.—°’|“¸ã < ó“Üüx’Ðß g=ÕO¦(¨†+¡Ëï¥W°Ï¢Å:Þãê©Ã{útYrzN2 :Þݪªºûnª×T íQ½À˜«ù­*¬±ÈŸý`™Ìè{Wª³/½¿LuÖK(­XIß» æ¾´ŠÃVs¥P‡ÀVý5è:zõ÷¥UÄö‡K¿ÝTpDýYu”J¨«»O©µz¨1 ”Çö'Y;^WldÕà ²âÕ'îl½ý¾LZxªAÚîd¢oêWÑ5{’¶ÛP•Æ:á6v§êØîɪ—JîxÑ…ˈíÇæÖcw¨MPu“¤ØþxT†=jgç¯„ï žQÃŽOЇ⫦³½ÎR•õçîlv^o[V/wè«¢æ%ŠmŸ0éû„ÿË×µmáÆ¾K^l|‡÷«•CÕØîD8£ª>žfp΢tü‚ÝÎÛrÔØþèXtàEþƒK4 r㮬TûÕI¾Òc»—˜ßAE½Ë ©Ï“÷óÒ“·²–ÿ:þˆñù3~ËÜeþxë6×”¢9« j$¶{“}e=gL¥Ê5òÁýïå¿Nníõ÷)4—ÄßíCwåeÁ¸[Ë qðïsS¯ý˾?wP¼õ;2ÞÂäîÌmyy_öby³ãr¾F3% »?÷‰ãWîÛŸåY¿)“1ë‰mx¢A_9èúçn{{ØŸ/^(Àä'Ž 6·…q?þß•;òýʹ Y¨¢Ÿµ·°Œ`y*üçî/ÔÀrEmÚ…ò¬æ˜$‡öu#¯ù¨X¸íýóõrT?eòíßZߧϽw\¤õ¯üYàñ»&7µ2³Ç¯Ü´&`å‹—?ôÈîL~P‘MñMlCûÞûüãÂÜì‘~yÇvÒí«½Ö_m}ñò¥Ãúí,löà¦è™$‡ÇÜ›|méâW¾rSf¤;ugÏ‹êS7R|SôÄ6°ÿÑ+“SÐýMâr¬ÚÙÃëå ß÷âwç9çÀ•Ä6û-/Úúš}·åöý;ËÖÛÃÇX‰gÝúEs?¥Ô¦žÌ½mŽz¼¦îzN¾l“~›O…âsæ[¯¢›:X’Áf·®q[³#ë_¹òswojëþ™0_þÐ#»3þ R›â›Ø¦¼5ër/øDÝÄO}}zØñ"ó¾žÇ_š|ås±r/¾ß»~G&_¹éŠa͇®)Éä®å ŸÛýŸGg®ÌGvç5u°{SLò¹+¬ì}â,4ÅþOkšëÊ&Ý}}µwÍ|ø¦ž"ñÕYæâ´¾m“ar$ým0àÖׇºr’‰mÊ»+2É-³¯4˜-µ)—_0`%9GMýìš×ü´þõFl×[yCdåv ^@cÄ6GM&ôà¶kÁá×ë«gŸ\$l|v—RuîØÁ±MaïѸJ1†Ûd8òýndºÊÛÔn!žëüsD8'¶ˆpØGls–åç4½V¬6_^_Vü&zâ¬{b™ÇD8¬gŽñ ærë—xŒ"ʼò¢þJm÷Õ°ƒØ®#ªá ± œKTCAžIœHfCYbbˆmˆ!¶ †Ø€bbˆmˆ!¶ †Ø€bbx¸éE򿁦9F@ýÄöE<â±r?‚9à:È$9ÄÛCl@ ± 1Ä6ÄÛCl@ ± 1Ä6ÄÛCl@ ± 1Ä6ÄÛCl@ ± 1Ä6ÄÛCl@ ± 1Ä6ÄÛCl@ ± 1Ä6ÄÛCl@ ± 1Ä6ÄÛCl@ ± 1Ä6ÄÛCl@ ± 1Ä6ÄÛãÏÝxŠ®ëî.?8F@ýÄöEÞï÷ÝE`I×uqÇÈu÷ Qî»Pî(‡”;ö¾ íÝv‰,ȽQQ”;ŠrS˜ö~çü,h»ê^vK¹ïE¹£(Ö&Úû^«áü,ÀYdA€\² @.Y —,KÈ••_÷¢ùÒŸø—¦™C¹£(7…io¾*( ûþÜ%i0~Ž¢ÜQ”›Â´7ßv•ûðß±ôÛ˜—¥ÜQ”›Â´7AûÇóùÔ 9”;ŠrS˜öæ«Rö Žö]ïôzhtãê•Ng1záìK¦;Ò_Žô3]?Í5Ë™@¹£(7…io~ % . °îßM¶ÚãßaпdôÐðÑéøY9Í—3–rGQn ÓÞìtŒ¸ÝXÃGG:‹ŽL‡Äèÿ?hñwÓìÞol…Sî(ÊMaÚ›o‹È‚³=×ßð®Ñ¼ ”;ŠrS˜öæ7‚Ž_§q¯³$…]g%_gI »ÎJ¾Î’PÆušê:K±R²àtËæ¬žž=åâ”%)L¹£(7…io~ þ1âÅs#N_ ¤ÜQ”›Â´7?S? ®4l÷öÖOãj©—é)û‡ô»i:ßö3ÊE¹)L{³_ñcÄž{N~ÍèݵW‹¾›øìKFõS]Qÿn6-F厢ܦ½ù¥~7èåÇEêÎþ¡ö6Š-˜©=%SîÛQî(§—ìFN_WÚ{«ÓKv£Ôç1@.Y —,«øµ#k¬¿½(wå¦0íÍQìÈu~]ñÎÅí,–rß‹rGQ¬M´÷½(VÃùYðaDÝÍÎCÊ}/ÊÅÅM´÷½hïw.‘FÔMõãLÊ} ÊÅO¯}F{ß‚ön»J|Q×öúÕøÇ’r_™rG9¼Üi´÷•iï5®u±uYßHÊ}YÊÅ×ä~Úû²´÷×Ê‚û—Í-7©½Š4ÃH±• ¾ ëíåBLjø1Y —,KÈ% ä’rÉ‚¹dA€\² @.Y —,KÈ% ä’rÉ‚¹dA€\² @.Y —,KÈ% ä’rÉ‚¹dA€\²à‘º®;{¸ ÍP›úR˜öN# ä’ô|>Ï^®B3Ô¦¾¦½ÓÈ‚ßb,ÑÓ µ©/…iï² @.Y —,K<ØëÔ 'XðÐ Õ©/…iï(² @®?¿ŸeÂX&¼Ç¡Æ¶cÚª˜ª·”{¨ü[¶g¨W¾ÖSåß²ö~9! >¬ýZ?,”»åŽR> l¥½+ÑÞ=LjrÉ‚¹dA€\² @.Y —,KÈ% ä’rÉ‚¹Î¹Ýe5îHó|>_¾»QãÑéCÓM]9)>¦ÜQ”›Â´7{È‚3¾Ý¬³£«ë:ƒäÊE¹)L{óLjmvÛèõO÷É®G¹£(7…iïÂdA€\²à9fO¹°›½*厢ܦ½Kr¾àiúe%Pî(ÊMaÚ»YpFû:©¦gW¼WÎÀø 厢ܦ½ùŒ,8ãÛ:ý,Fãj8ëÆeùH¹£(7…io>ã|Á“9Ó"ŠrGQn ÓÞ•È‚¿6»mdDU¥ÜQ”›Â´wa²à ì*¢ÜQ”›Â´wU²àaÞýäæè÷9gŸææ<·£ÜQ”›Â´7®Ù¬q¡ÖìP™’×Ý!ŸÆ(wå¦0íÍ;Ý´~pCC­SI» Ê]ŒrGQÐ!k£˜ä‚ŽRŸcĹdA€\² @.Y —,KÈ% ä’rÉ‚¹Î¹û[GQî(ÊMaÚ›’ÎÉ‚±w})iñÃQ¹+Qî(¢Ïˆö®D{÷#È% ä’rÉ‚¹dA€\ç\G|5Ó‹‰^‹m½dòõüÙ?¶'Ò~Ž+׎¥ÜQ”›Â´7‡gÆ@×u]×=ŸÏÑÍÎî‡Äëµýß×Oİù厢ܦ½9Jú1âÙþ^¹]Åí(wå¦0íÍÒ³à±F[cÔ¦ÜQ”›Â´w8YðñxsÊŦ1š‚Ͳ+Sî(ÊMaÚ›C8_ð?ý°m”@¹£(7…ioöKςӳ+>WûÇáì™á} 厢ܦ½9Pz|é[v4®V¶òh¼.³_ÿòébðUÊE¹)L{sYð{.ÂršÅí(wå¦0íÍéYpvhñ÷3§¦1º.H¹£(7…ioä:â½}ßx¹uAÊE¹)L{s”ô,8»_}ñWÚ¹)厢ܦ½9Pú1âÇàlÙÑ×¼¶1ð>ØWÿîùö”;ŠrS˜öæ(ÝtÛâÛÅûà%®¬]På.F¹£(èµQLrAG©/ý1@2Y —,KÈ% ä’rÉ‚¹dA€\² @®sîAç¾×Q”;ŠrS˜ö¦¤s²`ì]_JZüpTîJ”;Šè3¢½+ÑÞ=LjrÉ‚¹dA€\² @.Y ×9×ßÝôâ£áÅeý£³WœÍ>Úž çRî(ÊMaÚ›Y²àf¯Ö †®ë>‡O)wå¦0íÍ;Žo3mýþŸŸýRÑáä@ÊE¹)L{Ó £½G½ñîH¹£(7…io²à&³›A½Ùͣŧ §nîÙ.NÇXÚD¹£(7…ioš×wákCgñi‹Íݞλ‡7ÅŒ‡r‡Qn ÓÞ|Ch|io]m<¹ÝÜíé4&ûnKŸQî(ÊMaÚ›cåfÁW;îßÙþãæ6–>£ÜQ”›Â´7‡ËÍ‚M¹ò¼‡ÇÒ°<ªû·žÌrGQn ÓÞ+: ¾Ì¶æèí°~gøÎé¬Ôž‹”;ŠrS˜öæ(éY°ÝšÝ@ãåû§ÓxÕ³ã厢ܦ½9PÜoMO=ŸÏwÛL£¿|Ö¾íé<ÿþ“³(~C¹£(7…ioŽ’¾_°7lñưi<4ÝÜùl:Ói®œë)wå¦0íÍ~²àãqÜ鱟µøì«ÖLĈúŒrGQn ÓÞBüO?¢º÷×½ou›¦ÓŒ^š-ß ÜQ”›Â´7ûuÓxþí*:½ ˜vA•»厢 CÖF1É¥>ûrÉ‚¹dA€\² @.Y —,KÈ% ä’rý9e®îBE¹£(7…ioJ:' ÆÞõ¥¤ÅGå®D¹£ˆ>#Ú»íÝsŒ —,KÈ% ä’rÉ‚¹dA€\² @.Y —,ëœ{ЕԸ›Íóù|÷¨;Ý”rGQn ÓÞÈ‚kÑ£]×u]gDÝ—rGQn ÓÞÉ#>Ók ¹=v厢ܦ½‹‘OfDEQî(ÊMaÚ»Y —,˵#›î0wvmaÊE¹)L{'“fðDQî(ÊMaÚ;™cĹdÁK°AE¹£(7…iïdÁ“¹ ?ŠrGQn ÓÞ•È‚gz%ÛU!”;ŠrS˜ö.Ƶ#?åB­(ÊE¹)L{×& fÓ͹;厢ܦ½qŒ —,KÈ% ä’rÉ‚¹dA€\² @.Y ×9÷qOë(ÊE¹)L{SÒ9YÐ=m*YüpTîJ”;Šè3¢½+ÑÞ=LjrÉ‚¹dA€\² @.Y —,KÈ% ä’rÉ‚¹Î¹Ýuô· ™½³ÐðÑöÍj^/o<§=ýwO[3SÖSî(ÊMaÚ›¥gÁ­Ötðð9ýxèºnv¨Œž<}ÚÊ™žav厢ܦ½ipŒø»ž=þíøéXz¬Ø>›ÕÂÙñKÊE¹)L{G‘ÿ3mÁSšrÿV‘µ†rGQn ÓÞì' >ï›øKû«g·«öÌÔ~õM”;ŠrS˜öæ²à/´ÇÏNÆÒÕ(wå¦0íµ#ÿ3<õõÝnêÙ¿ÏžWÛ~ÎâdgÏá]3Á¯ÝJ”;ŠrS˜öf'Yð?ÏÉ…÷³¹¦MgG×ì5V³/Y¼Vÿ§Y¬§ÜQ”›Â´7û9Füotäo¶rú%ï/Èb‘rGQn ÓÞì! Îøö6ÊáWKõ»Ö ¤(wå¦0íÍgdÁÿöâY}ùÁsŽÅg”;ŠrS˜öf'Ypì7'.Ìn]}ŸÏ§¯™ªT¶u$¶$ ž ß;øŠƒ^ êXÏpG¾úæã+œ,x2;áâ¤@ ¶sî;ÂÈ+úʹµþW$Õ±Œ¾šjJm>¾ÂÙ/xö¿' ^ˆÓá:ì#BÈ‚×âôAà—dÁ+á\v 9dÁ‹r¼Î"QdÁër¼ø6YðêÄAø%;4²à ˆƒÀ—È‚÷àôAø;@²àm8}8œ,x3â |‰‚@&Yð~/†Ã ‚@,Yð–/! Þ˜8‡°SH& Þ›8ìñçì`¯>Ú±q®>”+Ç]¼vÚ)>¾ÂÙ/XÓá3F €,XÇ0ú†û=[Ò÷2#Æ á||…“KþÜŒ¯·ù`½õ‚žáH¬L„E¾ù€p²`qâ LõãB«ñÝv"+ÿ^Ô z†C2Y° ç_¯Ú5øc>R›òñ•I¬¬O„â ŒøÎxñ[Ó7¶)ቃ?cUßÂÇeúRˆÔ6\AZÚ&|‘ïMÃ/}õ›Òp†_J ¾ Žä’rÉ‚¹dA€\² @.Y —,KÈ% ä’rÉ‚¹dA€\² @.Y —,KÈ% ä’rÉ‚¹dA€\² @.Y —,KÈ% ä’rÉ‚¹dA€\² @.Y —,KÈ% ä’rÉ‚¹dA€\² @.Y —,KÈ% ä’rÉ‚¹dA€\² @®?g/À^]×µŸð|>³$ÌzhŽšð1ÙÚbÅí³à°0•>_*½—³êzýª®“믟ÙÅ&“è}u8c=ßq Ç&ŠÛgA.®Æ@†3å|A€\õ÷ 6ö«õÛ»£ç¾vÿ ß½öq·}Ô³opöpÆšUÔ>²Ð®Âì|sl,RCûµôس¨òhULÇÅÇk ±Ì¿©Ñbó{†óì’¼{èÖÃy4Ñ«¦…ëºîÝš)?«&ŠnúNî[³Ù÷Ù¿µvg?&E}þ赇¼påBξßëëÝܹŠf]³27ý³ñ„Å·¼ø®ß½Ó=«eOÝZ{Vìáoêg¾7è çö¬×ÿ³ñ„Å·¼ø®ß½Ó=«åšÃ¹=Í»ä—ö «(F©/åñp<ŸÏÅf]ÙÍÓIfôx<º®›–avcâFChQc%Ïo”¼YEïž9àôïɸæg_ûA5VËôi³ovÅU4»`Ÿ­ØŸ½)Žb8×Λô“ZYŽ’ê%ŠúLj}и—Ú‚¿”Ÿ­™{­ÿŽ‹[®›^r×_B çY!ÃyXýçäh#ïÜ4Q„fÁÛºÓ}°á~¼f®ÿÁô ñ©û¶ßõ+˜Ìp‰γK{Áåæ.›}tñ…³n4´>{ƒ)46’ÖÌk8lv.ØöôX{Rƒ–ú%´u{씢°žáÜXÂ’Ãy:FɆS6k$ŠR¿/x®ßç¶>‰îåFQø¾¬äM¾7è gö³’7¹ò ûv)G©Ï1b®bë®uಠg¸‘ÄcÄ\V7ù±û7ÚÚ_f—Ýš<5À†ó¥X4È‚\ÅÕN=ñáh ð1Ãùj¬dÁÃiûY‡¿gó%Zë÷¬ó2~\Jç ä’rÉ‚¹dA€\² @.Y —,KÈ% ä’rÉ‚¹dA€\² @.Y —,KÈ% ä’rÉ‚¹dA€\² @.Y —,KÈ% ä’rÉ‚¹dA€\² @.Y —,KÈ% ä’rÉ‚¹dA€\² @.Y —,KÈ% ä’rÉ‚¹dA€\² @.Y —,KÈ% ä’rý9{إ뺳8†á œB¼±çóyö"Ç0œ³8F Ë~An¬ëºÑÞ”ÑQ¶é¾–áVÎeͤ6Íhý“§‡ÏoLvåt>{ïí×Â,ÃyåÄ’‹ËðÁJævdAîjúöúKÿiÕuÝð³¯ýèú¹¼›ÚpRÃGÛ3ú`©Öç–aôÚCÞû»vê›Îk|¶üí×¶—aÍ”)À1bî§O6£?>¶dþ“}Ó\¦S-ÀtIg´i©Vš]†M/_ù­3]àÅõC†ó/ÿš×¶-R û¹ŸMŸ°Ã ÙM‘¨1—Žì,Îhö[~im=25»HŸ}èøýGùñèš º¸ü›^»iÊ”! ’âðO±áåì™áG𻹰Tïæ»òÄÁÅé¬\†5ï¾$y8ošÎþ×î™2w! RÄóùœ=£èý!æÝYA£Sô6ÍeÍRm:©± [ϵ¾ê³w+Î[í™NûµG-!Wæ|Aê~j7>³vž=Ó˜ïpúÏ¿K_cë—ªŸàì|Û/mÜoÎh9·¾;ØÊpþÌÖ3×/ƒñ^˜ý‚”²¸ÍzÔÞ¬Æ7ÓèÑéŽï-Õ;k–aÏÞ|Æp^é{32®CȂ٠龱TÂåå gø6Lj©ct\fô¡|ìgôQJ6-Õ±3…+3œ·Î÷/ôYB$ÈQß£é ¿¦§Ô,~=lZªõSn?sÓŽõ³×±¢†sCcùw.ÃÎ)s#ŽSÇìEÃ'Ìnã_µþcîuÚÐìŒú3ŠfmDzöRµßàè;¬± ‹+ª¡=e8Šá¼þ-4–ñ…eØ3eî¥Ûó­À½ŒRŸcĹdA€\² @.Y —,KÈ% ä’rÉ‚¹dA€\² @.Y —,KÈ% ä’rÉ‚¹dA€\² @.Y —,KÈ% ä’rÉ‚¹dA€\² @.Y —,KÈ% ä’rý™þ©ëºß/¿g¿ @.Y —,«{>Ÿg/ç°_ ×ÿ§Ä 7IEND®B`‚‰PNG  IHDRY7æêú pHYs M MÒέNtEXtSoftwareESP Ghostscript 815.04~8# [IDATxœíÖ1 À0À¿ç!cG={gæô¶X㺼 @—èò‚]^ Ë tyA€./Ð庼 @—èò‚]^ Ë tyA€./Ð庼 @—èò‚]^ Ë tyA€./Ð庼 @—èò‚]^ Ë tyA€./Ð庼 @—èò‚]^ Ë tyA€./Ð庼 @—èò‚]^ Ë tyA€./Ð庼 @—èò‚]^ Ë tyA€./Ð庼 @—èò‚]^ Ë tyA€./Ð庼 @—èò‚]^ Ë tyA€./Ð庼 @—èò‚]^ Ë tyA€./Ð庼 @—èò‚]^ Ë tyA€./Ð庼 @—èò‚]^ Ë tyA€./Ð庼 @—èò‚]^ Ë tyA€./Ð庼 @—èò‚]^ Ë tyA€./Ð庼 @—èò‚]^ Ë tyA€./Ð庼 @—èò‚]^ Ë tyA€./Ð庼 @—èò‚]^ Ë tyA€./Ð庼 @—èò‚]^ Ë tyA€./Ð庼 @—èò‚]^ Ë tyA€./Ð庼 @—èò‚]^ Ë tyA€./Ð庼 @—èò‚]^ Ë tyA€./Ð庼 @—èò‚]^ Ë tyA€./Ð庼 @—èò‚]^ Ë tyA€./Ð庼 @—èò‚]^ Ë tyA€./Ð庼 @—èò‚]^ Ë tyA€./Ð庼 @—èò‚]^ Ë tyA€./Ð庼 @—èò‚]^ Ë tyA€./Ð庼 @—èò‚]^ Ë tyA€./Ð庼 @—èò‚]^ Ë tyA€./Ð庼 @—èò‚]^ Ë tyA€./Ð庼 @—èò‚]^ Ë tyA€./Ð庼 @—èò‚]^ Ë tyA€./Ð庼 @—èò‚]^ Ë tyA€./Ð庼 @—èò‚]^ Ë tyA€./Ð庼 @—èò‚]^ Ë tyA€./Ð庼 @—èò‚]^ Ë tyA€./Ð庼 @—èò‚]^ Ë tyA€./Ð庼 @—èò‚]^ Ë tyA€./Ð庼 @—èò‚]^ Ë tyA€./Ð庼 @—èò‚]^ Ë tyA€./Ð庼 @—èò‚]^ Ë tyA€./Ð庼 @—èò‚]^ Ë tyA€./Ð庼 @—èò‚]^ Ë tyA€./Ð庼 @—èò‚]^ Ë tyA€./Ð庼 @—èò‚]^ Ë tyA€./Ð庼 @—èò‚]^ Ë tyA€./Ð庼 @—èò‚]^ Ë tyA€./Ð庼 @—èò‚]^ Ë tyA€./Ð庼 @—èò‚]^ Ë tyA€./Ð庼 @—èò‚]^ Ë tyA€./Ð庼 @—èò‚]^ Ë tyA€./Ð庼 @—èò‚]^ Ë tyA€./Ð庼 @—èò‚]^ Ë tyA€./Ð庼 @—èò‚]^ Ë tyA€./Ð庼 @—èò‚]^ Ë tyA€./Ð庼 @—èò‚]^ Ë tyA€./Ð庼 @—èò‚]^ Ë tyA€./Ð庼 @—èò‚]^ Ë tyA€./Ð庼 @—èò‚]^ Ë tyA€./Ð庼 @—èò‚]^ Ë tyA€./Ð庼 @—èò‚]^ Ë tyA€./Ð庼 @—èò‚]^ Ë tyA€./Ð庼 @—èò‚]^ Ë tyA€./Ð庼 @—èò‚]^ Ë tyA€./Ð庼 @—èò‚]^ Ë tyA€./Ð庼 @—èò‚]^ Ë tyA€./Ð庼 @—èò‚]^ Ë tyA€./Ð庼 @—èò‚]^ Ë tyA€./Ð庼 @—èò‚]^ Ë tyA€./Ð庼 @—èò‚]^ Ë tyA€./Ð庼 @—èò‚]^ ëk+®@IEND®B`‚libjgroups-java-2.12.2.Final.orig/doc/manual/en/images/StompArchitecture.fig0000644000175000017500000001260311647260573026606 0ustar moellermoeller#FIG 3.2 Produced by xfig version 3.2.5 Landscape Center Inches Letter 100.00 Single -2 1200 2 6 1650 6900 4275 8175 2 4 0 1 0 7 50 -1 -1 0.000 0 0 7 0 0 5 3375 7275 3375 6900 2550 6900 2550 7275 3375 7275 2 4 0 1 0 7 50 -1 -1 0.000 0 0 7 0 0 5 2475 7275 2475 6900 1650 6900 1650 7275 2475 7275 2 4 0 1 0 7 50 -1 -1 0.000 0 0 7 0 0 5 2475 7725 2475 7350 1650 7350 1650 7725 2475 7725 2 4 0 1 0 7 50 -1 -1 0.000 0 0 7 0 0 5 3375 7725 3375 7350 2550 7350 2550 7725 3375 7725 2 4 0 1 0 7 50 -1 -1 0.000 0 0 7 0 0 5 4275 7275 4275 6900 3450 6900 3450 7275 4275 7275 2 4 0 1 0 7 50 -1 -1 0.000 0 0 7 0 0 5 4275 7725 4275 7350 3450 7350 3450 7725 4275 7725 2 4 0 1 0 7 50 -1 -1 0.000 0 0 7 0 0 5 2475 8175 2475 7800 1650 7800 1650 8175 2475 8175 2 4 0 1 0 7 50 -1 -1 0.000 0 0 7 0 0 5 3375 8175 3375 7800 2550 7800 2550 8175 3375 8175 2 4 0 1 0 7 50 -1 -1 0.000 0 0 7 0 0 5 4275 8175 4275 7800 3450 7800 3450 8175 4275 8175 -6 6 5025 6900 7650 8175 2 4 0 1 0 7 50 -1 -1 0.000 0 0 7 0 0 5 6750 7275 6750 6900 5925 6900 5925 7275 6750 7275 2 4 0 1 0 7 50 -1 -1 0.000 0 0 7 0 0 5 5850 7275 5850 6900 5025 6900 5025 7275 5850 7275 2 4 0 1 0 7 50 -1 -1 0.000 0 0 7 0 0 5 5850 7725 5850 7350 5025 7350 5025 7725 5850 7725 2 4 0 1 0 7 50 -1 -1 0.000 0 0 7 0 0 5 6750 7725 6750 7350 5925 7350 5925 7725 6750 7725 2 4 0 1 0 7 50 -1 -1 0.000 0 0 7 0 0 5 7650 7275 7650 6900 6825 6900 6825 7275 7650 7275 2 4 0 1 0 7 50 -1 -1 0.000 0 0 7 0 0 5 7650 7725 7650 7350 6825 7350 6825 7725 7650 7725 2 4 0 1 0 7 50 -1 -1 0.000 0 0 7 0 0 5 5850 8175 5850 7800 5025 7800 5025 8175 5850 8175 2 4 0 1 0 7 50 -1 -1 0.000 0 0 7 0 0 5 6750 8175 6750 7800 5925 7800 5925 8175 6750 8175 2 4 0 1 0 7 50 -1 -1 0.000 0 0 7 0 0 5 7650 8175 7650 7800 6825 7800 6825 8175 7650 8175 -6 6 1800 600 4425 1875 2 4 0 1 0 7 50 -1 -1 0.000 0 0 7 0 0 5 3525 975 3525 600 2700 600 2700 975 3525 975 2 4 0 1 0 7 50 -1 -1 0.000 0 0 7 0 0 5 2625 975 2625 600 1800 600 1800 975 2625 975 2 4 0 1 0 7 50 -1 -1 0.000 0 0 7 0 0 5 2625 1425 2625 1050 1800 1050 1800 1425 2625 1425 2 4 0 1 0 7 50 -1 -1 0.000 0 0 7 0 0 5 3525 1425 3525 1050 2700 1050 2700 1425 3525 1425 2 4 0 1 0 7 50 -1 -1 0.000 0 0 7 0 0 5 4425 975 4425 600 3600 600 3600 975 4425 975 2 4 0 1 0 7 50 -1 -1 0.000 0 0 7 0 0 5 4425 1425 4425 1050 3600 1050 3600 1425 4425 1425 2 4 0 1 0 7 50 -1 -1 0.000 0 0 7 0 0 5 2625 1875 2625 1500 1800 1500 1800 1875 2625 1875 2 4 0 1 0 7 50 -1 -1 0.000 0 0 7 0 0 5 3525 1875 3525 1500 2700 1500 2700 1875 3525 1875 2 4 0 1 0 7 50 -1 -1 0.000 0 0 7 0 0 5 4425 1875 4425 1500 3600 1500 3600 1875 4425 1875 -6 6 5475 600 8100 1875 2 4 0 1 0 7 50 -1 -1 0.000 0 0 7 0 0 5 7200 975 7200 600 6375 600 6375 975 7200 975 2 4 0 1 0 7 50 -1 -1 0.000 0 0 7 0 0 5 6300 975 6300 600 5475 600 5475 975 6300 975 2 4 0 1 0 7 50 -1 -1 0.000 0 0 7 0 0 5 6300 1425 6300 1050 5475 1050 5475 1425 6300 1425 2 4 0 1 0 7 50 -1 -1 0.000 0 0 7 0 0 5 7200 1425 7200 1050 6375 1050 6375 1425 7200 1425 2 4 0 1 0 7 50 -1 -1 0.000 0 0 7 0 0 5 8100 975 8100 600 7275 600 7275 975 8100 975 2 4 0 1 0 7 50 -1 -1 0.000 0 0 7 0 0 5 8100 1425 8100 1050 7275 1050 7275 1425 8100 1425 2 4 0 1 0 7 50 -1 -1 0.000 0 0 7 0 0 5 6300 1875 6300 1500 5475 1500 5475 1875 6300 1875 2 4 0 1 0 7 50 -1 -1 0.000 0 0 7 0 0 5 7200 1875 7200 1500 6375 1500 6375 1875 7200 1875 2 4 0 1 0 7 50 -1 -1 0.000 0 0 7 0 0 5 8100 1875 8100 1500 7275 1500 7275 1875 8100 1875 -6 2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 2400 4575 3825 4575 3825 5325 2400 5325 2400 4575 2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 2400 3375 3825 3375 3825 4125 2400 4125 2400 3375 2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 5025 3375 6450 3375 6450 4125 5025 4125 5025 3375 2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 5025 4575 6450 4575 6450 5325 5025 5325 5025 4575 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2 1 1 3.00 60.00 120.00 2250 6750 2250 5625 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2 1 1 3.00 60.00 120.00 3000 6750 3000 5625 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2 1 1 3.00 60.00 120.00 3600 6750 3600 5625 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2 1 1 3.00 60.00 120.00 5400 6825 5400 5700 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2 1 1 3.00 60.00 120.00 6075 6825 6075 5700 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2 1 1 3.00 60.00 120.00 6975 6825 6450 5700 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2 1 1 3.00 60.00 120.00 3150 2025 3150 3150 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2 1 1 3.00 60.00 120.00 2325 2025 2775 3150 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2 1 1 3.00 60.00 120.00 3975 2025 3600 3150 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2 1 1 3.00 60.00 120.00 5850 2025 5850 3150 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2 1 1 3.00 60.00 120.00 7725 2025 6450 3150 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2 1 1 3.00 60.00 120.00 6825 2025 6075 3150 3 3 0 1 0 7 50 -1 -1 0.000 0 0 0 11 1800 3075 3150 2400 4725 2550 6600 2400 7575 3600 7500 5250 6375 6225 4050 6225 2325 6000 1575 4575 1800 3075 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 4 0 0 50 -1 16 16 0.0000 4 195 825 2775 3825 NodeA\001 4 0 0 50 -1 16 16 0.0000 4 195 825 5400 3825 NodeB\001 4 0 0 50 -1 16 16 0.0000 4 195 840 5400 5025 NodeD\001 4 0 0 50 -1 16 16 0.0000 4 195 840 2775 5025 NodeC\001 4 0 0 50 -1 16 16 0.0000 4 195 825 8325 1350 Clients\001 4 0 0 50 -1 16 16 0.0000 4 195 825 750 1350 Clients\001 4 0 0 50 -1 16 16 0.0000 4 195 825 600 7650 Clients\001 4 0 0 50 -1 16 16 0.0000 4 195 825 8250 7650 Clients\001 libjgroups-java-2.12.2.Final.orig/doc/manual/en/images/StompArchitecture.png0000644000175000017500000002114211647260573026623 0ustar moellermoeller‰PNG  IHDR=û¨ª¹b pHYs M MÒέNtEXtSoftwareGPL Ghostscript 8.62ƒ-ÏèIDATxœíÝÑšª¸Po¿ÿ+{.œÃ¸!ªÊZW3»[»Œ!?IïÇãIüé]@g÷û½w ÿÙx¡æƒÆ9WËØìj>h„î=nn=»Z¨÷økIjn"`IÍ|%»JÆš ¸×~yKî÷¸/|©65·¹¶ƒ"¿´J]%cÍ5üÓ»øÁˆ¹üLäñx|.—«¹¹Ùš ÈØìjn®j÷~1·ÈKn‰ÜŠ(ã_Íl”±ÙÕŠÜ ¹@&r €Lä™È-2‘[d"·ÈDn‰Ü ¹@&r+¢ŒwgQ3elv5‡"·‚Šü KÔÌF›]ÍqÈ-2‘[d2bn¿½ÿì÷¨ª¹¹àßW»[ÆfWssU»÷Óˆ¹u ÜçVz›šª}Tglv57T»{ßn·â/oÝÔçº7ÂöJÔ|DœJ.çÅÖî*kÎnèÜšt?iÚñ.¨y‡1{{ÆfWóãto¹@&ƒîo”Ü ¹@&zÐY÷­ÔW÷Õ|Ð8{º›]ÍнÇÍ­gW õ-IÍM,©¹€¯±dWÉXsƒ^OùsyKµ©¹­Èµù¥Uê*k®Áþ™Œ˜[ÁÏDfo£ææÂÞ¤ç ŒÍ®ææªvï§s €¼ä™ü–[÷¿Íþôó¿ùUÆ9¾šÙ(c³Vsð¡þ‡ëà?/¯<û‚Ë.è%þP¿u¾5û¼+æñxÈ€\R õ ö·ÒMŸøUœ¡~Ó:áÊ$n)i—fšŸš~óí^×O§ßk8S:€V² õ]Oø,hšQ.}âõ¦Bß^ÉúóÐË5Cý¹5›œÇgN¶Ðâ¸l¨¿h¾µ}—ï+s,€˜®êÞ~ãõ‹MÂfZ žÍ| ࡆúM¹õ|–#÷ãj0o  ÁoH–¡¾Á:áŽB÷}Äzé2Îg¨ßš[³;l z»ðñÈG¯ÙÜÊø2ÕÌF›}œšS õ?ìoMSÈ×ùé±;x{™!) Œ/MÍl”±ÙÇ©9þPÿÛuëE¼þôó7—»ò›ÛŸ€V‚õ¾Ç€LFÌ­Gì¯$˜½˜GÍÍU½5c³«¹¹ªÝûiÄܺîs+½MÍ Õ>ª36»šªÝ½o·[ñ—·.Îç—·W¢æ#âTr8/¶vWÉXsvCçÖ¤ûIÓ¾FœQÉvƒÔ\@ÆfWóãto¹@&ƒîo”Ü ¹@&G¿Ç$»î[©¯6î5ªù qöt36»š¡{›[Gîùx’¯%©¹‰€%5ð5–ì*k.`Ðë #.o©65·¹¶ƒ"¿´J]%cÍ5Øß “s+ø™ÈìÍcÔÜ\Ø›ô”±ÙÕÜ\Õîý4bn—Ü ¹QÆ9¾šÙ(c³«9¹@&r €Lä™È-2‘[d"·ÈDn‰Ü ¹@&r €LäVDï΢f6ÊØìjEnù+–¨™26»šã[d"·ÈdÄÜ ~{ÿÙïQUssÁ¿¯v·ŒÍ®ææªvï§së¸Ï­ô657Tû¨ÎØìjn¨v÷¾ÝnÅ_Þº©Ïuo„함ùˆ8•\ ΋­ÝU2ÖœÝй5é~Ò´ã]PócööŒÍ®æÆéÞr €LÝß )¹@&r €Lä™È-2‘[d"·ÈDn‰Ü:Q÷ÏÏo¿ÈøV•½å3ÖŸ±æëÉ-€Êß·¹”.ÒÜ‘Ü:Qد9HÄÁÌéºMº‚û’[= ­_É­s™rÁÅÅÀý~OTmz‹Òü*QŸITj4æ[WZGÈ­ÓY*„kd ƒ,u†%·ʱÍO²t˜,uF&·®`ÊÜ„V#r H/E¤(2¹EDŽp¶KÑ[R™…ܺˆ¥B“i5çó[@bÁ#!xyI™o]Ç”k#‡:ï*ÁËËKn´'´Î#·€”"CäÚ °¿u©çR¡½BûÄtd‰ûŒ74r?‰\[ r ˜ñ ª)®Ž ij™WudZ[W3å"¸gÌ4쨳Oòf?ý¡˜GÐÔn½ ©OnHÌñhW޼oå5ÆÖ ˆÙIbVU•Üâg­®æwœÇÑ}®ðú§›,N^Ih]Lnub©p%œZU¾ý›ótO¬OS1ŸðÀ XRyr‹nÛæŸ§ØŽÿ‹sW,ˆà X•Fï¦c?²%~Ñu¥-­mPŽ6AÖ>ã0ßÂJP…ºõÔt+,ÃÁ©2¸o5wï'Û° ¹UV´“ÓuÓ(ðŒ.ƒÂIº÷û|ö‡çÿöZ?Ô?û’[ÝœquF®¬ZâÄgHšXëÞ6À.xu%›1¹UAØ]ëÝ,¶R ol9½{~÷JM³‚[=œr’žfáš‘¨ªÚ}cÉ©}FhÅ!·ò)3$mÑw#‘˜×ˆvqFz ­PäV#ÝŸŸãyû÷ÐGÒ¢az ­häVg_— ËÇÕŽ… ×Çnùµ¼.¸kIXMÒbJ¯#«ñå›:¹—­¯–gß刧îçï{ w¿‰Ý—ù­˜äVoS®ò¬ ìkºó.¾÷Vþªy`üºlè¬12¹È€‡J¨óÙ8• î¼^±qÙ0T·ä“܊¡×X_6t$Æ÷OïFçžFðêšÃáñxÌÞ–ÅÁ˜‚ùV7® ¾1Fðæâ.ñ6ñÒ!³[}ÌÞ'Ôas[–Œ¬^Í Ìê~\¸›sæ[—ê~dÆ¡)xÕ÷kT_?…¢[Æg¾u¯‡„ïï€+}.~8S0ߺ‚ÕsXÑe–³ôGí4Çg¾uºç1°ñ0ätϸÀ$Th==þÿ¥ÛW–Ävrë\hˆfãQr™‘Ü:‘Ђu×#¿~ÿ€è Hne÷YþPç<­'}5 ¹u C3Dsð[¸ÚÃr«½ã¡U~ÊWžÛÿ°¿C2¹Õ˜™Ö:íÃíòÐÚ~Aï×§:þ$'·šq'ˆ¦á!éÐŽCnµÑðœîɺU]vzwÆ—&;*#[ ˜fm¤¡ÈZÄ!·9umÐÉì㨬Íý ÷sB?¹æq`–g¾µ‡K0vÐbƒ+Z¦\Ý 7ß:~kö+Ç_·¦†|ëÂ8ÆÊ­ƒgI ØçìÓ¯‹OïœPö5Pn½¦Î¯}®cbÕ8B ¼v+Zt7ÊþÖ‘àiþÙ¬qØàl½BË.WGCÌ·ÞBk{o‹³08Õܪ˜×F8õe:¶Ó9Þ7ެmüú‡"ž\lˆÜº}x_ûzÄúôó¨Í£—àÎ>ˆ" jPýuÂ[YÏ£UGÓjÂý<0_ÿ·{_ê^À°ŠÏ·~êÜAæX¯R¯¡Vªm9É‹ÓsF›µ×¸l*úó­-œcýºpÊ8â÷€%q¥Êó­-çAçXoRO¹^EndZ™í«­&[ðTv¾õ5´ͱâWKέPIVæÌ2‘Êó­–¤/¦µÇä}ç 5‡ï¥Xж©û“×sº¶Ÿâ:û“[[›}ãmÚqü}?cÕñ$N…¯TppiaaêXI»WÒ²Ó¡•bUŸ Ô\'|íÜñ¯¼Ø.ãJz–g»3Þñø½ÈñWª™[OÖ©ºÓòãh{‚èàeEÍܪ4ÇzÓvÊU²‰hbGßH±ízS®Ë ×Ê:pÃË5 ^—ÐEÆèŒÆÊ-gCÙ•[§2庀Ü ¹Ð’)×Ùä™È-€ÆL¹N5Pn¹˜ €r €ä@{– Ï#·ÈDnœÂ”ë$£ä–‹2j%·®gÊu†šß¿“î»n¨ ±Î°n¨ÎÀ¯äÖ¥KÇu†%v~2Ä:¡Í- K…Í ‘[”!·ÎeÊÕ–Ü “ú¹es  ’ú¹Х†ä™Ï-‹„@¦\­Ï-Š‘[dR9·,¡X*l¢rnPÜ “²¹e‘ÈRáqes €’j~ÿÖ“­ç)ÛìË\ùÑ‘§møÚêÛ–fº'1ßÊ­û‚ÃT@÷Jèû<þÖ½ +˜[#L¶^˜Äé ¢k…-®ƒ æÖP"$ôt~M_ÞQmk´ÉÖÓú«~;³{ûÍן®l|}ì×2¸F¯Î—1ßJo}AfÚZŸÝuxûéç“<A;YÄé ®ÖYg©ðˆR¹5ìùþÒ«þ;^Gœ¥Ÿ¾=|ö±_ÿ4½ôê ÷¿­T•Ê­Áqúö6ô¼þï–T£—‹;ÃmázBý3ÔÙßüy.;4Ÿq~mÕÁ›=¦^᳆†&urë6üºÄ#Åz“~þÔPÄõ_=ß#­ºƒuÂjNMŽiåg寈®8®é p±"¹å´åivböš±•Ÿ¾>|é±Ä×·3è-œ§Ô:!·ñeÚêxý—¥Ÿ~..}>ÛmuH²«Êaòù¾ -NRdš’b¾•¢È^FkœÑ^ïOÆiœq^i[Ö ½÷@FÖ'ö©[ŒCn‰Ü “ô¹es ÈË×és €¡È-2‘[d"·ÈDnIîÜr1!K •;·Ü ¹@&r €Lä™È-€Î\Rø¹@&r €Lþô.`,–˜è °ÜºŽ[{0Ñ`7ë„d"·ÈDn„`Ës#¹П-Ïíä™È-2‘[d"·ÈDn‰Ü ¹@&r €Lä™äÎ-_ ”a@Û(wn0¹@&r €Lä™È-2‘[d"·ÈDnIúÜòI= †ûýþxÕÖ oã-Þ¾õþõ-¨×Ÿ®l¼ýÂÊzàP-Ðõ.f¾•ÞúYí0Oo¿ùöÓÏ'y‚³% È­ –Î|?gE¯#ÎÒOß>ûXÂÒ²mY¨!¹UǃˆË.’Ò(¬àþÖmÈ-®çKnþªw .…ï.Ng€3ÔÌ­1q{ƥ螺‘V®ì g<ª<ÇÎÖ «9õ¤ø™U_ÍDßÎ`~ÆIäV)³;³×Œ­üôõáK}øçÿÒÑ•á“åbÎSvpÀ-®§·ñeZÐ{ý—¥Ÿ~..}>ÛçO}¦'¬+;Ãç)‹ÎÀI*ìÑr+Z=¡ŒÖ8£½ÞŸ”oœò/ðlÖ ȤrnqI}UÎ-€h,'·È¤~nY*‚0Ùj¢xné"ÅÏ-Š‘[W°HØJýÜr5<@%õs €JäÀé,64DnY*(cˆÜèÈd«-¹@&r àD&[Í’[¶¸j%·®g²u¹@&r à&['ùÓ»€ë<·¸úv#{lLtØg ÜêΙ¡¼îgÉ…Y' ¹ИÉÖ©ÆÊ-ŸâÈn¬Ü8›ÉÖÙä™ —[– ó˜l]`¸Ü8‰Ðº†Ü “sËR!МÉÖeFÌ-ò4·L¹€†L¶®4hnÔ¸¹eÊ4a²u±qsë&º€Ã„Öõ†Î­›èÈfôÜØÍd« ¹eÊì!´z‘[·›èÈCnýKtÛ™lu$·š¹ßWU1[šZ}É­ÿ˜rÄ'·þ"º€u&[ÝÉ­w¢ X"´"[3vD×ôû¡2¯WU1[ƒ,tÖÉ­yf]¤äú— eœÍd+¹µHtA/=¡‡ÜZ#º ‡KäÖ[¢ëí‚o½ªŠÙ$2Mkât“­PäÖwf]Ääú—Sˈpè ­häÖ&¢ ¹µ•è‚.ºÏ&M¶¢‘[?˜®Ùƒª{Âõª*fk]¯.¤ëÆäTâg+ç_1OÍzU³5ÊøR{½Ë'•áêÃ1™oýÌ‚!tÑë¶/D#·ö]p½ë§>&[1É­D\æñx\!V#“[û=£Kzq½ ׿\PÆ3±®_!Z‘ýé]@nÏέ—s±×þÖ±ûZF¯åpŽÏ|«k†pªk±çò‰ÐŠÏ|« ÑyM¯ÐJÁ|«™iͰw!PÐy§†Ï§½þÒv3ßjìyt9 ¾)±zÂoäV{Ó‰¡ãbr„¦&·Ná:Ch®Éb†Ä*@nÈÄ âp0–!·Îeâ í›rI¬bäÖL¼  Ç]Irë"¯WÉ;Š`·S.ÇZarëRÒ Îæø*Onu ½àˆÙ)—{^ŒCnu#½  ÑhäVgÒ vx½í“g4r+éÛI¬ÁÉ­@¤¬øŒ+ŸŒ“Ü çí¾òKçXàÜ êõŒòváûëWEJ8É–¸òõ c’[Ñ7ýš¨}wÐÙñSc ³Ì®øJnåð6ýº8ªÛŽ +O²~"lbÇ+qÅvr+™Ï»m>ÔC]ñÑvb×ðq™}ø“¥ÂÉ­¬^ÔÏ1ý-ÞB%Ö>Çç—×ÿi>5É*'·*ø<þß둈ã¯Ý’æAgg•)×häVMë³1~ÒvIs„áÕi§’[ÐØú0½”j©÷×åè[×bÊ5¹—Z[W6)£™-Ulp¹!¬oRöŠ„&ò»†ì‡Ü‚ 6^2zÝ8Ò‘[ÀúlìÈ¥7•’É”kr RrOt†õOïšyý>Iª’[d"·€RL¹Ê“[d"·ÈDnÕX*¬Mn‰Ü 2å*Ln‰Üj2åªJn‰ÜÊ2å*In‰Ü*3åªGn‰ÜŠ3å*Fn‰Üê3åªDn‰Ü ¹ ÁRar €Lä0 S®ä™È-` ¦\È-2‘[-=Þ%ÌèUUÌÖS®ìä™È-`8¦\©É-2‘[Àˆì¿æu÷æˆù™È-2‘[dò§w§ uµëÆÝD5”k×6cÓ©ù \]4šÊ¹õ즡úÇ×’ÔÜDÀ’f¬³äÛ±fV”½žð~ûÒ–jSs[‘k»Å.¯ÒÛ±fÖÙß “š¹ü,fö3jn.ò½|26š›‹ÜE#«™[T%·ÈDnõ‘q}@ÍCÉØtj„Ü ¹@&r €Lä™È-2‘[d"·ÈDn‰Ü ¹@&r«ŒwvQóP26š!·º‰üõ KÔ<”ŒM§æÈ-2‘[dR3·‚5Àìw°ª¹¹Èßu›±éÔÜ\ä.YÍܺî¯+=UÍ Å26šŠßEÃ*ÞpSíþ2·W¢æ#âT²Qœ‚k¿ÝkfIñÜšt?áÚÑÎjÞ!oÎØtjÞ!oc”Ü †²û[”$·ÈDnÉŸÞœ®û6ì«»‰j>(×®mƦSóA¹ºh4•sëÙMCõ¯%©¹‰€%Í XgÉ·;cͬ({=aäÏô-Õ¦æ¶"×v‹]^¥·;cͬ³¿@&5s+øYÌìgÔÜ\ØüÜr6š›‹ÜE#«™[T%·Èä=·î›ýéçó«ŒëjJƦSón¹Fþ¿®ƒÿ¼4óì‹5] ÐWº‘ÿ¿ùÖì­<ïãñ7©eù7íouŸp±°#ÿ¿ë„+³¶¥h]šZ~>júÍ·_x]0~ÿ­¥º;@UIGþf×>+˜¦K¤xý…©²·Òן€ ºŒümrk6*Ïœ",¤0«×Èßl¾µ}[ï+s,€ºŒüßï¿ñ‚Å&a3-}NÏf¾p½È#ÿŸ×‡¹—W«€y[ñ ~{1€¼’Žü›Ö wT¶ï3ÕK×¥p±°#ÿ¹5»¥¶±‚·+|zÍ­Œ/SÍCÉØtjÞ!ãÈÿ×þÖ4g|ý—Oôy‘þö"¦)á‘ÒÉøÒÔ<”ŒM§æ}äù߯ËXÿíן~þæÒcW~sû“p’\#¿ï1 “š¹õˆñÕKf/•Qss‘/FÍØtjn.r¬fnÝ÷וžªæ†â›NÍ Åï¢ao¸8Ÿ_Þ^‰šˆSÉFq ®ývg¬™%ÅskÒý„kß'!Ψd»Aj"cÓ©y‡¼]4ŽQr €ÊîoP’Ü ¹@&ß¿Ç$»îÛ°¯6î&ªù \»¶›NÍåê¢ÑTέ#7y<É×’ÔÜDÀ’f¬³äÛ±fV”½ž0ògú–jSs[‘k»Å.¯ÒÛ±fÖÙß “š¹ü,föÆ3jn.ì ~n9›NÍÍEÕÌ-ª’[d"·úȸ> æ¡dl:5Bn‰Ü ¹@&r €Lä™È-2‘[d"·ÈDn‰Ü ¹ÕGÆ;»¨y(›N̓[ÝDþz…%jJƦSóä™È-2©™[Á¿`ö;XÕÜ\äïºÍØtjn.r¬fnÝ÷וžªæ†â›NÍ Åï¢ao¸©¿v™Û+Qóq*Ù(NÁµßîŒ5³¤xnMºŸpíhg5ï·?gl:5ï·‹Æñ?’,΂`)áIEND®B`‚‰PNG  IHDR=û¨ª¹b pHYs M MÒέNtEXtSoftwareGPL Ghostscript 8.62ƒ-ÏŒIDATxœíÕA À0À¿çC/²¤U°ßöÌ,ˆ8¿àoPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPâ[”ø%¾@‰oPr½àóŒ‰ÞkIEND®B`‚libjgroups-java-2.12.2.Final.orig/doc/manual/en/images/StompProtocol.fig0000644000175000017500000000240011647260573025757 0ustar moellermoeller#FIG 3.2 Produced by xfig version 3.2.5 Landscape Center Inches Letter 100.00 Single -2 1200 2 2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 4950 2025 7575 2025 7575 2850 4950 2850 4950 2025 2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 4950 2925 7575 2925 7575 3750 4950 3750 4950 2925 2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 4950 3825 7575 3825 7575 4650 4950 4650 4950 3825 2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 4950 4725 7575 4725 7575 5550 4950 5550 4950 4725 2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 4950 5625 7575 5625 7575 6450 4950 6450 4950 5625 2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 4950 6525 7575 6525 7575 7350 4950 7350 4950 6525 2 4 0 1 0 7 50 -1 -1 0.000 0 0 7 0 0 5 8325 7800 8325 1650 4425 1650 4425 7800 8325 7800 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2 1 1 3.00 60.00 120.00 2400 2400 4800 2400 4 0 0 50 -1 16 18 0.0000 4 285 1290 5625 7050 Transport\001 4 0 0 50 -1 16 18 0.0000 4 225 1065 5775 2550 STOMP\001 4 0 0 50 -1 16 18 0.0000 4 225 1230 5625 4425 NAKACK\001 4 0 0 50 -1 16 18 0.0000 4 225 840 5700 3525 FRAG\001 4 0 0 50 -1 16 18 0.0000 4 225 750 5625 6150 PING\001 4 0 0 50 -1 16 18 0.0000 4 285 1095 5625 5250 FD_ALL\001 4 0 0 50 -1 16 18 0.0000 4 225 600 3375 2325 TCP\001 4 0 0 50 -1 16 18 0.0000 4 225 1995 300 2475 STOMP clients\001 libjgroups-java-2.12.2.Final.orig/doc/manual/en/images/StompProtocol.png0000644000175000017500000001207311647260573026005 0ustar moellermoeller‰PNG  IHDRœûí%* pHYs M MÒέNtEXtSoftwareGPL Ghostscript 8.62ƒ-Ï ŒIDATxœíÝÑš¢¸ÐÊùúý_™saã†ÀFÅZWÝ ‘*Ëlþ$H†á®>ø J)WœjR‡Èˆ¼RD}ÏÓLþò‹ÏìVŠO6~þwõ‘p{²vR”À0 ¯Á.Y@J–’%ì÷æ’%¤d )Y@J–’%¤d )Y@J–’%¤d )Y@J–’%¤d )Y@J–’%¤þ\}ÀŠRÊÕ‡@G†a¸úȸ>»Î×퉅1.R²€”, e¾„“´ŒóNf&»,Î|jÖœI]BJ)óX|°ÒÂÑ|¤.á$‹5G½Ô¨—)«—RT'pu šÇÀë‘ceÀ·Éúbl îÈ=úTX(8 Oê~!Å œL]Âo°Š8º„ßIÀ™Ô%ü’®5Í’Ê•ÆûÖbÎ÷útÑÀ§î`qƒo\íü~ÅCåêNðéÒï ôéß1®üJã¹Å}¿:/jÒõîäÜÑߺ¤~¥ñë$±ýºåJ›Ç^|¯«So2ÿu9Q€nýgîýÓ•Æ;ŒÁ¤…ñ‘ÅèS;ì¦çGÎi&ù1š? tâ+ë¸à}³J¡ïx Å,ðЭõu\»?½õx¸p¼¢%ÃÇ£V&Ì®y6Á­®JøÝZ~Ø£¶ádO^ÂóXë’±w 'ÛC“—Þz$«ÛϺä;Ì÷=RyÜ|wg Ïcý[—ŒQXÿ/Ü%òLîÑ ãQKx˜P—‡YüR¢Æo*ª´Ùø R—{Â&Ô%¤d )Y@J–’%¤d )Y@J–’%¤d )Y@J–’%¤d )Y@J–’%¤d )Y@ÊýÞáJ)WÔÈèÝ0 W¬0Æ@J–’%¤Ì—@ïL¼ó®Ïù3Y7Ðg÷Áùº=±0Æ@J–’%¤d )Y@J–’%¤d )Y@J–’%¤d )Y@J–’%¤d )Y@J–’%¤d )Y@J–’%¤þ\}À•R*ÏÃPßæµA½åÊ6‹×·çKÔ%ÀeêQ´ºïâîI›ì¦.R-¥À|›W§_JY­<ê¼?ÛØ&‡S—×XàjÙ`²Íøˆêäd²¸9Ñ!Yô«RdTª–y½Â·ÉàŸ²V¸è¹w ÕR7l­-òcFgR——i™ù0—~ ê uàšà13¶†Çb#œF]\cëÔ$!FWÔ%@w>1㳕 ‘1—P—½Øº‚kþ5*‚ä*²¸Å/|,o~,ߺˆ,.Ö¾škòÈ⃋ómæK€ýö­àúôTûw8®î"NN¦. %KHÉR²€”, %KHÉR²€”, %KHÉR²€”, %KHùÎy¸· ¤s²zçVôÏ)Y@J–2_½3ñλ>çÏd Ü@ŸÝçëöÄÂ)Y@J–’%¤d )Y@J–’%¤d )Y@J–’%¤d )Y@J–’%¤d )Y@J–’%¤d )Y@J–úsõ·QJùùù†aÓ³¯ÇÜqµÁÉf•w3ê`›Iým;zóRÊü <ü¥K–›í‹“M{Õk Jƒï%Å»c_š Y|Ѿ~¹²Wcƒó ^¬æ™ ÙG–Û4vÊ»%½yxT‚d7Yl¶#N/ * ~Ú½>Ø%H²ø–­½ó…P'/(ø}d °Çá#]öæ‚$'K€¶ÆÉêöãTc›æÙÖ—fµŠÀWŒò¦`xýãu-ȧKïîW_šUê`¿}çò‡‡Á¤ÁOíï¸\‘Fê ò:—¯<;y¤¥7¯ÔõëÓBi²º8ÀbewÇ“q­MK‹•Õ—¦…,Rû¾2ëØ6¿±¼ioVE²ƒ,NÕÞS7NÆ,Žƒ5>¾4#c‚°ÓiCêÆîõùÇPJQ—’%¤d )Y@J–’%¤d )Y@J–’%¤d )Y@J–’%¤Ü£nÀ4èœ,Þux¿ ˜0Æ@J–’%¤Ì—@ïL¼ó®Ïù3Y7Ðg÷Áùº=±0Æ@J–’%¤d )Y@J–’%¤d )Y@J–’%¤d )Y@J–’%¤d )Y@J–r^`ƒú=b_÷þ´Mx§á±ÙÅv^ÏV^buêà$ÝÞ«œœºجåì~²Í+HJ)yepH#K]œá.Ò-Yœ¤>›Â­ÉàNRŸd Ð;\ý3÷l¶XèëŸL]ÜŒa®©K€ÍÎ,AÆÌ=S—w%]ú¡.N’L¡Ï÷$]Q—gØ×õ[Áuêà+ÎYë5ù>•ÕµíKÔ%ÀIŽí²@WÔ%À;¾Õñ{/÷þìê‹Êž¯R—’%¤Œq×hYÙe`ê.d p 9ñ›ã %KHÉR²€”, %KHÉR²€”, %KHÉR²€”, %KHÉRî_7ÐrÛ(¸,Þ¹gý3Æ@J–’%¤Ì—@ïL¼ó®Ïù3Y7Ðg÷Áùº=±0Æ@J–’%¤d )Y@J–’%¤d )Y@J–’%¤d )Y@J–’%¤d )Y@J–r^`ƒO÷ˆÜEøµÙøàä¿‹mΟ¿–{wK] å>äí÷*/¥,nÜíÝÎQ—›-V!¥”Õº¡e›O/Ôþ*œO]¤ŽíÜÇ!¯I³ã#ª“Éà$-I 'nJ–çi,,*…μ^¡²H)&0÷l¶åÂ0 ¯eZíå…ÅÁýS—ر:K5ó›¨K€ÍN. æ+ƒéº¸@½4·#K€k,7õŠGÆtK–Wª”&“§IÏd p™ÕÒ¤¼ù´==%@w/H|="Núd°AûE${íxJœtH]@J–’%¤d )Y@J–’%¤d )Y@J–’%¤d )Y@J–’%¤d )Y@J–’%¤Üïn ”rõ!@,Þ Ãpõ!À c\¤d )Y@Ê| ôÎÄ;ïúœ?“%p}vœ¯Û c\¤d )Y@J–’%¤d )Y@J–’%¤d )Y@J–’%¤d )Y@J–’%¤d )÷èš´ÜÖ½„K]@J]4™Ô¯2å×"ù1sêR²€”1.àHã Ð|th>{?>;Ùk¾A¾û¼…yãï‡ý¾q)Å0Wº8Þ¼ß_\6y°²W¸{ã!µ¬Uc‘ºøŠÅ³þ÷ ;îI©Qi³ñE'ÿž´?/8Æÿ¾þaº8Þ¼ƒnéŽß·ÙÑ}/¦Â{¢¬¶/3vS—§Ú7ŽôšÀP%tK–gø4ÊÔn¾£Pé‡1.àëÆzâÞlÇTy?d p­10pµ·¹uY c\ÀI>­ëm/V¶ÆÉÏ?ã`ãõ"õ P*ÍO«S—_7vÍ‹-|ZλøH~ T[Øé´sU'Å«ž³¾«Ï?†RŠº€”, %KHYÇÜ^‡SO£. %KHÉR²€”, %KHÉR²€”, %KHÉR²€”, %KHÉRî_7ðºŸ9tK–@ïÜè‰þã %KHÉR²€”, %KHÉR²€”, %KHÉR²€”, %KHÉR²€”, %KHÉR²€”, %Kد”ò#K à KHÉR²v†á5R UJ†áG–@Bœðdcüüüüû/`Ÿ1N|šx‚Å?xY‡Q£ð‹©ñÁTÀôéYlIEND®B`‚‰PNG  IHDRœûí%* pHYs M MÒέNtEXtSoftwareGPL Ghostscript 8.62ƒ-ÏÁIDATxœíÕA À0À¿çCÄ„¤U°ßöÌ,Îë¾ç%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@å%T^@u™Â5XúûIEND®B`‚libjgroups-java-2.12.2.Final.orig/doc/manual/en/images/Tunneling.fig0000644000175000017500000000434411647260573025107 0ustar moellermoeller#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 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 2850 5250 2850 5250 3525 3975 3525 3975 2850 2 2 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 5 3975 1050 5250 1050 5250 1725 3975 1725 3975 1050 2 1 0 1 0 7 50 -1 -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 2775 3975 3225 2 1 0 1 0 7 50 -1 -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 2625 3975 1500 2 1 0 1 0 7 50 -1 -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 7575 2775 5250 3225 2 1 0 1 0 7 50 -1 -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 7575 2625 5250 1350 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 375 1425 1500 GMS\001 4 0 0 50 0 16 11 0.0000 4 120 225 1500 1950 FD\001 4 0 0 50 0 16 11 0.0000 4 120 405 1425 2400 PING\001 4 0 0 50 0 16 11 0.0000 4 120 660 1275 2850 TUNNEL\001 4 0 0 50 0 16 11 0.0000 4 120 660 7800 2850 TUNNEL\001 4 0 0 50 0 16 11 0.0000 4 120 405 7950 2400 PING\001 4 0 0 50 0 16 11 0.0000 4 120 225 8025 1950 FD\001 4 0 0 50 0 16 11 0.0000 4 120 375 7950 1500 GMS\001 4 0 0 50 0 16 11 0.0000 4 150 1020 4125 3225 GossipRouter\001 4 0 0 50 0 16 11 0.0000 4 150 1020 4125 1500 GossipRouter\001 4 0 0 50 0 16 11 0.0000 4 120 330 3150 1800 TCP\001 4 0 0 50 0 16 11 0.0000 4 120 330 6150 1800 TCP\001 4 0 0 50 0 16 11 0.0000 4 120 330 6150 2850 TCP\001 4 0 0 50 0 16 11 0.0000 4 120 330 3225 2850 TCP\001 libjgroups-java-2.12.2.Final.orig/doc/manual/en/images/Tunneling.png0000644000175000017500000000777111647260573025135 0ustar moellermoeller‰PNG  IHDRê½s¡$ pHYs M MÒέNtEXtSoftwareESP Ghostscript 815.02—v îIDATxœíÝÙ–‚¸Ðß^¾ÿ+{.rš•f*D ÓÞWU8ò‘Ax}>Ÿ`xÿ”Þª ªðz½^¯Wé­€[(Þ­DÈy@„< ‘DÈy@„< ‘DÈy@„< ‘DÈy@„< ‘DÈy@„< ‘D\’¯×kçßsoøû›P¿ KÎ+óóvÁ7Õ„Oé’í‡.笖œÏ¿”+Ú²Zb_¯×óEú}ß[çûðù|ò%Ó¦}ž½Ê!=¸s%êt¢<Ïþ}¬¨_“Ëê;-™v>ÿ7²Jg?ð#¸°ä8‡ ¸;jÂ']“³d›-Ì30þmׯ¾Ï26éÛU%gùV•` åÂò\ÄýE3y$Æn H;^r ~'Êócç77Ž'OÕz‰ñ×þOC(!Fu®ä@N”çR$wµÒþÌö|ùAä¹·B‘:Qr¦%ù›<±­ð—Óå9¯+ϺY« '„Ž)Þ­ðûd"ä‰< BÈ"ä‰< BÈ"ä‰< BÈ"ä‰< BÈ"ä‰< BÈ"ä‰< "â]zèÄëõ*½ …}>ŸÒ›?‘\fä QÒýEDÈy@DÄ{À~Ï‘»¹#Ƭß1Xý8à× œ0`Ũ¿€y@"ˆ$ò€y@"ˆ$ò€y@"ˆ$ò€y@âþÉpéRêC]'™žÈ¸ÆçóI‘ßcC6Ð[ò`õxx½^³c#_âÄU_ô²8­®âǵ|+­+߀ô·"M´P1^Ÿ³Ý›íÛê~ž\øEï¿ðôZ.){SC&MTŒçÁr‹Oì€0èÒNÙXž-ûâgK¦w;~µº–ü©·gV¡omÒ‘@®•ŠñÑùE³Cn¶Üñ3¦©GeªvSñHò“/Éí<4-Y®e¹%é¡é}–Ož–1½›â;z*ÆÇ“¿êùÊ<íƒîmuñφdóúw¹äøûo­åˆ_Îôó5†âMDÔ]1Þ˜«½«¦TœµâéÕ~ÙØÉ€ƒƬ×õÜF^~^&ˆº+Æ‹û‹–'SšÉ$;ecöÇŸƒç,W·¿µS]{êAP+ãõíƒåXÜòßåЊù¦#Ø* yå¸:x›¿pY<ޤÅêZbí°\Ž'ßASx4MTŒÃ5]ë<›îC¸ääý—µ×ð¹ÞŒ¦¿úƒZÜÇJÊÕcÒþú}2'íœìpœAê!øÎêtÏ"[RÃÚ¯"¨<à'*¯ IÊ’œ§ÎºƒT ÷?à  3MK-½! Dû€£F›qQƒü⥷…þÉþ¦J*H÷‘üA³ R¼C§0Ô>µ‘ O°b|Ç`kÀïøÍ‚j¹ÖÅ3†úxSŨ¿ˆ9§ŸM0ÔÌåäAŽ\:þÛŸ°|ÚÁ©_Zñ{÷ÑcåŠ&ȃ6ä‡èì8Ì—Ì.˜¾õ’UN6õK*ȃƬ™ËÛp/Ÿc´`pû©pº\Ñy0ÍØq{¬þ¦ÑãÈgÉŠ«³b|¢}°ß5lu<;\©¬ +Æòó‹Ìd¸ƒIDqzw+R1>Ñ>ð–‡I¾bP¡ˆ +ÆbýEÜÁ!ÍiRáa~ÈæõC³€ßjYù©Pyp/åŽ3©,yp#ÅN0©”ëó`6•~ø³*2ÿ7aëå@i†_ô×}ÔDÅxKû`kǶ–,6}fÝôÆC=:K…ú+Æ×/šƒ“åN6úÝ¿^/a×ú|>©Òèûgü5TŒÆ.# à>µêtKüÙá•’põ¡G”QxFÓê¯o?8ýÚVÚ†MhW£ê¯‹Ýÿ ï,[í8+±Qß1Z¥ô:¨P¶b,9~°³çË¢*-ž›@ºT(X1võ9ñûým‚Î 4äž)Þ×®e´¦Ú_ó‹¾ Ö†j5=Ô\ yp”rõkt¨¹òàoŠ4¤ËA…gȃ?h@‹¤Â ò`“’­“ _‘ë4  Rá y0§Ð@—L@ú“<øO(+Ð7v¼£‘‹CÜMA̺¶Žý+Æw V î\d¨Ï—§Btt–sÒçPìzv5hñ"zÀMÔ㎬ÞÎÙàýƃæA~ùÀ²[´z_a #æIDÀÌì¶õ·¤ Ç„°cØ*bÄ<`I!Häò€Dqâ÷«3sÓ5¡fx·Æâùòå;/ŸÏÖ·0{tõSõI2Q®ÒGÅøuäeåȦl=mk¹BvÄη/™þ^-d  ]¥Šñöß'/Ãp9¿˜}¤>^Τ ürcu׫xàÔû… ×¢´ë™+ó?‘_%áÕñê¬[{nóÀ%º)H['…;•Ãåj«£ÂöÁ“¦[#M:»îéj·/|«ƒ‚´:ÞM¼]å–¹µÃ^"xRÙøJ©‚tÚ¹é°wïZ[ãp7úñtà¦lèà®L­ŸNþhðÝßwSñ¾õ÷£}¡i‡?8aµÝ0T¹"Æ™ýQ<8¯õÁ¨™ŸC?O\ ÝÁ¨‡(N\L6ÀA 6òàF²r ròà!²ÍfL*í•“le4mç²ÌáÔ§ò °ü€1•¶èÿéÌ;œ¢VÃV*7T X1¾ûþFe°J ¹qö4§¿¨v²' „6‰Ë J‘üßÁIJ—ŸAç;{½^³kŸMµólÉÖ–Ïšöe¹–­;›Oa°|ò}I¹º;ð0yÀÜŸJ¿_®r«ÌOÒÓ¿Ë•Yïê•g–k9â«'omÆVJ­> ¥Èöì\šû—TXm‹l=ºš §7û¸_.g¶úYMuú§ôÐŒÕZlêá9òò­ZröÇŸƒç,W·¿µS}µÒÕäó_Çß ž¤}ÀÎEBÞ_¿.ÎÿÍ_¸:¤±õÐl];I“¿Û¹>¢—Ô9m fÓ*tp©RU^%Um%›Q§Š÷ ô!h³N¸Š< BÈ"ä‰< BÈ"ä‰< BÈ"ä‰< BÈ"ä‰û's™÷ê!¸†û”Aëô!Häò€D!Häò€Dñò³RBû€ä`¥?ÀmýuIEND®B`‚‰PNG  IHDRê½s¡$ pHYs M MÒέNtEXtSoftwareESP Ghostscript 815.02—vIDATxœíÕ± €@İÀþ;?Å öérsà÷î·ø? ü€ñÊ? ü€ñÊ? ü€ñÊ? ü€ñÊ? ü€ñÊ? ü€ñÊ? ü€ñÊ? ü€ñÊ? ü€ñÊ? ü€ñÊ? ü€ñÊ? ü€ñÊ? ü€ñÊ? ü€ñÊ? ü€ñÊ? ü€ñÊ? ü€ñÊ? ü€ñÊ? ü€ñÊ? ü€ñÊ? ü€ñÊ? ü€ñÊ? ü€ñÊ? ü€ñÊ? ü€ñÊ? ü€ñÊ? ü€ñÊ? ü€ñÊ? ü€ñÊ? ü€ñÊ? ü€ñÊ? ü€ñÊ? ü€ñÊ? ü€ñÊ? ü€ñÊ? ü€ñÊ? ü€ñÊ? ü€ñÊ? ü€ñÊ? ü€ñÊ? ü€ñÊ? ü€ñÊ? ü€ñÊ? ü€ñÊ? ü€ñÊ? ü€ñÊ? ü€ñÊ? ü€ñÊ? ü€ñÊ? ü€ñÊ? ü€ñÊ? ü€ñÊ? ü€ñÊ? ü€ñÊ? ü€ñÊ? ü€ñÊ? ü€ñÊ? ü€ñʘ¿ÀуcÚ`IEND®B`‚libjgroups-java-2.12.2.Final.orig/doc/manual/en/master.xml0000644000175000017500000002147611647260573023232 0ustar moellermoeller ]> Reliable Multicasting with the JGroups Toolkit Jan 2011 Bela Ban JGroups Project
belaban@yahoo.com
1998-2006 Bela Ban 2006-2011 Red Hat Inc This document is licensed under the Creative Commons Attribution-ShareAlike (CC-BY-SA) 3.0
Foreword This is the JGroups manual. It provides information about: Installation and configuration Using JGroups (the API) Configuration of the JGroups protocols The focus is on how to use JGroups, not on how JGroups is implemented. 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 manual 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 as 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, and 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, 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 Java ... 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 manual to Jeannette and Michelle. &overview; &installation; &api; &blocks; &advanced; &writing; &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 .
libjgroups-java-2.12.2.Final.orig/doc/manual/en/modules/0000755000175000017500000000000011647260573022653 5ustar moellermoellerlibjgroups-java-2.12.2.Final.orig/doc/manual/en/modules/advanced.xml0000644000175000017500000027301211647260573025147 0ustar moellermoeller 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.
The shared transport: sharing a transport between multiple channels in a JVM To save resources (threads, sockets and CPU cycles), transports of channels residing within the same JVM can be shared. If we have 4 channels inside of a JVM (as is the case in an application server such as JBoss), then we have 4 separate thread pools and sockets (1 per transport, and there are 4 transports (1 per channel)). If those transport happen to be the same (all 4 channels use UDP, for example), then we can share them and only create 1 instance of UDP. That transport instance is created and started only once, when the first channel is created, and is deleted when the last channel is closed. Each channel created over a shared transport has to join a different cluster. An exception will be thrown if a channel sharing a transport tries to connect to a cluster to which another channel over the same transport is already connected. When we have 3 channels (C1 connected to "cluster-1", C2 connected to "cluster-2" and C3 connected to "cluster-3") sending messages over the same shared transport, the cluster name with which the channel connected is used to multiplex messages over the shared transport: a header with the cluster name ("cluster-1") is added when C1 sends a message. When a message with a header of "cluster-1" is received by the shared transport, it is used to demultiplex the message and dispatch it to the right channel (C1 in this example) for processing. How channels can share a single transport is shown in .
A shared transport
Here we see 4 channels which share 2 transports. Note that first 3 channels which share transport "tp_one" have the same protocols on top of the shared transport. This is not required; the protocols above "tp_one" could be different for each of the 3 channels as long as all applications residing on the same shared transport have the same requirements for the transport's configuration. To use shared transports, all we need to do is to add a property "singleton_name" to the transport configuration. All channels with the same singleton name will be shared.
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" 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. JDBC_PING: using a shared database via JDBC or DataSource. 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 or TCP as bottommost layer. Recommended discovery protocol is PING, starting with 2.8 release, you do not have to specify any gossip routers in PING. <TUNNEL gossip_router_hosts="127.0.0.1[12001]" /> <PING /> TCPGOSSIP uses the GossipRouter (outside the firewall) at port 12001 to register its address (periodically) and to retrieve the initial membership for its group. It is not recommended to use TCPGOSSIP for discovery if TUNNEL is already used. TCPGOSSIP might be used in rare scenarios when registration and initial member discovery has to be done through gossip router indepedent of transport protocol being used. Starting with 2.8 release TCPGOSSIP accepts one or multiple router hosts as a comma delimited list of host[port] elements specified in a property initial_hosts. 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. Starting with 2.8 release TUNNEL transport layer accepts one or multiple router hosts as a comma delimited list of host[port] elements specified in a property gossip_router_hosts. 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. . Starting with 2.8 release, gossip router is no longer a single point of failure. In a set-up with multiple gossip routers, routers do not communicate among themselves, and single point of failure is avoided by having each channel simply connect to multiple available routers. In case one or more routers go down, cluster members are still able to exchange message through remaining available router instances, if there are any. For each send invocation, a channel goes through a list of available connections to routers and attempts to send a message on each connection until it succeeds. If a message could not be sent on any of the connections – an exception is raised. Default policy for connection selection is random. However, we also provide an plug-in interface for other policies as well. Gossip router configuration is static and is not updated for the lifetime of the channel. A list of available routers has to be provided in channel configuration file. 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, 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, second, 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.
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 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 "cl" letters, where "c" includes the cluster name and "l" includes local address of the channel. The default is "cl"
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.
Scopes: concurrent message delivery for messages from the same sender In the previous paragraph, we showed how the concurrent stack delivers messages from different senders concurrently. But all (non-OOB) messages from the same sender P are delivered in the order in which P sent them. However, this is not good enough for certain types of applications. Consider the case of an application which replicates HTTP sessions. If we have sessions X, Y and Z, then updates to these sessions are delivered in the order in which there were performed, e.g. X1, X2, X3, Y1, Z1, Z2, Z3, Y2, Y3, X4. This means that update Y1 has to wait until updates X1-3 have been delivered. If these updates take some time, e.g. spent in lock acquisition or deserialization, then all subsequent messages are delayed by the sum of the times taken by the messages ahead of them in the delivery order. However, in most cases, updates to different web sessions should be completely unrelated, so they could be delivered concurrently. For instance, a modification to session X should not have any effect on session Y, therefore updates to X, Y and Z can be delivered concurrently. One solution to this is out-of-band (OOB) messages (see next paragraph). However, OOB messages do not guarantee ordering, so updates X1-3 could be delivered as X1, X3, X2. If this is not wanted, but messages pertaining to a given web session should all be delivered concurrently between sessions, but ordered within a given session, then we can resort to scoped messages. Scoped messages apply only to regular (non-OOB) messages, and are delivered concurrently between scopes, but ordered within a given scope. For example, if we used the sessions above (e.g. the jsessionid) as scopes, then the delivery could be as follows ('->' means sequential, '||' means concurrent): X1 -> X2 -> X3 -> X4 || Y1 -> Y2 -> Y3 || Z1 -> Z2 -> Z3 This means that all updates to X are delivered in parallel to updates to Y and updates to Z. However, within a given scope, updates are delivered in the order in which they were performed, so X1 is delivered before X2, which is deliverd before X3 and so on. Taking the above example, using scoped messages, update Y1 does not have to wait for updates X1-3 to complete, but is processed immediately. To set the scope of a message, use method Message.setScope(short). Scopes are implemented in a separate protocol called . This protocol has to be placed somewhere above ordering protocols like UNICAST or NAKACK (or SEQUENCER for that matter). Uniqueness of scopes Note that scopes should be as unique as possible. Compare this to hashing: the fewer collisions there are, the better the concurrency will be. So, if for example, two web sessions pick the same scope, then updates to those sessions will be delivered in the order in which they were sent, and not concurrently. While this doesn't cause erraneous behavior, it defies the purpose of SCOPE. Also note that, if multicast and unicast messages have the same scope, they will be delivered in sequence. So if A multicasts messages to the group with scope 25, and A also unicasts messages to B with scope 25, then A's multicasts and unicasts will be delivered in order at B ! Again, this is correct, but since multicasts and unicasts are unrelated, might slow down things !
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.
Replacing the default and OOB thread pools In 2.7, there are 3 thread pools and 4 thread factories in TP: Thread pools and factories in TP Name Description Default thread pool This is the pools for handling incoming messages. It can be fetched using getDefaultThreadPool() and replaced using setDefaultThreadPool(). When setting a thread pool, the old thread pool (if any) will be shutdown and all of it tasks cancelled first OOB thread pool This is the pool for handling incoming OOB messages. Methods to get and set it are getOOBThreadPool() and setOOBThreadPool() Timer thread pool This is the thread pool for the timer. The max number of threads is set through the timer.num_threads property. The timer thread pool cannot be set, it can only be retrieved using getTimer(). However, the thread factory of the timer can be replaced (see below) Default thread factory This is the thread factory (org.jgroups.util.ThreadFactory) of the default thread pool, which handles incoming messages. A thread pool factory is used to name threads and possibly make them daemons. It can be accessed using getDefaultThreadPoolThreadFactory() and setDefaultThreadPoolThreadFactory() OOB thread factory This is the thread factory for the OOB thread pool. It can be retrieved using getOOBThreadPoolThreadFactory() and set using method setOOBThreadPoolThreadFactory() Timer thread factory This is the thread factory for the timer thread pool. It can be accessed using getTimerThreadFactory() and setTimerThreadFactory() Global thread factory The global thread factory can get used (e.g. by protocols) to create threads which don't live in the transport, e.g. the FD_SOCK server socket handler thread. Each protocol has a method getTransport(). Once the TP is obtained, getThreadFactory() can be called to get the global thread factory. The global thread factory can be replaced with setThreadFactory()
Sharing of thread pools between channels in the same JVM In 2.7, the default and OOB thread pools can be shared between instances running inside the same JVM. The advantage here is that multiple channels running within the same JVM can pool (and therefore save) threads. The disadvantage is that thread naming will not show to which channel instance an incoming thread belongs to. Note that we can not just shared thread pools between JChannels within the same JVM, but we can also share entire transports. For details see this section.
Misc
Shunning Note that in 2.8, shunning has been removed, so the sections below only apply to versions up to 2.7. 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"/>
Using a custom socket factory JGroups creates all of its sockets through a SocketFactory, which is located in the transport (TP) or TP.ProtocolAdapter (in a shared transport). The factory has methods to create sockets (Socket, ServerSocket, DatagramSocket and MulticastSocket) Currently, SocketFactory does not support creation of NIO sockets / channels. , closen sockets and list all open sockets. Every socket creation method has a service name, which could be for example "jgroups.fd_sock.srv_sock". The service name is used to look up a port (e.g. in a config file) and create the correct socket. To provide one's own socket factory, the following has to be done: if we have a non-shared transport, the code below creates a SocketFactory implementation and sets it in the transport: JChannel ch; MySocketFactory factory; // e.g. extends DefaultSocketFactory ch=new JChannel("config.xml"); ch.setSocketFactory(new MySocketFactory()); ch.connect("demo"); If a shared transport is used, then we have to set 2 socket factories: 1 in the shared transport and one in the TP.ProtocolAdapter: JChannel c1=new JChannel("config.xml"), c2=new JChannel("config.xml"); TP transport=c1.getProtocolStack().getTransport(); transport.setSocketFactory(new MySocketFactory("transport")); c1.setSocketFactory(new MySocketFactory("first-cluster")); c2.setSocketFactory(new MySocketFactory("second-cluster")); c1.connect("first-cluster"); c2.connect("second-cluster"); First, we grab one of the channels to fetch the transport and set a SocketFactory in it. Then we set one SocketFactory per channel that resides on the shared transport. When JChannel.connect() is called, the SocketFactory will be set in TP.ProtocolAdapter.
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); // requires separate thread as we don't want to block JGroups handler.start(); } } 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("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("Not 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 coord 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.
Large clusters This section is a collection of best practices and tips and tricks for running large clusters on JGroups. By large clusters, we mean several hundred nodes in a cluster.
Reducing chattiness When we have a chatty protocol, scaling to a large number of nodes might be a problem: too many messages are sent and - because they are generated in addition to the regular traffic - this can have a negative impact on the cluster. A possible impact is that more of the regular messages are dropped, and have to be retransmitted, which impacts performance. Or heartbeats are dropped, leading to false suspicions. So while the negative effects of chatty protocols may not be seen in small clusters, they will be seen in large clusters !
Discovery A discovery protocol (e.g. PING, TCPPING, MPING etc) is run at startup, to discover the initial membership, and periodically by the merge protocol, to detect partitioned subclusters. When we send a multicast discovery request to a large cluster, every node in the cluster might possibly reply with a discovery response sent back to the sender. So, in a cluster of 300 nodes, the discovery requester might be up to 299 discovery responses ! Even worse, because num_ping_requests in Discovery is by default set to 2, so we're sending 2 discovery requests, we might receive up to num_ping_requests * (N-1) discovery responses, even though we might be able to find out the coordinator after a few responses already ! To reduce the large number of responses, we can set a max_rank property: the value defines which members are going to send a discovery response. The rank is the index of a member in a cluster: in {A,B,C,D,E}, A's index is 1, B's index is 2 and so on. A max_rank of 3 would trigger discovery responses from only A, B and C, but not from D or E. We highly recommend setting max_rank in large clusters. This functionality was implemented in https://jira.jboss.org/browse/JGRP-1181.
Failure detection protocols Failure detection protocols determine when a member is unresponsive, and subsequently suspect it. Usually (FD, FD_ALL), messages (heartbeats) are used to determine the health of a member, but we can also use TCP connections (FD_SOCK) to connect to a member P, and suspect P when the connection is closed. Heartbeating requires messages to be sent around, and we need to be careful to limit the number of messages sent by a failure detection protocol (1) to detect crashed members and (2) when a member has been suspected. The following sections discuss how to configure FD_ALL and FD_SOCK, the most commonly used failure detection protocols, for use in large clusters.
FD_SOCK
FD_ALL
Bridging between remote clusters In 2.12, the RELAY protocol was added to JGroups (for the properties see RELAY). It allows for bridging of remote clusters. For example, if we have a cluster in New York (NYC) and another one in San Francisco (SFO), then RELAY allows us to bridge NYC and SFO, so that multicast messages sent in NYC will be forwarded to SFO and vice versa. The NYC and SFO clusters could for example use IP multicasting (UDP as transport), and the bridge could use TCP as transport. The SFO and NYC clusters don't even need to use the same cluster name. shows how the two clusters are bridged.
Relaying between different clusters
The cluster on the left side with nodes A (the coordinator), B and C is called "NYC" and use IP multicasting (UDP as transport). The cluster on the right side ("SFO") has nodes D (coordinator), E and F. The bridge between the local clusters NYC and SFO is essentially another cluster with the coordinators (A and D) of the local clusters as members. The bridge typically uses TCP as transport, but any of the supported JGroups transports could be used (including UDP, if supported across a WAN, for instance). Only a coordinator relays traffic between the local and remote cluster. When A crashes or leaves, then the next-in-line (B) takes over and starts relaying. Relaying is done via the RELAY protocol added to the top of the stack. The bridge is configured with the bridge_props property, e.g. bridge_props="/home/bela/tcp.xml". This creates a JChannel inside RELAY. Note that property "site" must be set in both subclusters. In the example above, we could set site="nyc" for the NYC subcluster and site="sfo" for the SFO ubcluster. The design is described in detail in JGroups/doc/design/RELAY.txt (part of the source distribution). In a nutshell, multicast messages received in a local cluster are wrapped and forwarded to the remote cluster by a relay (= the coordinator of a local cluster). When a remote cluster receives such a message, it is unwrapped and put onto the local cluster. JGroups uses subclasses of UUID (PayloadUUID) to ship the site name with an address. When we see an address with site="nyc" on the SFO side, then RELAY will forward the message to the SFO subcluster, and vice versa. When C multicasts a message in the NYC cluster, A will forward it to D, which will re-broadcast the message on its local cluster, with the sender being D. This means that the sender of the local broadcast will appear as D (so all retransmit requests got to D), but the original sender C is preserved in the header. At the RELAY protocol, the sender will be replaced with the original sender (C) having site="nyc". When node F wants to reply to the sender of the multicast, the destination of the message will be C, which is intercepted by the RELAY protocol and forwarded to the current relay (D). D then picks the correct destination (C) and sends the message to the remote cluster, where A makes sure C (the original sender) receives it. An important design goal of RELAY is to be able to have completely autonomous clusters, so NYC doesn't for example have to block waiting for credits from SFO, or a node in the SFO cluster doesn't have to ask a node in NYC for retransmission of a missing message.
Views RELAY presents a global view to the application, e.g. a view received by nodes could be {D,E,F,A,B,C}. This view is the same on all nodes, and a global view is generated by taking the two local views, e.g. A|5 {A,B,C} and D|2 {D,E,F}, comparing the coordinators' addresses (the UUIDs for A and D) and concatenating the views into a list. So if D's UUID is greater than A's UUID, we first add D's members into the global view ({D,E,F}), and then A's members. Therefore, we'll always see all of A's members, followed by all of D's members, or the other way round. To see which nodes are local and which ones remote, we can iterate through the addresses (PayloadUUID) and use the site (PayloadUUID.getPayload()) name to for example differentiate between "nyc" and "sfo".
Configuration To setup a relay, we need essentially 3 XML configuration files: 2 to configure the local clusters and 1 for the bridge. To configure the first local cluster, we can copy udp.xml from the JGroups distribution and add RELAY on top of it: <RELAY bridge_props="/home/bela/tcp.xml" />. Let's say we call this config relay.xml. The second local cluster can be configured by copying relay.xml to relay2.xml. Then change the mcast_addr and/or mcast_port, so we actually have 2 different cluster in case we run instances of both clusters in the same network. Of course, if the nodes of one cluster are run in a different network from the nodes of the other cluster, and they cannot talk to each other, then we can simply use the same configuration. The 'site' property needs to be configured in relay.xml and relay2.xml, and it has to be different. For example, relay.xml could use site="nyc" and relay2.xml could use site="sfo". The bridge is configured by taking the stock tcp.xml and making sure both local clusters can see each other through TCP.
Daisychaining Daisychaining refers to a way of disseminating messages sent to the entire cluster. The idea behind it is that it is inefficient to broadcast a message in clusters where IP multicasting is not available. For example, if we only have TCP available (as is the case in most clouds today), then we have to send a broadcast (or group) message N-1 times. If we want to broadcast M to a cluster of 10, we send the same message 9 times. Example: if we have {A,B,C,D,E,F}, and A broadcasts M, then it sends it to B, then to C, then to D etc. If we have a 1 GB switch, and M is 1GB, then sending a broadcast to 9 members takes 9 seconds, even if we parallelize the sending of M. This is due to the fact that the link to the switch only sustains 1GB / sec. (Note that I'm conveniently ignoring the fact that the switch will start dropping packets if it is overloaded, causing TCP to retransmit, slowing things down)... Let's introduce the concept of a round. A round is the time it takes to send or receive a message. In the above example, a round takes 1 second if we send 1 GB messages. In the existing N-1 approach, it takes X * (N-1) rounds to send X messages to a cluster of N nodes. So to broadcast 10 messages a the cluster of 10, it takes 90 rounds. Enter DAISYCHAIN. The idea is that, instead of sending a message to N-1 members, we only send it to our neighbor, which forwards it to its neighbor, and so on. For example, in {A,B,C,D,E}, D would broadcast a message by forwarding it to E, E forwards it to A, A to B, B to C and C to D. We use a time-to-live field, which gets decremented on every forward, and a message gets discarded when the time-to-live is 0. The advantage is that, instead of taxing the link between a member and the switch to send N-1 messages, we distribute the traffic more evenly across the links between the nodes and the switch. Let's take a look at an example, where A broadcasts messages m1 and m2 in cluster {A,B,C,D}, '-->' means sending:
Traditional N-1 approach Round 1: A(m1) --> B Round 2: A(m1) --> C Round 3: A(m1) --> D Round 4: A(m2) --> B Round 5: A(m2) --> C Round 6: A(m2) --> D It takes 6 rounds to broadcast m1 and m2 to the cluster.
Daisychaining approach Round 1: A(m1) --> B Round 2: A(m2) --> B || B(m1) --> C Round 3: B(m2) --> C || C(m1) --> D Round 4: C(m2) --> D In round 1, A send m1 to B. In round 2, A sends m2 to B, but B also forwards m1 (received in round 1) to C. In round 3, A is done. B forwards m2 to C and C forwards m1 to D (in parallel, denoted by '||'). In round 4, C forwards m2 to D.
Switch usage Let's take a look at this in terms of switch usage: in the N-1 approach, A can only send 125MB/sec, no matter how many members there are in the cluster, so it is constrained by the link capacity to the switch. (Note that A can also receive 125MB/sec in parallel with today's full duplex links). So the link between A and the switch gets hot. In the daisychaining approach, link usage is more even: if we look for example at round 2, A sending to B and B sending to C uses 2 different links, so there are no constraints regarding capacity of a link. The same goes for B sending to C and C sending to D. In terms of rounds, the daisy chaining approach uses X + (N-2) rounds, so for a cluster size of 10 and broadcasting 10 messages, it requires only 18 rounds, compared to 90 for the N-1 approach !
Performance To measure performance of DAISYCHAIN, a performance test (test.Perf) was run, with 4 nodes connected to a 1 GB switch; and every node sending 1 million 8K messages, for a total of 32GB received by every node. The config used was tcp.xml. The N-1 approach yielded a throughput of 73 MB/node/sec, and the daisy chaining approach 107MB/node/sec !
Configuration DAISYCHAIN can be placed directly on top of the transport, regardless of whether it is UDP or TCP, e.g. <TCP .../> <DAISYCHAIN .../> <TCPPING .../>
Ergonomics Ergonomics is similar to the dynamic setting of optimal values for the JVM, e.g. garbage collection, memory sizes etc. In JGroups, ergonomics means that we try to dynamically determine and set optimal values for protocol properties. Examples are thread pool size, flow control credits, heartbeat frequency and so on.
libjgroups-java-2.12.2.Final.orig/doc/manual/en/modules/api.xml0000644000175000017500000021275011647260573024155 0ustar moellermoeller 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 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 unserialized.
Interfaces These interfaces are used with some of the APIs presented below, therefore they are listed first.
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 introduced 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); }
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. Note that block() should be brief, or else the entire FLUSH protocol is blocked. Sending messages in callbacks Note that anything that could block should not be done in a callback. This includes sending of messages; if we have FLUSH on the stack, and send a message in a viewAccepted() callback, then the following happens: the FLUSH protocol blocks all (multicast) messages before installing a view, then installs the view, then unblocks. However, because installation of the view triggers the viewAccepted() callback, sending of messages inside of viewAccepted() will block. This in turn blocks the viewAccepted() thread, so the flush will never return ! If we need to send a message in a callback, the sending should be done on a separate thread, or a timer task should be submitted to the timer.
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(); // deprecated in 2.8 void channelReconnected(Address addr); // deprecated in 2.8 } 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 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 Note that JChannel.receive() has been deprecated and will be removed in 3.0. The preferred way of receiving messages is now via a Receiver callback (push style).
ExtendedReceiver public interface ExtendedReceiver extends ExtendedMessageListener, MembershipListener { } This is a receiver who will be able to handle partial state transfer
ReceiverAdapter and ExtendedReceiverAdapter These classes implement Receiver and ExtendedReceiver. When implementing a callback, one can simply extend ReceiverAdapter and overwrite receive() in order to not having to implement all callbacks of the interface.
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 size(); } Please never use implementations of Address directly; Address should always be used as an opaque identifier of a cluster node ! 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). In 2.8, the default implementation of Address was changed from IpAddress to org.jgroups.util.UUID.
Message Data is sent between members in the form of messages ( org.jgroups.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"); 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 view=channel.getView(); Address first=view.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 Receiver.viewAccepted(), 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.Note that the latter 2 methods only take the ID into account.
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) .
JChannel 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. The public constructor of JChannel looks as follows: public JChannel(String props) throws ChannelException {} It creates an instance of JChannel . The props argument points to an XML file containing the configuration of the protocol stack to be used. This can be a String, but there are also other constructors which take for example a DOM element or a URL (more on this later). If the props 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. For example, the Draw demo can be launched as follows: java org.javagroups.demos.Draw -props file:/home/bela/udp.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" 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" /> <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" 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.
Programmatic creation Usually, channels are created by passing the name of an XML configuration file to the JChannel() constructor. On top of this declarative configuration, JGroups provides an API to create a channel programmatically. The way to do this is to first create a JChannel, then an instance of ProtocolStack, then add all desired protocols to the stack and finally calling init() on the stack to set it up. The rest, e.g. calling JChannel.connect() is the same as with the declarative creation. An example of how to programmatically create a channel is shown below (copied from ProgrammaticChat): JChannel ch=new JChannel(false); // 1 ProtocolStack stack=new ProtocolStack(); // 2 ch.setProtocolStack(stack); // 3 stack.addProtocol(new UDP().setValue("bind_addr", InetAddress.getByName("192.168.1.5"))) .addProtocol(new PING()) .addProtocol(new MERGE2()) .addProtocol(new FD_SOCK()) .addProtocol(new FD_ALL().setValue("timeout", 12000).setValue("interval", 3000)) .addProtocol(new VERIFY_SUSPECT()) .addProtocol(new BARRIER()) .addProtocol(new NAKACK()) .addProtocol(new UNICAST2()) .addProtocol(new STABLE()) .addProtocol(new GMS()) .addProtocol(new UFC()) .addProtocol(new MFC()) .addProtocol(new FRAG2()); // 4 stack.init(); // 5 ch.setReceiver(new ReceiverAdapter() { public void viewAccepted(View new_view) { System.out.println("view: " + new_view); } public void receive(Message msg) { System.out.println(msg.getObject() + " [" + msg.getSrc() + "]"); } }); ch.connect("ChatCluster"); for(;;) { String line=Util.readStringFromStdin(": "); ch.send(null, null, line); } First a JChannel is created. The 'false' argument tells the channel not to create a ProtocolStack. This is needed because we will create one ourselves later (2) and set it in the channel (3). Next, all protocols are added to the stack. Note that the order is from bottom (transport protocol) to top. So UDP as transport is added first, then PING and so on, until FRAG2, which is the top protocol. Every protocol can be configured via setters, but there is also a generic setValue(String attr_name, Object value), which can be used to configure protocols as well, as shown in the example. Once the stack is configured, we call ProtocolStack.init() to link all protocols correctly and to call init() in every protocol instance. After this, the channel is ready to be used and all subsequent actions (e.g. connect()) can be executed. When the init() method returns, we have essentially the equivalent of new JChannel(config_file).
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. 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. Note that in 2.8, shunning has been removed, therefore this option has been deprecated. 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. Note that in 2.8, shunning has been removed, therefore this option has been deprecated. The equivalent method to get options is getOpt(): public Object getOpt(int option); Given an option, the current value of the option is returned. Deprecating options in 3.0 Most of the options (except LOCAL) have been deprecated in 2.6.x and will be removed in 3.0.
Giving the channel a logical name A channel can be given a logical name which is then used instead of the channel's address. A logical name might show the function of a channel, e.g. "HostA-HTTP-Cluster", which is more legible than a UUID 3c7e52ea-4087-1859-e0a9-77a0d2f69f29. For example, when we have 3 channels, using logical names we might see a view "{A,B,C}", which is nicer than "{56f3f99e-2fc0-8282-9eb0-866f542ae437, ee0be4af-0b45-8ed6-3f6e-92548bfa5cde, 9241a071-10ce-a931-f675-ff2e3240e1ad} !" If no logical name is set, JGroups generates one, using the hostname and a random number, e.g. linux-3442. If this is not desired and the UUIDs should be shown, use system property -Djgroups.print_uuids=true. The logical name can be set using: public void setName(String logical_name); This should be done before connecting a channel. Note that the logical name stays with a channel until the channel is destroyed, whereas a UUID is created on each connection. When JGroups starts, it prints the logical name and the associated physical address(es): ------------------------------------------------------------------- GMS: address=mac-53465, cluster=DrawGroupDemo, physical address=192.168.1.3:49932 ------------------------------------------------------------------- ** View=[mac-53465|0] [mac-53465] The logical name is mac-53465 and the physical address is 192.168.1.3:49932. The UUID is not shown here.
Generating custom addresses Since 2.12 address generation is pluggable. This means that an application can determine what kind of addresses it uses. The default address type is UUID, and since some protocols use UUID, it is recommended to provide custom classes as subclasses of UUID. This can be used to for example pass additional data around with an address, for example information about the location of the node to which the address is assigned. Note that methods equals(), hashCode() and compare() of the UUID super class should not be changed. To use custom addresses, the following things have to be done: Write an implementation of org.jgroups.stack.AddressGenerator For any class CustomAddress, it will need to get registered with the ClassConfigurator in order to marshal it correctly: class CustomAddress extends UUID { static { ClassConfigurator.add((short)8900, CustomAddress.class); } } Note that the ID should be chosen such that it doesn't collide with any IDs defined in jg-magic-map.xml. Set the address generator in JChannel: setAddressGenerator(AddressGenerator). This has to be done before the channel is connected An example of a subclass is org.jgroups.util.PayloadUUID.
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 member 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 Since 2.8 the method is getAddress() . 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(); // use getAddress() with 2.8.0+ Method getClusterName() 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: Map 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 hashmap, 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: Map data; try { Address 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=channel.receive(0); // wait forever if(obj instanceof Message) Message msg=(Message)obj; else if(obj instanceof View) 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 try { String s=(String)msg.getObject(); // error if obj 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. This is the preferred and recommended way of receiving messages. In 3.0, the receive() method will be removed from JChannel. 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) { // make a copy so that other msgs don't change the state copy=copyState(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() { // so nobody else can modify the map while we serialize it synchronized(m) { 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); } } } // use default props (has to include STATE_TRANSFER) channel=new JChannel(); 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) { // so nobody can modify the map while we serialize it synchronized(m) { 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); } } } } // use default props (has to include pbcast.STATE_TRANSFER) channel=new JChannel(); 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.
libjgroups-java-2.12.2.Final.orig/doc/manual/en/modules/blocks.xml0000644000175000017500000011470711647260573024664 0ustar moellermoellerBuilding 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 implements 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 Note that this building block has been deprecated and should not be used anymore ! Use a Receiver instead. 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 implements Map<Address,Rsp> { 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(); } 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.
RequestOptions RequestOptions is a collection of options that can be passed into a call, e.g. the mode (GET_ALL, GET_NONE), timeout, flags etc. It is an alternative to passing multiple arguments to a method. All calls with individual parameters have been deprecated in 2.9 and the new calls with RequestOptions are: public RspList callRemoteMethods(Collection<Address> dests, String method_name, Object[] args,Class[] types, RequestOptions options); public RspList callRemoteMethods(Collection<Address> dests, MethodCall method_call, RequestOptions options); public Object callRemoteMethod(Address dest, String method_name, Object[] args, Class[] types, RequestOptions options); public Object callRemoteMethod(Address dest, MethodCall call, RequestOptions options); An example of how to use RequestOptions is: RpcDispatcher disp; RequestOptions opts=new RequestOptions(Request.GET_ALL) .setFlags(Message.NO_FC | Message.DONT_BUNDLE); Object val=disp.callRemoteMethod(target, method_call, opts);
Asynchronous calls with futures When invoking a synchronous call, the calling thread is blocked until the response (or responses) has been received. A Future allows a caller to return immediately and grab the result(s) later. In 2.9, two new methods, which return futures, have been added to RpcDispatcher: public NotifyingFuture<RspList> callRemoteMethodsWithFuture(Collection<Address> dests, MethodCall method_call, RequestOptions options); public <T> NotifyingFuture<T> callRemoteMethodWithFuture(Address dest, MethodCall call, RequestOptions options); A NotifyingFuture extends java.util.concurrent.Future, with its regular methods such as isDone(), get() and cancel(). NotifyingFuture adds setListener<FutureListener> to get notified when the result is available. This is shown in the following code: NotifyingFuture<RspList> future=dispatcher.callRemoteMethodsWithFuture(...); future.setListener(new FutureListener() { void futureDone(Future<T> future) { System.out.println("result is " + future.get()); } } );
ReplicatedHashMap This class was written as a demo of how state can be shared between nodes of a cluster. It has never been heavily tested and is therefore not meant to be used in production, and unsupported. A ReplicatedHashMap uses a concurrent hashmap internally and allows to create several instances of hashmaps 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 replicated hashmaps 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, 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 ReplicatedHashMap 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). ReplicatedHashMap allow members in a group to share common state across process and machine boundaries.
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.
Distributed locking In 2.12, a new distributed locking service was added, replacing DistributedLockManager. The new service is implemented as a protocol and is used via org.jgroups.blocks.locking.LockService. LockService talks to the locking protocol via events. The main abstraction of a distributed lock is an implementation of java.util.concurrent.locks.Lock. All lock methods are supported, however, conditions are not yet supported. (Based on feedback, they might be added later). Below is an example of how LockService is typically used: // locking.xml needs to have a locking protocol JChannel ch=new JChannel("/home/bela/locking.xml"); LockService lock_service=new LockService(ch); ch.connect("lock-cluster"); Lock lock=lock_service.getLock("mylock"); lock.lock(); try { // do something with the locked resource } finally { lock.unlock(); } In the example, we create a channel, then a LockService, then connect the channel. Then we grab a lock named "mylock", which we lock and subsequently unlock. Note that the owner of a lock is always a given thread in a cluster, so the owner is the JGroups address and the thread ID. This means that different threads inside the same JVM trying to access the same named lock will compete for it. If thread-22 grabs the lock first, then thread-5 will block until thread-23 releases the lock. JGroups includes a demo (org.jgroups.demos.LockServiceDemo), which can be used to interactively experiment with distributed locks. LockServiceDemo -h dumps all command line options. Currently (Jan 2011), there are 2 protocols which provide locking: PEER_LOCK and CENTRAL_LOCK. The locking protocol has to be placed at or towards the top of the stack (close to the channel).
Locking and merges The following scenario is susceptible to merging: we have a cluster view of {A,B,C,D} and then the cluster splits into {A,B} and {C,D}. Assume that B and D now acquire a lock "mylock". This is what happens (with the locking protocol being CENTRAL_LOCK): There are 2 coordinators: A for {A,B} and C for {C,D} B successfully acquires "mylock" from A D successfully acquires "mylock" from C The partitions merge back into {A,B,C,D}. Now, only A is the coordinator, but C ceases to be a coordinator Problem: D still holds a lock which should actually be invalid ! There is no easy way (via the Lock API) to 'remove' the lock from D. We could for example simply release D's lock on "mylock", but then there's no way telling D that the lock it holds is actually stale ! Therefore the recommended solution here is for nodes to listen to MergeView changes if they expect merging to occur, and re-acquire all of their locks after a merge, e.g.: Lock l1, l2, l3; LockService lock_service; ... public void viewAccepted(View view) { if(view instanceof MergeView) { new Thread() { public void run() { lock_service.unlockAll(); // stop all access to resources protected by l1, l2 or l3 // every thread needs to re-acquire the locks it holds } }.start } }
Distributed ExecutionService In 2.12, a distributed execution service was added. The new service is implemented as a protocol and is used via org.jgroups.blocks.executor.ExecutionService. ExecutionService talks to the executing protocol via events. The main abstraction is an implementation of java.util.concurrent.locks.ExecutorService. All methods are supported. The restrictions are however that the Callable or Runnable must be Serializable, Externalizable or Streamable. Also the result produced from the future needs to be Serializable, Externalizable or Streamable. If the Callable or Runnable are not then an IllegalArgumentException is immediately thrown. If a result is not then a NotSerializableException with the name of the class will be returned to the Future as an exception cause. Below is an example of how ExecutionService is typically used: // locking.xml needs to have a locking protocol JChannel ch=new JChannel("/home/bela/executing.xml"); ExecutionService exec_service =new ExecutionService(ch); ch.connect("exec-cluster"); Future<Value> future = exec_service.submit(new MyCallable()); try { Value value = future.get(); // Do something with value } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.getCause().printStackTrace(); } In the example, we create a channel, then an ExecutionService, then connect the channel. Then we submit our callable giving us a Future. Then we wait for the future to finish returning our value and do something with it. If any exception occurs we print the stack trace of that exception. JGroups includes a demo (org.jgroups.demos.ExecutionServiceDemo), which can be used to interactively experiment with a distributed sort algorithm and performance. This is for demonstration purposes and performance should not be assumed to be better than local. ExecutionServiceDemo -h dumps all command line options. Currently (March 2011), there is 1 protocol which provide executions: CENTRAL_EXECUTOR. The executing protocol has to be placed at or towards the top of the stack (close to the channel).
libjgroups-java-2.12.2.Final.orig/doc/manual/en/modules/eventlist.xml0000644000175000017500000000012311647260573025406 0ustar moellermoellerList of events libjgroups-java-2.12.2.Final.orig/doc/manual/en/modules/installation.xml0000644000175000017500000004733311647260573026110 0ustar moellermoeller Installation and Configuration The installation refers to version 2.8 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 1.5 or higher. Version 2.9 requires JDK 1.6 or higher. There is no JNI code present so JGroups 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 log4j.jar. This JAR is optional, for example if JDK logging is used, we don't need it. Note that commons-logging is not a requirement any more since version 2.8. Place the JAR files somewhere in your CLASSPATH, and you're ready to start using JGroups.
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 xalan.jar: to format the output of the JUnit tests using an XSLT converter to HTML 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 library without unit tests and demos jgroups-all.jar - the complete JGroups library 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: [mac] /Users/bela/JGroups$ java org.jgroups.Version Version: 2.8.0.GA CVS: $Id: installation.xml,v 1.10 2010/04/30 14:27:39 vlada 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. State transfer (see the section in the API later) can also be tested by passing the -state flag to Draw.
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.1.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 /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
Supported classes JGroups project has been around since 2001. Over this time, some of the JGroups classes have been used in experimental phases and have never been matured enough to be used in today's production releases. However, they were not removed since some people used them in their products. The following tables list unsupported and experimental classes. These classes are not actively maintained, and we will not work to resolve potential issues you might find. Their final faith is not yet determined; they might even be removed altogether in the next major release. Weight your risks if you decide to use them anyway.
Experimental ${Experimental}
Unsupported ${Unsupported}
libjgroups-java-2.12.2.Final.orig/doc/manual/en/modules/overview.xml0000644000175000017500000002403511647260573025247 0ustar moellermoellerOverview 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 group is a cluster. We use these terms 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 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. 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 (transport) protocol puts it on the network. The same happens in the reverse direction: the 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 (also including itself). 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. Note that the push approach to receiving messages and views is preferred. This involves setting a Receiver in the channel and getting callbacks invoked by JGroups whenever a message or view is received. The current pull approach (JChannel.receive() method) has been deprecated in 2.8 and will be removed in 3.0. 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 programmatically. 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 all protocols. 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). The configuration 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.
libjgroups-java-2.12.2.Final.orig/doc/manual/en/modules/protocols.xml0000644000175000017500000015253211647260573025431 0ustar moellermoeller 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 ${UDP}
TCP ${TCP}
TUNNEL ${TUNNEL}
Initial membership discovery The task of the discovery is to find an initial membership, which is used to determine the current coordinator. Once a coordinator is found, the joiner sends a JOIN request to the coord.
PING ${PING}
FILE_PING This uses a shared directory into which all members write their addresses. New joiners read all addresses from this directory (which needs to be shared, e.g. via NFS or SMB) and ping each of the elements of the resulting set of members. When a member leaves, it deletes its corresponding file. FILE_PING can be used instead of GossipRouter in cases where no external process is desired. ${PING}
JDBC_PING This uses a shared Database into which all members write their addresses. New joiners read all addresses from this Database and ping each of the elements of the resulting set of members. When a member leaves, it deletes its corresponding record. JDBC_PING is an alternative to S3_PING by using Amazon RDS instead of S3. ${JDBC_PING}
TCPPING ${TCPPING}
TCPGOSSIP ${TCPGOSSIP}
MPING ${MPING}
BPING BPING uses UDP broadcasts to discover other nodes. The default broadcast address (dest) is 255.255.255.255, and should be replaced with a subnet specific broadcast, e.g. 192.168.1.255. ${BPING}
S3_PING This uses an Amazon S3 bucket into which all members write their addresses. New joiners read all addresses from this bucket and ping each of the elements of the resulting set of members. When a member leaves, it deletes its corresponding file. S3_PING is primarily meant to be used on Amazon EC2 where multicast traffic is not allowed and no external process (GossipRouter) is desired. When Amazon RDS is preferred over S3, or if a shared database is used, an alternative is to use JDBC_PING. ${S3_PING}
Merging after a network partition
MERGE2 ${MERGE2}
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. ${FD}
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 example 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}
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. ${FD_PING}
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. ${FD_SOCK}
VERIFY_SUSPECT ${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. ${NAKACK}
UNICAST UNICAST provides reliable delivery and FIFO (= First In First Out) properties for point-to-point messages between one sender and one receiver. 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 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. On top of a reliable transport, such as TCP, UNICAST is not really needed. However, concurrent delivery of messages from the same sender is prevented by UNICAST by acquiring a lock on the sender's retransmission table, so unless concurrent delivery is desired, UNICAST should not be removed from the stack even if TCP is used. ${UNICAST}
UNICAST2 UNICAST2 provides lossless, ordered, communication between 2 members. Contrary to UNICAST, it uses negative acks (similar to NAKACK) rather than positive acks. This reduces the communication overhead required for sending an ack for every message. ${UNICAST2}
Fragmentation
FRAG and FRAG2 ${FRAG}
Ordering
SEQUENCER SEQUENCER provider total order for multicast (=group) messages by forwarding messages to the current coordinator, which then sends the messages to the cluster on behalf of the original sender. Because it is always the same sender (whose messages are delivered in FIFO order), a global (or total) order is established. Sending members add every forwarded message M to a buffer and remove M when they receive it. Should the current coordinator crash, all buffered messages are forwarded to the new coordinator. Note that retransmissions go to the original sender, not to the coordinator. ${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 ${GMS}
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 ${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. Prior to 2.6.9 and 2.8 releases streaming state transfer relied exclusively on its own tcp sockets to transfer state between members. The downside of tcp socket approach is that it is not firewall friendly. If use_default_transport property of pbcast.STREAMING_STATE_TRANSFER is set to true streaming state transfer will use normal messages to transfer state. This approach besides being completely transparent to application is also firewall friendly. However, as expected, tcp sockets have better performance.
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: ${STREAMING_STATE_TRANSFER}
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. ${FC}
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. SFC is currently experimental, we recommend to use MFC and UFC (see below) instead. ${SFC}
MFC and UFC In 2.10, FC was separated into MFC (Multicast Flow Control) and Unicast Flow Control (UFC). The reason was that multicast flow control should not be impeded by unicast flow control, and vice versa. Also, performance for the separate implementations could be increased, plus they can be individually omitted. For example, if no unicast flow control is needed, UFC can be left out of the stack configuration.
MFC ${MFC}
UFC ${UFC}
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 ${STABLE}
Misc
COMPRESS ${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. ${FLUSH}
SCOPE As discussed in Scopes, the SCOPE protocol is used to deliver updates to different scopes concurrently. It has to be placed somewhere above UNICAST and NAKACK. SCOPE has a separate thread pool. The reason why the default thread pool from the transport wasn't used is that the default thread pool has a different purpose. For example, it can use a queue to which all incoming messages are added, which would defy the purpose of concurrent delivery in SCOPE. As a matter of fact, using a queue would most likely delay messages get sent up into SCOPE ! Also, the default pool's rejection policy might not be "run", so the SCOPE implementation would have to catch rejection exceptions and engage in a retry protocol, which is complex and wastes resources. The configuration of the thread pool is shown below. If you expect concurrent messages to N different scopes, then the max pool size would ideally be set to N. However, in most cases, this is not necessary as (a) the messages might not be to different scopes or (b) not all N scopes might get messages at the same time. So even if the max pool size is a bit smaller, the cost of this is slight delays, in the sense that a message for scope Y might wait until the thread processing message for scope X is available. To remove unused scopes, an expiry policy is provided: expiration_time is the number of milliseconds after which an idle scope is removed. An idle scope is a scope which hasn't seen any messages for expiration_time milliseconds. The expiration_interval value defines the number of milliseconds at which the expiry task runs. Setting both values to 0 disables expiration; it would then have to be done manually (see for details). ${SCOPE}
RELAY RELAY bridges traffic between seperate clusters, see for details. ${RELAY}
STOMP STOMP is a JGroups protocol which implements the STOMP protocol. Currently (as of Nov 2010), transactions and acks are not implemented. The location of a STOMP protocol in a stack is shown in .
STOMP in a protocol stack
The STOMP protocol should be near the top of the stack. A STOMP instance listens on a TCP socket for client connections. The port and bind address of the server socket can be defined via properties. A client can send SUBSCRIBE commands for various destinations. When a SEND for a given destination is received, STOMP adds a header to the message and broadcasts it to all cluster nodes. Every node then in turn forwards the message to all of its connected clients which have subscribed to the same destination. When a destination is not given, STOMP simply forwards the message to all connected clients. Traffic can be generated by clients and by servers. In the latter case, we could for example have code executing in the address space of a JGroups (server) node. In the former case, clients use the SEND command to send messages to a JGroups server and receive messages via the MESSAGE command. If there is code on the server which generates messages, it is important that both client and server code agree on a marshalling format, e.g. JSON, so that they understand each other's messages. Clients can be written in any language, as long as they understand the STOMP protocol. Note that the JGroups STOMP protocol implementation sends additional information (e.g. INFO) to clients; non-JGroups STOMP clients should simply ignore them. JGroups comes with a STOMP client (org.jgroups.client.StompConnection) and a demo (StompDraw). Both need to be started with the address and port of a JGroups cluster node. Once they have been started, the JGroups STOMP protocol will notify clients of cluster changes, which is needed so client can failover to another JGroups server node when a node is shut down. E.g. when a client connects to C, after connection, it'll get a list of endpoints (e.g. A,B,C,D). When C is terminated, or crashes, the client automatically reconnects to any of the remaining nodes, e.g. A, B, or D. When this happens, a client is also re-subscribed to the destinations it registered for. The JGroups STOMP protocol can be used when we have clients, which are either not in the same network segment as the JGroups server nodes, or which don't want to become full-blown JGroups server nodes. shows a typical setup.
STOMP architecture
There are 4 nodes in a cluster. Say the cluster is in a LAN, and communication is via IP multicasting (UDP as transport). We now have clients which do not want to be part of the cluster themselves, e.g. because they're in a different geographic location (and we don't want to switch the main cluster to TCP), or because clients are frequently started and stopped, and therefore the cost of startup and joining wouldn't be amortized over the lifetime of a client. Another reason could be that clients are written in a different language, or perhaps, we don't want a large cluster, which could be the case if we for example have 10 JGroups server nodes and 1000 clients connected to them. In the example, we see 9 clients connected to every JGroups cluster node. If a client connected to node A sends a message to destination /topics/chat, then the message is multicast from node A to all other nodes (B, C and D). Every node then forwards the message to those clients which have previously subscribed to /topics/chat. When node A crashes (or leaves) the JGroups STOMP clients (org.jgroups.client.StompConnection) simply pick another server node and connect to it. The properties for STOMP are shown below: ${STOMP}
DAISYCHAIN The DAISYCHAIN protocol is discussed in . ${DAISYCHAIN}
RATE_LIMITER RATE_LIMITER can be used to set a limit on the data sent per time unit. When sending data, only max_bytes can be sent per time_period milliseconds. E.g. if max_bytes="50M" and time_period="1000", then a sender can only send 50MBytes / sec max. ${RATE_LIMITER}
Locking protocols There are currently 2 locking protocols: org.jgroups.protocols.CENTRAL_LOCK and org.jgroups.protocols.PEER_LOCK.
CENTRAL_LOCK CENTRAL_LOCK has the current coordinator of a cluster grants locks, so every node has to communicate with the coordinator to acquire or release a lock. Lock requests by different nodes for the same lock are processed in the order in which they are received. A coordinator maintains a lock table. To prevent losing the knowledge of who holds which locks, the coordinator can push lock information to a number of backups defined by num_backups. If num_backups is 0, no replication of lock information happens. If num_backups is greater than 0, then the coordinator pushes information about acquired and released locks to all backup nodes. Topology changes might create new backup nodes, and lock information is pushed to those on becoming a new backup node. The advantage of CENTRAL_LOCK is that all lock requests are granted in the same order across the cluster, which is not the case with PEER_LOCK. ${CENTRAL_LOCK}
PEER_LOCK PEER_LOCK acquires a lock by contacting all cluster nodes, and lock acquisition is only successful if all non-faulty cluster nodes (peers) grant it. Unless a total order configuration is used (e.g. org.jgroups.protocols.SEQUENCER based), lock requests for the same resource from different senders may be received in different order, so deadlocks can occur. Example: Nodes A and B A and B call lock(X) at the same time A receives L(X,A) followed by L(X,B): locks X(A), queues L(X,B) B receives L(X,B) followed by L(X,A): locks X(B), queues L(X,A) To acquire a lock, we need lock grants from both A and B, but this will never happen here. To fix this, either add SEQUENCER to the configuration, so that all lock requests are received in the same global order at both A and B, or use java.util.concurrent.locks.Lock.tryLock(long,javaTimeUnit) with retries if a lock cannot be acquired. ${PEER_LOCK}
CENTRAL_EXECUTOR CENTRAL_EXECUTOR is an implementation of Executing which is needed by the ExecutionService. ${CENTRAL_EXECUTOR}
libjgroups-java-2.12.2.Final.orig/doc/manual/en/modules/tracing.xml0000644000175000017500000004020511647260573025025 0ustar moellermoellerTracing 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.
libjgroups-java-2.12.2.Final.orig/doc/manual/en/modules/writing.xml0000644000175000017500000001426111647260573025064 0ustar moellermoeller Writing protocols This chapter discusses how to write custom protocols
Anatomy of a protocol
Writing user defined headers Headers are mainly used by protocols, to ship additional information around with a message, without having to place it into the payload buffer, which is often occupied by the application already. However, headers can also be used by an application, e.g. to add information to a message, without having to squeeze it into the payload buffer. A header has to extend org.jgroups.Header, have an empty public constructor and (currently) implement the Externalizable interface (writeExternal() and readExternal() methods). Note that the latter requirement (Externalizable) will probably go away in 3.0. A header should also override size(), which returns the total number of bytes taken up in the output stream when an instance is marshalled using Streamable. Streamable is an interface for efficient marshalling with methods void writeTo(DataOutputStream out) throws IOException; and void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException;. Method writeTo() needs to write all relevant instance variables to the output stream and readFrom() needs to read them back in. It is important that size() returns the correct number of bytes, because some components such a message bundling in the transport depend on this, as they need to measure the exact number of bytes before sending a message off. If size() returns fewer bytes than what will actually be written to the stream, then it is possible that (if we use UDP with a 65535 bytes maximum) the datagram packet is dropped by UDP ! The final requirement is to add the newly created header class to jg-magic-map.xml (in the ./conf directory), or - if this is not a JGroups internal protocol - to add the class to ClassConfigurator. This can be done with method ClassConfigurator.getInstance().put(1899, MyHeader.class). The code below shows how an application defines a custom header, MyHeader, and uses it to attach additional information to message sent (to itself): public class bla { public static void main(String[] args) throws ChannelException, ClassNotFoundException { JChannel ch=new JChannel(); ch.connect("demo"); ch.setReceiver(new ReceiverAdapter() { public void receive(Message msg) { MyHeader hdr=(MyHeader)msg.getHeader("x"); System.out.println("-- received message " + msg + ", header is " + hdr); } }); ClassConfigurator.getInstance().add((short)1900, MyHeader.class); int cnt=1; for(int i=0; i < 5; i++) { Message msg=new Message(); msg.putHeader((short)1900, new MyHeader(cnt++)); ch.send(msg); } ch.close(); } public static class MyHeader extends Header implements Streamable { int counter=0; public MyHeader() { } private MyHeader(int counter) { this.counter=counter; } public String toString() { return "counter=" + counter; } public int size() { return Global.INT_SIZE; } public void writeTo(DataOutputStream out) throws IOException { out.writeInt(counter); } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { counter=in.readInt(); } } } The MyHeader class has an empty public constructor and implements the writeExternal() and readExternal() methods with no-op implementations. The state is represented as an integer counter. Method size() returns 4 bytes (Global.INT_SIZE), which is the number of bytes written by writeTo() and read by readFrom(). Before sending messages with instances of MyHeader attached, the program registers the MyHeader class with the ClassConfigurator. The example uses a magic number of 1900, but any number greater than 1024 can be used. If the magic number was already taken, an IllegalAccessException would be thrown. The final part is adding an instance of MyHeader to a message using Message.putHeader(). The first argument is a name which has to be unique across all headers for a given message. Usually, protocols use the protocol name (e.g. "UDP", "NAKACK"), so these names should not be used by an application. The second argument is an instance of the header. Getting a header is done through Message.getHeader() which takes the name as argument. This name of course has to be the same as the one used in putHeader().
libjgroups-java-2.12.2.Final.orig/doc/structure.txt0000644000175000017500000000276111647260573022133 0ustar moellermoeller 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 libjgroups-java-2.12.2.Final.orig/doc/tests/0000755000175000017500000000000011647260573020466 5ustar moellermoellerlibjgroups-java-2.12.2.Final.orig/doc/tests/ManualTests.txt0000644000175000017500000000745211647260573023477 0ustar moellermoeller 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 *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 - Suspend A (CTRL-Z) - Wait until B shows membershiop of 1, then resume A (fg) - A and B needs to merge back into a group of 2 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 libjgroups-java-2.12.2.Final.orig/doc/todo.lst0000644000175000017500000010162311647260573021020 0ustar moellermoeller 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 libjgroups-java-2.12.2.Final.orig/doc/tutorial/0000755000175000017500000000000011647260573021167 5ustar moellermoellerlibjgroups-java-2.12.2.Final.orig/doc/tutorial/.cvsignore0000644000175000017500000000000611647260573023163 0ustar moellermoellerbuild libjgroups-java-2.12.2.Final.orig/doc/tutorial/build.xml0000644000175000017500000000162111647260573023010 0ustar moellermoeller libjgroups-java-2.12.2.Final.orig/doc/tutorial/en/0000755000175000017500000000000011647260573021571 5ustar moellermoellerlibjgroups-java-2.12.2.Final.orig/doc/tutorial/en/code/0000755000175000017500000000000011647260573022503 5ustar moellermoellerlibjgroups-java-2.12.2.Final.orig/doc/tutorial/en/code/SimpleChat.java0000644000175000017500000000475311647260573025410 0ustar moellermoellerimport 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(); } } libjgroups-java-2.12.2.Final.orig/doc/tutorial/en/images/0000755000175000017500000000000011647260573023036 5ustar moellermoellerlibjgroups-java-2.12.2.Final.orig/doc/tutorial/en/images/BinaryContents.png0000644000175000017500000021211411647260573026507 0ustar moellermoeller‰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`‚libjgroups-java-2.12.2.Final.orig/doc/tutorial/en/master.xml0000644000175000017500000000602111647260573023605 0ustar moellermoeller ]> JGroups tutorial 2009 Bela Ban JGroups Project

belaban@yahoo.com
1998-2006 Bela Ban 2006-2011 Red Hat Inc This document is licensed under the Creative Commons Attribution-ShareAlike (CC-BY-SA) 3.0 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 . libjgroups-java-2.12.2.Final.orig/doc/tutorial/en/modules/0000755000175000017500000000000011647260573023241 5ustar moellermoellerlibjgroups-java-2.12.2.Final.orig/doc/tutorial/en/modules/installation.xml0000644000175000017500000002225511647260573026472 0ustar moellermoeller 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 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 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.4 2009/05/13 13:22:09 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.
libjgroups-java-2.12.2.Final.orig/doc/tutorial/en/modules/sampleapp.xml0000644000175000017500000004571311647260573025757 0ustar moellermoeller 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
libjgroups-java-2.12.2.Final.orig/lib/0000755000175000017500000000000011647260573017325 5ustar moellermoellerlibjgroups-java-2.12.2.Final.orig/pom.xml0000644000175000017500000002271711647260573020105 0ustar moellermoeller 4.0.0 org.jgroups jgroups bundle JGroups 2.12.2.Final http://www.jgroups.org Reliable cluster communication toolkit JBoss, a division of Red Hat http://www.jboss.org Bela Ban belaban@yahoo.com GNU Lesser General Public License 2.1 http://www.opensource.org/licenses/lgpl-2.1.php cvs -d:pserver:anonymous@javagroups.cvs.sourceforge.net:/cvsroot/javagroups cvs -d:ext:USER@javagroups.cvs.sf.net/cvsroot/javagroups cvs -d:pserver:anonymous@javagroups.cvs.sourceforge.net:/cvsroot/javagroups jira https://jira.jboss.com/jira/browse/JGRP 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 bsh bsh 1.2b7 true compile log4j log4j 1.2.14 true compile src conf *.xml *-service.xml ${project.build.outputDirectory}/schema maven-compiler-plugin 1.6 1.6 org/jgroups/util/JUnitXMLReporter.java maven-antrun-plugin compile run xalan xalan 2.7.1 xalan serializer 2.7.1 org.apache.maven.plugins maven-source-plugin 2.1.1 true attach-sources jar org.apache.maven.plugins maven-jar-plugin conf/manifest.mf org.apache.felix maven-bundle-plugin true schema;version=${project.version}, org.jgroups;version=${project.version}, org.jgroups.annotations;version=${project.version}, org.jgroups.auth;version=${project.version}, org.jgroups.blocks;version=${project.version}, org.jgroups.blocks.mux;version=${project.version}, org.jgroups.blocks.locking;version=${project.version}, org.jgroups.blocks.executor;version=${project.version}, org.jgroups.client;version=${project.version}, org.jgroups.conf;version=${project.version}, org.jgroups.debug;version=${project.version}, org.jgroups.demos;version=${project.version}, org.jgroups.demos.wb;version=${project.version}, org.jgroups.jmx;version=${project.version}, org.jgroups.logging;version=${project.version}, org.jgroups.mux;version=${project.version}, org.jgroups.persistence;version=${project.version}, org.jgroups.protocols;version=${project.version}, org.jgroups.protocols.pbcast;version=${project.version}, org.jgroups.stack;version=${project.version}, org.jgroups.util;version=${project.version}, org.jgroups.tests;version=${project.version} !bsh.*,* J2SE-1.6 libjgroups-java-2.12.2.Final.orig/src/0000755000175000017500000000000011647260573017346 5ustar moellermoellerlibjgroups-java-2.12.2.Final.orig/src/org/0000755000175000017500000000000011647260573020135 5ustar moellermoellerlibjgroups-java-2.12.2.Final.orig/src/org/jgroups/0000755000175000017500000000000011647260573021626 5ustar moellermoellerlibjgroups-java-2.12.2.Final.orig/src/org/jgroups/Address.java0000644000175000017500000000166011647260573024061 0ustar moellermoeller 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 // flags used for marshalling public static final byte NULL = 1 << 0; public static final byte UUID_ADDR = 1 << 1; public static final byte IP_ADDR = 1 << 2; /** * 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(); } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/BlockEvent.java0000644000175000017500000000024011647260573024521 0ustar moellermoeller package org.jgroups; /** * Trivial object that represents a block event. */ public class BlockEvent { public String toString() {return "BlockEvent";} } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/Channel.java0000644000175000017500000006213311647260573024046 0ustar moellermoeller package org.jgroups; import org.jgroups.logging.Log; import org.jgroups.annotations.MBean; import org.jgroups.annotations.ManagedOperation; import org.jgroups.stack.Protocol; import org.jgroups.stack.ProtocolStack; import org.jgroups.util.DefaultSocketFactory; import org.jgroups.util.SocketFactory; 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 */ @MBean(description="Channel") public abstract class Channel implements Transport { @Deprecated public static final int BLOCK=0; @Deprecated public static final int VIEW=1; @Deprecated public static final int SUSPECT=2; public static final int LOCAL=3; @Deprecated public static final int GET_STATE_EVENTS=4; @Deprecated public static final int AUTO_RECONNECT=5; @Deprecated 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 SocketFactory socket_factory=new DefaultSocketFactory(); protected abstract Log getLog(); public abstract ProtocolStack getProtocolStack(); public SocketFactory getSocketFactory() { return socket_factory; } public void setSocketFactory(SocketFactory factory) { socket_factory=factory; ProtocolStack stack=getProtocolStack(); Protocol prot=stack != null? stack.getTopProtocol() : null; if(prot != null) prot.setSocketFactory(factory); } /** 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 */ @Deprecated 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; abstract public void send(Address dst, Address src, byte[] buf) throws ChannelNotConnectedException, ChannelClosedException; abstract public void send(Address dst, Address src, byte[] buf, int offset, int length) 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. Addresses can be used as destination in the send() operation. @return The channel's address (opaque) @deprecated Use {@link #getAddress()} instead */ abstract public Address getLocalAddress(); /** 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. Successor to {@link #getAddress()}. Addresses can be used as destination in the send() operation. @return The channel's address (opaque) */ abstract public Address getAddress(); /** * Returns the logical name of this channel if set. * @return The logical name or null (if not set) */ abstract public String getName(); /** * Returns the logical name of a given member. The lookup is from the local cache of logical address * / logical name mappings and no remote communication is performed. * @param member * @return */ abstract public String getName(Address member); /** * Sets the logical name for the channel. The name will stay associated with this channel for the channel's * lifetime (until close() is called). This method should be called before calling connect().
* @param name */ abstract public void setName(String name); /** 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(); public String getProperties() { return "n/a"; } /** 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; } public UpHandler getUpHandler() { return 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. */ @ManagedOperation public synchronized void addChannelListener(ChannelListener listener) { if(listener == null) return; if(channel_listeners == null) channel_listeners=new CopyOnWriteArraySet(); channel_listeners.add(listener); } @ManagedOperation 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; } public Receiver getReceiver() { return receiver; } /** 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); } } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/ChannelClosedException.java0000644000175000017500000000067111647260573027056 0ustar moellermoeller 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"; } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/ChannelException.java0000644000175000017500000000072611647260573025725 0ustar moellermoeller 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); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/ChannelFactory.java0000644000175000017500000001511011647260573025367 0ustar moellermoeller 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 * @deprecated Might get removed in 3.0. Use your own method of injecting channels */ @Deprecated 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; } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/ChannelListener.java0000644000175000017500000000072311647260573025551 0ustar moellermoeller 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); @Deprecated void channelShunned(); @Deprecated void channelReconnected(Address addr); } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/ChannelListenerAdapter.java0000644000175000017500000000070711647260573027054 0ustar moellermoellerpackage org.jgroups; /** * Class which implements {@link org.jgroups.ChannelListener} * @author Bela Ban */ 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) { } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/ChannelNotConnectedException.java0000644000175000017500000000071411647260573030226 0ustar moellermoeller 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"; } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/Event.java0000644000175000017500000002320711647260573023556 0ustar moellermoellerpackage 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 FIND_ALL_VIEWS = 13; // arg = JoinPromise (or null (merge2)) public static final int MERGE = 14; // arg = Map 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 OVERWRITE_DIGEST = 42; // arg = Digest 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 = List

(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 CONNECT_WITH_STATE_TRANSFER = 80; // arg = cluster name (string) public static final int PREPARE_VIEW = 86; // arg = View public static final int GET_PHYSICAL_ADDRESS = 87; // arg = Address --> PhysicalAddress public static final int GET_LOGICAL_PHYSICAL_MAPPINGS = 88; // arg = null --> Map public static final int SET_PHYSICAL_ADDRESS = 89; // arg = Tuple public static final int REMOVE_ADDRESS = 90; // arg = Address public static final int GET_LOCAL_ADDRESS = 91; // arg = null --> UUID (local_addr) public static final int CONNECT_USE_FLUSH = 92; public static final int CONNECT_WITH_STATE_TRANSFER_USE_FLUSH = 93; public static final int SUSPEND_BUT_FAIL = 94; // used in FLUSH testing, no args public static final int LOCK = 95; // arg=LockInfo public static final int UNLOCK = 96; // arg=LockInfo public static final int UNLOCK_ALL = 97; // arg=null public static final int LOCK_AWAIT = 98; // arg=LockInfo public static final int LOCK_SIGNAL = 99; // arg=AwaitInfo 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 */ @Deprecated public void setType(int type) { throw new IllegalAccessError("setType() has been deprecated, to make Events immutable"); } public Object getArg() { return arg; } @Deprecated 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 FIND_ALL_VIEWS: return "FIND_ALL_VIEWS"; 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 OVERWRITE_DIGEST: return "OVERWRITE_DIGEST"; case MERGE: return "MERGE"; 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 SUSPEND_BUT_FAIL: return "SUSPEND_BUT_FAIL"; 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 CONNECT_WITH_STATE_TRANSFER: return "CONNECT_WITH_STATE_TRANSFER"; case PREPARE_VIEW: return "PREPARE_VIEW"; case GET_PHYSICAL_ADDRESS: return "GET_PHYSICAL_ADDRESS"; case GET_LOGICAL_PHYSICAL_MAPPINGS: return "GET_LOGICAL_PHYSICAL_MAPPINGS"; case SET_PHYSICAL_ADDRESS: return "SET_PHYSICAL_ADDRESS"; case REMOVE_ADDRESS: return "REMOVE_ADDRESS"; case GET_LOCAL_ADDRESS: return "GET_LOCAL_ADDRESS"; case CONNECT_USE_FLUSH: return "CONNECT_USE_FLUSH"; case CONNECT_WITH_STATE_TRANSFER_USE_FLUSH: return "CONNECT_WITH_STATE_TRANSFER_USE_FLUSH"; case LOCK: return "LOCK"; case UNLOCK: return "UNLOCK"; case UNLOCK_ALL: return "UNLOCK_ALL"; case LOCK_AWAIT: return "LOCK_AWAIT"; case LOCK_SIGNAL: return "LOCK_SIGNAL"; 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(); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/ExtendedMembershipListener.java0000644000175000017500000000133511647260573027755 0ustar moellermoellerpackage org.jgroups; /** * @author Bela Ban */ 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(); } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/ExtendedMessageListener.java0000644000175000017500000000534011647260573027246 0ustar moellermoellerpackage 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 * */ 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); } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/ExtendedReceiver.java0000644000175000017500000000043711647260573025722 0ustar moellermoellerpackage org.jgroups; /** * Extends Receiver, plus the partial state transfer methods. * This interface will disappear (be merged with Receiver) in 3.0. * @author Bela Ban */ public interface ExtendedReceiver extends Receiver, ExtendedMessageListener, ExtendedMembershipListener { } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/ExtendedReceiverAdapter.java0000644000175000017500000000177711647260573027233 0ustar moellermoellerpackage org.jgroups; import java.io.InputStream; import java.io.OutputStream; import org.jgroups.util.Util; /** * @author Bela Ban */ 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); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/GetStateEvent.java0000644000175000017500000000102511647260573025211 0ustar moellermoeller 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 + ']';} } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/Global.java0000644000175000017500000001527511647260573023703 0ustar moellermoellerpackage org.jgroups; import org.jgroups.conf.ClassConfigurator; import org.jgroups.protocols.SCOPE; import org.jgroups.util.UUID; /** * Globals used by JGroups packages. * * @author Bela Ban Mar 29, 2004 */ public class Global { 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 int DOUBLE_SIZE = Double.SIZE / 8; // 8; public static final int FLOAT_SIZE = Float.SIZE / 8; // 4; public static final int MAX_DATAGRAM_PACKET_SIZE=1 << 16; public static final Object NULL=new Object(); public static final short SCOPE_ID=ClassConfigurator.getProtocolId(SCOPE.class); public static final String IPv4="java.net.preferIPv4Stack"; public static final String IPv6="java.net.preferIPv6Addresses"; public static final String NON_LOOPBACK_ADDRESS="NON_LOOPBACK_ADDRESS"; 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 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 BPING_BIND_PORT="jgroups.bping.bind_port"; public static final String STOMP_BIND_ADDR="jgroups.stomp.bind_addr"; public static final String STOMP_ENDPOINT_ADDR="jgroups.stomp.endpoint_addr"; public static final String MAGIC_NUMBER_FILE="jgroups.conf.magic_number_file"; public static final String PROTOCOL_ID_FILE="jgroups.conf.protocol_id_file"; public static final String RESOLVE_DNS="jgroups.resolve_dns"; public static final String PRINT_UUIDS="jgroups.print_uuids"; public static final String UUID_CACHE_MAX_ELEMENTS="jgroups.uuid_cache.max_elements"; public static final String UUID_CACHE_MAX_AGE="jgroups.uuid_cache.max_age"; public static final String IPV6_MCAST_PREFIX="jgroups.ipmcast.prefix"; 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 String USE_JDK_LOGGER="jgroups.use.jdk_logger"; // forces use of the JDK logger public static final String CUSTOM_LOG_FACTORY="jgroups.logging.log_factory_class"; public static final long DEFAULT_FIRST_UNICAST_SEQNO = 1; /** First ID assigned for building blocks (defined in jg-protocols.xml) */ public static final short BLOCKS_START_ID=200; 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 String PREFIX="org.jgroups.protocols."; public static final String XML_VALIDATION="jgroups.xml.validation"; // for TestNG public static final String FUNCTIONAL="functional"; public static final String TIME_SENSITIVE="time-sensitive"; public static final String STACK_DEPENDENT="stack-dependent"; public static final String STACK_INDEPENDENT="stack-independent"; public static final String GOSSIP_ROUTER="gossip-router"; public static final String FLUSH="flush"; public static final String INITIAL_MCAST_ADDR="INITIAL_MCAST_ADDR"; public static final String INITIAL_MCAST_PORT="INITIAL_MCAST_PORT"; public static final String INITIAL_TCP_PORT="INITIAL_TCP_PORT"; public static final String UDP_MCAST_SOCK="jgroups.udp.mcast_sock"; public static final String UDP_UCAST_SOCK="jgroups.udp.unicast_sock"; public static final String TCP_SRV_SOCK="jgroups.tcp.srv_sock"; public static final String TCP_SOCK="jgroups.tcp.sock"; public static final String TUNNEL_UCAST_SOCK="jgroups.tunnel.ucast_sock"; public static final String MPING_MCAST_SOCK="jgroups.mping.mcast_sock"; public static final String BPING_SOCK="jgroups.bping.sock"; public static final String TP_DIAG_MCAST_SOCK="jgroups.tp.diag.mcast_sock"; public static final String STREAMING_STATE_TRANSFER_SERVER_SOCK="jgroups.streaming_state_transfer.srv_sock"; public static final String FD_SOCK_SRV_SOCK="jgroups.fd_sock.srv_sock"; public static final String BSH_SRV_SOCK="jgroups.bsh.srv_sock"; public static final String STOMP_SRV_SOCK="jgroups.stomp.srv_sock"; public static final String CCHM_INITIAL_CAPACITY="cchm.initial_capacity"; public static final String CCHM_LOAD_FACTOR="cchm.load_factor"; public static final String CCHM_CONCURRENCY_LEVEL="cchm.concurrency_level"; public static final int IPV4_SIZE=4; public static final int IPV6_SIZE=16; public static final int SMALL_CLUSTER_SIZE=10; public static final int NORMAL_CLUSTER_SIZE=20; public static final int BIG_CLUSTER_SIZE=100; 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; } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/Header.java0000644000175000017500000000162411647260573023664 0ustar moellermoeller package org.jgroups; import org.jgroups.util.Streamable; /** * Abstract base class for all headers to be added to a Message. * @author Bela Ban */ public abstract class Header implements Streamable { 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 abstract int size(); public String toString() { return '[' + getClass().getName() + " Header]"; } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/JChannel.java0000644000175000017500000025350111647260573024161 0ustar moellermoellerpackage org.jgroups; import org.jgroups.annotations.MBean; import org.jgroups.annotations.ManagedAttribute; import org.jgroups.annotations.ManagedOperation; import org.jgroups.blocks.MethodCall; import org.jgroups.conf.ConfiguratorFactory; import org.jgroups.conf.ProtocolConfiguration; import org.jgroups.conf.ProtocolStackConfigurator; import org.jgroups.logging.Log; import org.jgroups.logging.LogFactory; import org.jgroups.protocols.TP; import org.jgroups.stack.*; import org.jgroups.util.*; import org.w3c.dom.Element; import java.io.File; import java.io.InputStream; import java.io.OutputStream; import java.io.Serializable; import java.lang.reflect.Method; import java.net.DatagramSocket; import java.net.ServerSocket; 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.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 */ @MBean(description="JGroups channel") public class JChannel extends Channel { /** The default protocol stack used by the default constructor */ public static final String DEFAULT_PROTOCOL_STACK="udp.xml"; /*the address of this JChannel instance*/ protected Address local_addr=null; protected AddressGenerator address_generator=null; protected String name=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; private final Promise state_promise=new Promise(); private final Exchanger applstate_exchanger=new Exchanger(); /*flag to indicate whether to receive blocks, if this is set to true, receive_views is set to true*/ @ManagedAttribute(description="Flag indicating whether to receive blocks",writable=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*/ @ManagedAttribute(description="Flag indicating whether to receive this channel's own messages",writable=true) private boolean receive_local_msgs=true; /*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 config=Util.createConcurrentMap(16); protected final Log log=LogFactory.getLog(JChannel.class); /** Collect statistics */ @ManagedAttribute(description="Collect channel statistics",writable=true) 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(); /** * Creates a JChannel without a protocol stack; used for programmatic creation of channel and protocol stack * @param create_protocol_stack If true, tthe default configuration will be used. If false, no protocol stack * will be created */ public JChannel(boolean create_protocol_stack) { if(create_protocol_stack) { try { init(ConfiguratorFactory.getStackConfigurator(DEFAULT_PROTOCOL_STACK)); } catch(ChannelException e) { throw new RuntimeException(e); } } } /** * 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; try { c=ConfiguratorFactory.getStackConfigurator(properties); } catch(Exception x) { throw new ChannelException("unable to load protocol stack", x); } init(c); } /** * Creates a channel with the same configuration as the channel passed to this constructor. This is used by * testing code, and should not be used by any other code ! * @param ch * @throws ChannelException */ public JChannel(JChannel ch) throws ChannelException { init(ch); receive_blocks=ch.receive_blocks; receive_local_msgs=ch.receive_local_msgs; } /** * Returns the protocol stack */ public ProtocolStack getProtocolStack() { return prot_stack; } public void setProtocolStack(ProtocolStack stack) { this.prot_stack=stack; if(prot_stack != null) prot_stack.setChannel(this); } 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 prot_stack != null? prot_stack.printProtocolSpec(true) : null; } public boolean statsEnabled() { return stats; } public void enableStats(boolean stats) { this.stats=stats; } @ManagedOperation public void resetStats() { sent_msgs=received_msgs=sent_bytes=received_bytes=0; } @ManagedAttribute public long getSentMessages() {return sent_msgs;} @ManagedAttribute public long getSentBytes() {return sent_bytes;} @ManagedAttribute public long getReceivedMessages() {return received_msgs;} @ManagedAttribute public long getReceivedBytes() {return received_bytes;} @ManagedAttribute public int getNumberOfTasksInTimer() { TimeScheduler timer=getTimer(); return timer != null? timer.size() : -1; } @ManagedAttribute public int getTimerThreads() { TimeScheduler timer=getTimer(); return timer != null? timer.getMinThreads() : -1; } @ManagedOperation public String dumpTimerQueue() { TimeScheduler timer=getTimer(); return timer != null? timer.dumpTimerTasks() : "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. */ @ManagedOperation(description="Connects the channel to a group") public synchronized void connect(String cluster_name) throws ChannelException { connect(cluster_name,true); } /** * Connects the channel to a group. * 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. * This method starts the protocol stack by calling ProtocolStack.start, * then it sends an Event.CONNECT event down the stack and waits for the return value. * Once the call returns, the channel listeners are notified and the channel is considered connected. * * @param cluster_name A 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. */ @ManagedOperation(description="Connects the channel to a group") public synchronized void connect(String cluster_name, boolean useFlushIfPresent) throws ChannelException { if (connected) { if (log.isTraceEnabled()) log.trace("already connected to " + cluster_name); return; } setAddress(); startStack(cluster_name); if (cluster_name != null) { // only connect if we are not a unicast channel Event connect_event; if (useFlushIfPresent) { connect_event = new Event(Event.CONNECT_USE_FLUSH, cluster_name); } else { connect_event = new Event(Event.CONNECT, cluster_name); } // waits forever until connected (or channel is closed) Object res = downcall(connect_event); if (res != null && res instanceof Exception) { // the JOIN was rejected by the coordinator stopStack(true, false); init(); throw new ChannelException("connect() failed", (Throwable) res); } } 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 { connect(cluster_name, target, state_id, timeout,true); } /** * 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, boolean useFlushIfPresent) throws ChannelException { if(connected) { if(log.isTraceEnabled()) log.trace("already connected to " + this.cluster_name); return; } setAddress(); startStack(cluster_name); boolean stateTransferOk; boolean joinSuccessful; boolean canFetchState=false; // only connect if we are not a unicast channel if(cluster_name == null) return; try { Event connect_event; if(useFlushIfPresent) connect_event=new Event(Event.CONNECT_WITH_STATE_TRANSFER_USE_FLUSH, cluster_name); else 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(getAddress() + " could not fetch state " + (state_id == null ? "(full)" : state_id) + " from " + (target == null ? "(all)" : target)); } } catch(Exception e) { throw new StateTransferException(getAddress() + " could not fetch state " + (state_id == null ? "(full)" : state_id) + " from " + (target == null ? "(all)" : target), e); } } } finally { if (flushSupported() && useFlushIfPresent){ //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
    *
*/ @ManagedOperation(description="Disconnects the channel if connected") 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
*/ @ManagedOperation(description="Disconnects and destroys the channel") public synchronized void close() { _close(true, true); // by default disconnect before closing channel and close mq } /** * Shuts down a channel without disconnecting. To be used by tests only, don't use for application purposes * @deprecated Use {@link Util#shutdown(Channel)} instead. This method will be removed in 3.0 */ @ManagedOperation(description="Shuts down the channel without disconnecting") @Deprecated public synchronized void shutdown() { try { Util.shutdown(this); } catch(Exception e) { log.error("failed shutting down channel " + getAddress(), e); } } /** * 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 *
* @deprecated With the removal of shunning, this method should not be used anymore */ @Deprecated public synchronized void open() throws ChannelException { if(!closed) throw new ChannelException("channel is already open"); try { mq.reset(); String props=getProperties(); List configs=Configurator.parseConfigurations(props); // new stack is created on open() - bela June 12 2003 prot_stack=new ProtocolStack(this); prot_stack.setup(configs); closed=false; } catch(Exception e) { throw new ChannelException("failed to open channel" , e); } } /** * returns true if the Open operation has been called successfully */ @ManagedAttribute public boolean isOpen() { return !closed; } /** * returns true if the Connect operation has been called successfully */ @ManagedAttribute public boolean isConnected() { return connected; } @ManagedAttribute public int getNumMessages() { return mq.size(); } @ManagedOperation 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. */ @ManagedOperation 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, List attrs) { return prot_stack.dumpStats(protocol_name, attrs); } @ManagedOperation public Map dumpStats(String protocol_name) { return prot_stack.dumpStats(protocol_name, null); } protected Map dumpChannelStats() { Map retval=new HashMap(); retval.put("sent_msgs", sent_msgs); retval.put("sent_bytes", sent_bytes); retval.put("received_msgs", received_msgs); retval.put("received_bytes", 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 */ @ManagedOperation 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 */ @ManagedOperation public void send(Address dst, Address src, Serializable obj) throws ChannelNotConnectedException, ChannelClosedException { send(new Message(dst, src, obj)); } public void send(Address dst, Address src, byte[] buf) throws ChannelNotConnectedException, ChannelClosedException { send(new Message(dst, src, buf)); } public void send(Address dst, Address src, byte[] buf, int offset, int length) throws ChannelNotConnectedException, ChannelClosedException { send(new Message(dst, src, buf, offset, length)); } /** * 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 * * @deprecated Use a {@link Receiver} instead */ 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; } @ManagedAttribute(name="View") public String getViewAsString() { View v=getView(); return v != null ? v.toString() : "n/a"; } @ManagedAttribute public static String getVersion() { return Version.printDescription(); } @Deprecated public Address getLocalAddress() { return getAddress(); } /** * Returns the local address of the channel (null if the channel is closed) */ public Address getAddress() { return closed ? null : local_addr; } @ManagedAttribute(name="Address") public String getAddressAsString() { return local_addr != null? local_addr.toString() : "n/a"; } @ManagedAttribute(name="Address (UUID)") public String getAddressAsUUID() { return local_addr instanceof UUID? ((UUID)local_addr).toStringLong() : null; } public String getName() { return name; } public String getName(Address member) { return member != null? UUID.get(member) : null; } /** * Sets the logical name for the channel. The name will stay associated with this channel for the channel's * lifetime (until close() is called). This method should be called before calling connect().
* @param name */ @ManagedAttribute(writable=true, description="The logical name of this channel. Stays with the channel until " + "the channel is closed") public void setName(String name) { if(name != null) { this.name=name; if(local_addr != null) { UUID.add(local_addr, this.name); } } } /** * 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; } @ManagedAttribute(description="Returns cluster name this channel is connected to") public String getClusterName() { return closed ? null : !connected ? null : cluster_name; } /** * Returns the current {@link AddressGenerator}, or null if none is set * @return * @since 2.12 */ public AddressGenerator getAddressGenerator() { return address_generator; } /** * Sets the new {@link AddressGenerator}. New addresses will be generated using the new generator. This * should not be done while a channel is connected, but before connecting. * @param address_generator * @since 2.12 */ public void setAddressGenerator(AddressGenerator address_generator) { this.address_generator=address_generator; } /** * 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: case SUSPECT: case GET_STATE_EVENTS: case AUTO_RECONNECT: case AUTO_GETSTATE: 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 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; 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; case SUSPECT: return Boolean.TRUE; case AUTO_RECONNECT: return false; case AUTO_GETSTATE: return 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 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 // Changed: when a channel gets a view of which it is a member then it should be // connected even if connect() hasn't returned yet ! (bela Noc 2010) if(connected == false) { connected=true; } break; case Event.CONFIG: Map cfg=(Map)evt.getArg(); if(cfg != null) { if(cfg.containsKey("state_transfer")) { state_transfer_supported=((Boolean)cfg.get("state_transfer")).booleanValue(); } if(cfg.containsKey("flush_supported")) { flush_supported=((Boolean)cfg.get("flush_supported")).booleanValue(); } cfg.putAll(cfg); } 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.GET_LOCAL_ADDRESS: return local_addr; default: break; } // If UpHandler is installed, pass all events to it and return (UpHandler is e.g. a building block) if(up_handler != null) return up_handler.up(evt); 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 true; } if(receiver != null) { try { receiver.block(); } catch(Throwable t) { if(log.isErrorEnabled()) log.error("failed calling block() in receiver", t); } return 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); } } 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 UUID) ((UUID)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 UUID) ((UUID)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); } @ManagedOperation 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'); 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("state_transfer_supported=").append(state_transfer_supported).append('\n'); sb.append("props=").append(getProperties()).append('\n'); } return sb.toString(); } /* ----------------------------------- Private Methods ------------------------------------- */ protected final void init(ProtocolStackConfigurator configurator) throws ChannelException { if(log.isInfoEnabled()) log.info("JGroups version: " + Version.description); List configs; try { configs=configurator.getProtocolStack(); for(ProtocolConfiguration config: configs) config.substituteVariables(); // replace vars with system props } catch(Exception e) { throw new ChannelException("unable to parse the protocol configuration", e); } synchronized(Channel.class) { prot_stack=new ProtocolStack(this); try { prot_stack.setup(configs); // Setup protocol stack (creates protocol, calls init() on them) } catch(Throwable e) { throw new ChannelException("unable to setup the protocol stack", e); } } } protected final void init(JChannel ch) throws ChannelException { if(ch == null) throw new IllegalArgumentException("channel is null"); if(log.isInfoEnabled()) log.info("JGroups version: " + Version.description); synchronized(JChannel.class) { prot_stack=new ProtocolStack(this); try { prot_stack.setup(ch.getProtocolStack()); // 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() { if(local_addr != null) down(new Event(Event.REMOVE_ADDRESS, local_addr)); 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; if(socket_factory != null) prot_stack.getTopProtocol().setSocketFactory(socket_factory); try { prot_stack.startStack(cluster_name, local_addr); // calls start() in all protocols, from top to bottom } catch(Throwable e) { throw new ChannelException("failed to start protocol stack", e); } /*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); } /** * Generates new UUID and sets local address. Sends down a REMOVE_ADDRESS (if existing address was present) and * a SET_LOCAL_ADDRESS */ protected void setAddress() { Address old_addr=local_addr; local_addr=address_generator != null? address_generator.generateAddress() : UUID.randomUUID(); byte[] buf=(byte[])additional_data.get("additional_data"); if(buf != null) ((UUID)local_addr).setAdditionalData(buf); if(old_addr != null) down(new Event(Event.REMOVE_ADDRESS, old_addr)); if(name == null || name.length() == 0) // generate a logical name if not set name=Util.generateLocalName(); if(name != null && name.length() > 0) UUID.add(local_addr, name); Event evt=new Event(Event.SET_LOCAL_ADDRESS, local_addr); down(evt); if(up_handler != null) up_handler.up(evt); } /** * 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); 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) { Address old_addr=local_addr; 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 if(old_addr != null) UUID.remove(old_addr); } protected void stopStack(boolean stop, boolean destroy) { if(prot_stack != null) { try { if(stop) 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) { mq.close(flush_entries); } 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; 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"); } down(new Event(Event.RESUME)); } public void stopFlush(List

flushParticipants) { if(!flushSupported()) { throw new IllegalStateException("Flush is not supported, add pbcast.FLUSH protocol to your configuration"); } down(new Event(Event.RESUME, flushParticipants)); } @Override public Map getInfo(){ return new HashMap(config); } public void setInfo(String key, Object value) { if(key != null) config.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) { Map map=new HashMap(2); for(String key: keys) { if(key.startsWith("jmx")) { Map tmp_stats; int index=key.indexOf("="); if(index > -1) { List list=null; String protocol_name=key.substring(index +1); index=protocol_name.indexOf("."); if(index > -1) { String rest=protocol_name; protocol_name=protocol_name.substring(0, index); String attrs=rest.substring(index +1); // e.g. "num_sent,msgs,num_received_msgs" list=Util.parseStringList(attrs, ","); } tmp_stats=dumpStats(protocol_name, list); } 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"); } if(key.equals("socks")) { map.put("socks", getOpenSockets()); } if(key.startsWith("invoke") || key.startsWith("op")) { int index=key.indexOf("="); if(index != -1) { try { handleOperation(map, key.substring(index+1)); } catch(Throwable throwable) { log.error("failed invoking operation " + key.substring(index+1), throwable); } } } } map.put("version", Version.description); if(my_view != null && !map.containsKey("view")) map.put("view", my_view.toString()); map.put("local_addr", getAddressAsString() + " [" + getAddressAsUUID() + "]"); PhysicalAddress physical_addr=(PhysicalAddress)downcall(new Event(Event.GET_PHYSICAL_ADDRESS, local_addr)); if(physical_addr != null) map.put("physical_addr", physical_addr.toString()); map.put("cluster", getClusterName()); return map; } public String[] supportedKeys() { return new String[]{"jmx", "info", "invoke=[]", "\nop=[]", "socks"}; } String getOpenSockets() { Map socks=getSocketFactory().getSockets(); TP transport=getProtocolStack().getTransport(); if(transport != null && transport.isSingleton()) { Map tmp=transport.getSocketFactory().getSockets(); if(tmp != null) socks.putAll(tmp); } StringBuilder sb=new StringBuilder(); if(socks != null) { for(Map.Entry entry: socks.entrySet()) { Object key=entry.getKey(); if(key instanceof ServerSocket) { ServerSocket tmp=(ServerSocket)key; sb.append(tmp.getInetAddress()).append(":").append(tmp.getLocalPort()) .append(" ").append(entry.getValue()).append(" [tcp]"); } else if(key instanceof DatagramSocket) { DatagramSocket sock=(DatagramSocket)key; sb.append(sock.getLocalAddress()).append(":").append(sock.getLocalPort()) .append(" ").append(entry.getValue()).append(" [udp]"); } else { sb.append(key).append(" ").append(entry.getValue()); } sb.append("\n"); } } return sb.toString(); } /** * Invokes an operation and puts the return value into map * @param map * @param operation Protocol.OperationName[args], e.g. STABLE.foo[arg1 arg2 arg3] */ private void handleOperation(Map map, String operation) throws Throwable { int index=operation.indexOf("."); if(index == -1) throw new IllegalArgumentException("operation " + operation + " is missing the protocol name"); String prot_name=operation.substring(0, index); Protocol prot=prot_stack.findProtocol(prot_name); if(prot == null) throw new IllegalArgumentException("protocol " + prot_name + " not found"); int args_index=operation.indexOf("["); String method_name; if(args_index != -1) method_name=operation.substring(index +1, args_index).trim(); else method_name=operation.substring(index+1).trim(); String[] args=null; if(args_index != -1) { int end_index=operation.indexOf("]"); if(end_index == -1) throw new IllegalArgumentException("] not found"); List str_args=Util.parseCommaDelimitedStrings(operation.substring(args_index + 1, end_index)); Object[] strings=str_args.toArray(); args=new String[strings.length]; for(int i=0; i < strings.length; i++) args[i]=(String)strings[i]; } Method method=MethodCall.findMethod(prot.getClass(), method_name, args); MethodCall call=new MethodCall(method); Object[] converted_args=null; if(args != null) { converted_args=new Object[args.length]; Class[] types=method.getParameterTypes(); for(int i=0; i < args.length; i++) converted_args[i]=MethodCall.convert(args[i], types[i]); } Object retval=call.invoke(prot, converted_args); if(retval != null) map.put(prot_name + "." + method_name, retval.toString()); } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/JChannelFactory.java0000644000175000017500000005467011647260573025517 0ustar moellermoeller package org.jgroups; import org.jgroups.logging.Log; import org.jgroups.logging.LogFactory; import org.jgroups.annotations.MBean; import org.jgroups.annotations.ManagedAttribute; import org.jgroups.annotations.ManagedOperation; 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. * @see JChannelFactory * @deprecated Might get removed in 3.0. Use your own method of injecting channels */ @Deprecated @MBean(description="Factory to create channels") 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 */ @ManagedAttribute(writable=true) private String domain="jgroups"; /** Whether or not to expose channels via JMX */ @ManagedAttribute(description="Expose channels via JMX", writable=true) private boolean expose_channels=true; /** Whether to expose the factory only, or all protocols as well */ @ManagedAttribute(description="Expose protocols via JMX", writable=true) 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); } /** * @deprecated Use a shared transport instead of the multiplexer * @param properties * @throws Exception */ public void setMultiplexerConfig(Object properties) throws Exception { setMultiplexerConfig(properties, true); } /** * @deprecated Use a shared transport instead of the multiplexer * @param properties * @param replace * @throws Exception */ 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); } } /** * @deprecated Use a shared transport instead of the multiplexer * @param file * @throws Exception */ public void setMultiplexerConfig(File file) throws Exception { setMultiplexerConfig(file, true); } /** * @deprecated Use a shared transport instead of the multiplexer * @param file * @param replace * @throws Exception */ 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); } } /** * @deprecated Use a shared transport instead of the multiplexer * @param properties * @throws Exception */ public void setMultiplexerConfig(Element properties) throws Exception { parse(properties, true); } /** * @deprecated Use a shared transport instead of the multiplexer * @param properties * @param replace * @throws Exception */ public void setMultiplexerConfig(Element properties, boolean replace) throws Exception { parse(properties, replace); } /** * @deprecated Use a shared transport instead of the multiplexer * @param url * @throws Exception */ public void setMultiplexerConfig(URL url) throws Exception { setMultiplexerConfig(url, true); } /** * @deprecated Use a shared transport instead of the multiplexer * @param url * @param replace * @throws Exception */ 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); } } /** * @deprecated Use a shared transport instead of the multiplexer * @param properties * @throws Exception */ @ManagedOperation public void setMultiplexerConfig(String properties) throws Exception { setMultiplexerConfig(properties, true); } /** * @deprecated Use a shared transport instead of the multiplexer * @param properties * @param replace * @throws Exception */ @ManagedOperation 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 */ @ManagedOperation 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; } /** * @deprecated Use a shared transport instead of the multiplexer * @return Returns all configurations */ @ManagedOperation(description="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 */ @ManagedOperation(description="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); } /** * @deprecated Use a shared transport instead of the multiplexer * @param stack_name * @param id * @return * @throws Exception */ @ManagedOperation(description="Create multiplexed channel") public Channel createMultiplexerChannel(String stack_name, String id) throws Exception { return createMultiplexerChannel(stack_name, id, false, null); } /** * @deprecated Use a shared transport instead of the multiplexer * @param stack_name * @param id * @param register_for_state_transfer * @param substate_id * @return * @throws Exception */ @ManagedOperation(description="Create multiplexed channel with state transfer reguistration") 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. * @deprecated Use a shared transport instead of the multiplexer * @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"; } } @ManagedOperation public void start() throws Exception { } @ManagedOperation public void stop() { } @ManagedOperation 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(); } @ManagedOperation public String dumpConfiguration() { return stacks.keySet().toString(); } @ManagedOperation 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 '<" + PROTOCOL_STACKS + ">' 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()); } } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/Membership.java0000644000175000017500000001555111647260573024573 0ustar moellermoeller package org.jgroups; import org.jgroups.logging.Log; import org.jgroups.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); } } } public void add(Address ... mbrs) { for(Address mbr: mbrs) add(mbr); } /** * 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(); } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/MembershipListener.java0000644000175000017500000000364411647260573026301 0ustar moellermoeller 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(); } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/MergeView.java0000644000175000017500000001043311647260573024364 0ustar moellermoeller 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 View copy() { return (MergeView)clone(); } 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; } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/Message.java0000644000175000017500000006252511647260573024067 0ustar moellermoeller package org.jgroups; import org.jgroups.conf.ClassConfigurator; import org.jgroups.logging.Log; import org.jgroups.logging.LogFactory; import org.jgroups.util.Buffer; import org.jgroups.util.Headers; import org.jgroups.util.Streamable; import org.jgroups.util.Util; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.util.Map; /** * 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 */ public class Message implements Streamable { protected Address dest_addr; protected Address src_addr; /** The payload */ private byte[] buf; /** The index into the payload (usually 0) */ protected int offset; /** The number of bytes in the buffer (usually buf.length is buf not equal to null). */ protected int length; /** All headers are placed here */ protected Headers headers; private volatile byte flags; private volatile byte transient_flags; // transient_flags is neither marshalled nor copied protected static final Log log=LogFactory.getLog(Message.class); static final byte DEST_SET = 1 << 0; static final byte SRC_SET = 1 << 1; static final byte BUF_SET = 1 << 2; // =============================== Flags ==================================== public static final byte OOB = 1 << 0; // message is out-of-band public static final byte DONT_BUNDLE = 1 << 1; // don't bundle message at the transport public static final byte NO_FC = 1 << 2; // bypass flow control public static final byte SCOPED = 1 << 3; // when a message has a scope // the following 2 flags might get removed in 3.x, when https://jira.jboss.org/browse/JGRP-1250 is resolved public static final byte NO_RELIABILITY = 1 << 4; // bypass UNICAST(2) and NAKACK public static final byte NO_TOTAL_ORDER = 1 << 5; // bypass total order (e.g. SEQUENCER) public static final byte NO_RELAY = 1 << 6; // bypass relaying (RELAY) // =========================== Transient flags ============================== public static final byte OOB_DELIVERED = 1 << 0; // OOB which has already been delivered up the stack /** 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 marshalled into the byte buffer. Obj to be serializable (e.g. implementing * Serializable, Externalizable or Streamable, or be a basic type (e.g. Integer, Short etc)).! * 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().

*/ public Message(Address dest, Address src, Object 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. Parameter * 'obj' has to be serializable (e.g. implementing Serializable, Externalizable or Streamable, or be a basic * type (e.g. Integer, Short etc)). */ final public void setObject(Object obj) { if(obj == null) return; try { byte[] tmp=Util.objectToByteBuffer(obj); setBuffer(tmp); } catch(Exception ex) { throw new IllegalArgumentException(ex); } } /** * Uses custom 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); flags &= ~flag; } public boolean isFlagSet(byte flag) { return isFlagSet(flags, flag); } /** * Same as {@link #setFlag(byte)} but transient flags are never marshalled * @param flag */ public void setTransientFlag(byte flag) { if(flag > Byte.MAX_VALUE || flag < 0) throw new IllegalArgumentException("flag has to be >= 0 and <= " + Byte.MAX_VALUE); transient_flags |= flag; } /** * Atomically checks if a given flag is set and - if not - sets it. When multiple threads concurrently call this * method with the same flag, only one of them will be able to set the flag * @param flag * @return True if the flag could be set, false if not (was already set) */ public boolean setTransientFlagIfAbsent(byte flag) { if(flag > Byte.MAX_VALUE || flag < 0) throw new IllegalArgumentException("flag has to be >= 0 and <= " + Byte.MAX_VALUE); synchronized(this) { if(isTransientFlagSet(flag)) return false; else setTransientFlag(flag); return true; } } public void clearTransientFlag(byte flag) { if(flag > Byte.MAX_VALUE || flag < 0) throw new IllegalArgumentException("flag has to be >= 0 and <= " + Byte.MAX_VALUE); transient_flags &= ~flag; } public boolean isTransientFlagSet(byte flag) { return isFlagSet(transient_flags, flag); } protected static boolean isFlagSet(byte flags, byte flag) { return (flags & flag) == flag; } public byte getFlags() { return flags; } public byte getTransientFlags() { return transient_flags; } public void setScope(short scope) { Util.setScope(this, scope); } public short getScope() { return Util.getScope(this); } /*---------------------- Used by protocol layers ----------------------*/ /** Puts a header given an ID into the hashmap. Overwrites potential existing entry. */ public void putHeader(short id, Header hdr) { if(id < 0) throw new IllegalArgumentException("An ID of " + id + " is invalid"); headers.putHeader(id, hdr); } /** * Puts a header given a key into the map, only if the key doesn't exist yet * @param id * @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(short id, Header hdr) { if(id <= 0) throw new IllegalArgumentException("An ID of " + id + " is invalid"); return headers.putHeaderIfAbsent(id, hdr); } /** * * @param key * @return the header associated 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(short id) { return getHeader(id); } public Header getHeader(short id) { if(id <= 0) throw new IllegalArgumentException("An ID of " + id + " is invalid. Add the protocol which calls " + "getHeader() to jg-protocol-ids.xml"); return headers.getHeader(id); } /*---------------------------------------------------------------------*/ 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) { return copy(copy_buffer, 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 * @param copy_headers Copy the headers * @return Message with specified data */ public Message copy(boolean copy_buffer, boolean copy_headers) { 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=copy_headers? createHeaders(headers) : createHeaders(3); return retval; } /** * Doesn't copy any headers except for those with ID >= copy_headers_above * @param copy_buffer * @param starting_id * @return A message with headers whose ID are >= starting_id */ public Message copy(boolean copy_buffer, short starting_id) { Message retval=copy(copy_buffer, false); if(starting_id > 0) { for(Map.Entry entry: getHeaders().entrySet()) { short id=entry.getKey(); if(id >= starting_id) retval.putHeader(id, entry.getValue()); } } 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(flags)); if(transient_flags > 0) ret.append(", transient_flags=" + transientFlagsToString(transient_flags)); 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 ""; } } public String printObjectHeaders() { return headers.printObjectHeaders(); } /* ----------------------------------- 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(dest_addr != null) leading=Util.setFlag(leading, DEST_SET); if(src_addr != null) leading=Util.setFlag(leading, SRC_SET); if(buf != null) leading=Util.setFlag(leading, BUF_SET); // 1. write the leading byte first out.write(leading); // 2. the flags (e.g. OOB, LOW_PRIO) out.write(flags); // 3. dest_addr if(dest_addr != null) Util.writeAddress(dest_addr, out); // 4. src_addr if(src_addr != null) Util.writeAddress(src_addr, out); // 5. buf if(buf != null) { out.writeInt(length); out.write(buf, offset, length); } // 6. headers int size=headers.size(); out.writeShort(size); final short[] ids=headers.getRawIDs(); final Header[] hdrs=headers.getRawHeaders(); for(int i=0; i < ids.length; i++) { if(ids[i] > 0) { out.writeShort(ids[i]); writeHeader(hdrs[i], out); } } } /** * Writes the message to the output stream, but excludes the dest and src addresses unless the src address given * as argument is different from the message's src address * @param src * @param out * @throws IOException */ public void writeToNoAddrs(Address src, DataOutputStream out) throws IOException { byte leading=0; boolean write_src_addr=src == null || src_addr != null && !src_addr.equals(src); if(write_src_addr) leading=Util.setFlag(leading, SRC_SET); if(buf != null) leading=Util.setFlag(leading, BUF_SET); // 1. write the leading byte first out.write(leading); // 2. the flags (e.g. OOB, LOW_PRIO) out.write(flags); // 4. src_addr if(write_src_addr) Util.writeAddress(src_addr, out); // 5. buf if(buf != null) { out.writeInt(length); out.write(buf, offset, length); } // 6. headers int size=headers.size(); out.writeShort(size); final short[] ids=headers.getRawIDs(); final Header[] hdrs=headers.getRawHeaders(); for(int i=0; i < ids.length; i++) { if(ids[i] > 0) { out.writeShort(ids[i]); writeHeader(hdrs[i], out); } } } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { // 1. read the leading byte first byte leading=in.readByte(); // 2. the flags flags=in.readByte(); // 3. dest_addr if(Util.isFlagSet(leading, DEST_SET)) dest_addr=Util.readAddress(in); // 4. src_addr if(Util.isFlagSet(leading, SRC_SET)) src_addr=Util.readAddress(in); // 5. buf if(Util.isFlagSet(leading, BUF_SET)) { int len=in.readInt(); buf=new byte[len]; in.readFully(buf, 0, len); length=len; } // 6. headers int len=in.readShort(); headers=createHeaders(len); short[] ids=headers.getRawIDs(); Header[] hdrs=headers.getRawHeaders(); for(int i=0; i < len; i++) { short id=in.readShort(); Header hdr=readHeader(in); ids[i]=id; hdrs[i]=hdr; } } /* --------------------------------- End of Interface Streamable ----------------------------- */ /** * 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 if(dest_addr != null) retval+=Util.size(dest_addr); if(src_addr != null) retval+=Util.size(src_addr); if(buf != null) retval+=Global.INT_SIZE // length (integer) + length; // number of bytes in the buffer retval+=Global.SHORT_SIZE; // number of headers retval+=headers.marshalledSize(); return retval; } /* ----------------------------------- Private methods ------------------------------- */ public static String flagsToString(byte flags) { StringBuilder sb=new StringBuilder(); boolean first=true; if(isFlagSet(flags, OOB)) { first=false; sb.append("OOB"); } if(isFlagSet(flags, DONT_BUNDLE)) { if(!first) sb.append("|"); else first=false; sb.append("DONT_BUNDLE"); } if(isFlagSet(flags, NO_FC)) { if(!first) sb.append("|"); else first=false; sb.append("NO_FC"); } if(isFlagSet(flags, SCOPED)) { if(!first) sb.append("|"); else first=false; sb.append("SCOPED"); } if(isFlagSet(flags, NO_RELIABILITY)) { if(!first) sb.append("|"); else first=false; sb.append("NO_RELIABILITY"); } if(isFlagSet(flags, NO_TOTAL_ORDER)) { if(!first) sb.append("|"); else first=false; sb.append("NO_TOTAL_ORDER"); } if(isFlagSet(flags, NO_RELAY)) { if(!first) sb.append("|"); else first=false; sb.append("NO_RELAY"); } return sb.toString(); } public static String transientFlagsToString(byte flags) { StringBuilder sb=new StringBuilder(); if(isFlagSet(flags, OOB_DELIVERED)) sb.append("OOB_DELIVERED"); return sb.toString(); } private static void writeHeader(Header hdr, DataOutputStream out) throws IOException { short magic_number=ClassConfigurator.getMagicNumber(hdr.getClass()); out.writeShort(magic_number); hdr.writeTo(out); } private static Header readHeader(DataInputStream in) throws IOException { try { short magic_number=in.readShort(); Class clazz=ClassConfigurator.get(magic_number); if(clazz == null) throw new IllegalArgumentException("magic number " + magic_number + " is not available in magic map"); Header hdr=(Header)clazz.newInstance(); hdr.readFrom(in); return hdr; } catch(Exception ex) { IOException io_ex=new IOException("failed reading header"); io_ex.initCause(ex); throw io_ex; } } 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 ---------------------------- */ } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/MessageListener.java0000644000175000017500000000155211647260573025566 0ustar moellermoeller 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); } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/PhysicalAddress.java0000644000175000017500000000024011647260573025547 0ustar moellermoellerpackage org.jgroups; /** * Represents a physical (as opposed to logical) address * @author Bela Ban */ public interface PhysicalAddress extends Address { } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/Receiver.java0000644000175000017500000000033111647260573024232 0ustar moellermoellerpackage org.jgroups; /** * Defines the callbacks that are invoked when messages, views etc are received on a channel * @author Bela Ban */ public interface Receiver extends MessageListener, MembershipListener { } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/ReceiverAdapter.java0000644000175000017500000000060011647260573025532 0ustar moellermoellerpackage org.jgroups; /** * @author Bela Ban */ public class ReceiverAdapter implements Receiver { public void receive(Message msg) { } public byte[] getState() { return null; } public void setState(byte[] state) { } public void viewAccepted(View view) { } public void suspect(Address mbr) { } public void block() { } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/SetStateEvent.java0000644000175000017500000000112711647260573025230 0ustar moellermoeller package org.jgroups; /** * Encapsulates a state returned by Channel.receive(), as requested by * Channel.getState(s) previously. * @author Bela Ban */ 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 + ']';} } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/StateTransferException.java0000644000175000017500000000113111647260573027131 0ustar moellermoellerpackage 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); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/StreamingGetStateEvent.java0000644000175000017500000000436011647260573027070 0ustar moellermoellerpackage 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; } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/StreamingSetStateEvent.java0000644000175000017500000000412711647260573027105 0ustar moellermoellerpackage 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; } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/SuspectEvent.java0000644000175000017500000000054511647260573025125 0ustar moellermoeller 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";} } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/SuspectedException.java0000644000175000017500000000066511647260573026316 0ustar moellermoeller 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";} } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/TimeoutException.java0000644000175000017500000000064011647260573025776 0ustar moellermoeller 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(); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/Transport.java0000644000175000017500000000125011647260573024463 0ustar moellermoeller 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; } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/UnblockEvent.java0000644000175000017500000000026711647260573025075 0ustar moellermoellerpackage org.jgroups; /** * Trivial object that represents a block event. * @author Bela Ban */ public class UnblockEvent { public String toString() {return "UnblockEvent";} } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/UpHandler.java0000644000175000017500000000037211647260573024355 0ustar moellermoeller 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); } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/Version.java0000644000175000017500000000741411647260573024124 0ustar moellermoeller 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 * Holds version information for JGroups. */ @Immutable public class Version { public static final short major = 2; public static final short minor = 12; public static final short micro = 2; public static final String description="2.12.2.Final"; public static final short version=encode(major, minor, micro); public static final String string_version=print(version); 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); } /** * Returns the catenation of the description and cvs fields. * @return String with description */ public static String printDescription() { return "JGroups " + description; } /** * 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(version == ver) return true; short tmp_major=(short)((ver & MAJOR_MASK) >> MAJOR_SHIFT); short tmp_minor=(short)((ver & MINOR_MASK) >> MINOR_SHIFT); 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; } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/View.java0000644000175000017500000002222511647260573023406 0ustar moellermoeller 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; import java.util.Collection; /** * 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 addresses 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 */ 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; @Deprecated 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; } public View(ViewId vid, Collection
members) { this.vid=vid; this.members=new Vector
(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, Collection
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; } public ViewId getViewId() {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(); } public View copy() { ViewId vid2=vid != null ? (ViewId)vid.clone() : null; Vector
members2=members != null ? new Vector
(members) : null; return new View(vid2, members2); } /** * 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 * @deprecated Will be removed in 3.0 */ public void addPayload(String key, Object value) { if(payload == null) { payload=new HashMap(7); } payload.put(key, value); } /** * * @param key * @return * @deprecated Will be removed in 3.0 */ public Object removePayload(String key) { return payload != null? payload.remove(key) : null; } /** * * @param key * @return * @deprecated Will be removed in 3.0 */ 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); } } @SuppressWarnings("unchecked") 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); } } @SuppressWarnings("unchecked") 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; } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/ViewId.java0000644000175000017500000000725411647260573023670 0ustar moellermoeller 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; } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/annotations/0000755000175000017500000000000011647260573024163 5ustar moellermoellerlibjgroups-java-2.12.2.Final.orig/src/org/jgroups/annotations/DeprecatedProperty.java0000644000175000017500000000050011647260573030626 0ustar moellermoellerpackage org.jgroups.annotations; import java.lang.annotation.*; /** * * Represents an array of deprecated Protocol properties * * * @author Vladimir Blagojevic */ @Retention(RetentionPolicy.RUNTIME) @Target( { ElementType.TYPE}) public @interface DeprecatedProperty { String [] names() default""; } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/annotations/Experimental.java0000644000175000017500000000102111647260573027455 0ustar moellermoellerpackage org.jgroups.annotations; import java.lang.annotation.Target; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** * Elements annotated with this annotation are experimental and may get removed from the distribution at any time * @author Bela Ban */ @Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.PACKAGE}) @Retention(RetentionPolicy.RUNTIME) public @interface Experimental { String comment() default ""; }libjgroups-java-2.12.2.Final.orig/src/org/jgroups/annotations/GuardedBy.java0000644000175000017500000000041711674476335026703 0ustar moellermoeller/* 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(); } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/annotations/Immutable.java0000644000175000017500000000037711674476335026761 0ustar moellermoeller/* 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 { } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/annotations/LocalAddress.java0000644000175000017500000000100411647260573027361 0ustar moellermoellerpackage org.jgroups.annotations; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * This is an assertion, checked at startup time. It ensures that the field this annotation is on is (1) an InetAddress * and (2) a valid address on a local network interface * @author Bela Ban * @since 2.11.2 */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface LocalAddress { } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/annotations/MBean.java0000755000175000017500000000135411647260573026016 0ustar moellermoellerpackage org.jgroups.annotations; import java.lang.annotation.*; /** * Optional annotation that exposes all public methods in the class * hierarchy (excluding Object) as MBean operations. All methods * are exposed if and only if exposeAll attribute is true. *

* * If a more fine grained MBean attribute and operation exposure is needed * do not use @MBean annotation but annotate fields and public methods directly * using @ManagedOperation and @ManagedAttribute annotations. * * * @author Chris Mills */ @Retention(RetentionPolicy.RUNTIME) @Target( { ElementType.TYPE }) @Inherited public @interface MBean { String objectName() default ""; boolean exposeAll() default false; String description() default ""; } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/annotations/ManagedAttribute.java0000755000175000017500000000126211647260573030252 0ustar moellermoellerpackage org.jgroups.annotations; import java.lang.annotation.*; /** * Indicates that a public method or a field (any visibility) in * an MBean class defines an MBean attribute. This annotation can * be applied to either a field or a public setter and/or getter * method of a public class that is itself is optionally annotated * with an @MBean annotation, or inherits such an annotation from * a superclass. * * @author Chris Mills */ @Retention(RetentionPolicy.RUNTIME) @Target( { ElementType.METHOD, ElementType.FIELD }) public @interface ManagedAttribute { String description() default ""; String name() default ""; boolean writable() default false; } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/annotations/ManagedOperation.java0000755000175000017500000000102311647260573030242 0ustar moellermoellerpackage org.jgroups.annotations; import java.lang.annotation.*; /** * Indicates that a method in an MBean class defines an MBean * operation. @ManagedOperation annotation can be applied to a * public method of a public class that is itself optionally * annotated with an @MBean annotation, or inherits such an * annotation from a superclass. * * @author Chris Mills */ @Retention(RetentionPolicy.RUNTIME) @Target( { ElementType.METHOD }) public @interface ManagedOperation { String description() default ""; } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/annotations/Property.java0000644000175000017500000000403311647260573026652 0ustar moellermoellerpackage org.jgroups.annotations; import java.lang.annotation.*; import org.jgroups.conf.PropertyConverter; import org.jgroups.conf.PropertyConverters; /** * Represents a Protocol property assigned from corresponding field in JGroups * properties file. * *

* Since all protocol properties are read as String instances from properties * file properties need to be converted to an appropriate field type of a * matching Protocol instance. JGroups supplies set of converters in * {@link PropertyConverters} class. * *

* Third parties can provide their own converters if such need arises by * implementing {@link PropertyConverter} interface and by specifying that * converter as converter on a specific Property annotation of a field or a * method instance. * *

* Property annotation can decorate either a field or a method of a Property * class. If a method is decorated with Property annotation it is assumed that * such a method is a setter with only one parameter type that a specified * converter can convert from a String to an actual type. * * * @author Vladimir Blagojevic */ @Retention(RetentionPolicy.RUNTIME) @Target( { ElementType.METHOD, ElementType.FIELD }) public @interface Property { String name() default ""; String description() default ""; String deprecatedMessage() default ""; Class converter() default PropertyConverters.Default.class; String dependsUpon() default ""; String[] systemProperty() default {}; /** * Global.NON_LOOPBACK_ADDRESS means pick any valid non-loopback IPv4 address */ String defaultValueIPv4() default "" ; /** * Global.NON_LOOPBACK_ADDRESS means pick any valid non-loopback IPv6 address */ String defaultValueIPv6() default "" ; /** Expose this property also as a managed attribute */ boolean exposeAsManagedAttribute() default true; /* Should this managed attribute be writeable ? If set to true, automatically sets exposeAsManagedAttribute to true */ boolean writable() default true; } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/annotations/Unsupported.java0000644000175000017500000000077511647260573027367 0ustar moellermoellerpackage org.jgroups.annotations; import java.lang.annotation.Target; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** * Elements annotated with this annotation are unsupported and may get removed from the distribution at any time * @author Bela Ban */ @Target({ElementType.TYPE, ElementType.METHOD, ElementType.PACKAGE}) @Retention(RetentionPolicy.RUNTIME) public @interface Unsupported { String comment() default ""; } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/auth/0000755000175000017500000000000011647260573022567 5ustar moellermoellerlibjgroups-java-2.12.2.Final.orig/src/org/jgroups/auth/AuthToken.java0000644000175000017500000000253511647260573025341 0ustar moellermoellerpackage org.jgroups.auth; import java.io.Serializable; import org.jgroups.Message; import org.jgroups.logging.Log; import org.jgroups.logging.LogFactory; import org.jgroups.protocols.AUTH; import org.jgroups.util.Streamable; /** * 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 transient AUTH auth = null; public void setAuth(AUTH auth) { this.auth = auth; } public void init() {} /** * 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(); /** * 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); } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/auth/FixedMembershipToken.java0000644000175000017500000001203011647260573027502 0ustar moellermoeller/* * 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 java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.StringTokenizer; import org.jgroups.Event; import org.jgroups.Message; import org.jgroups.PhysicalAddress; import org.jgroups.annotations.Property; import org.jgroups.util.Util; /** *

* 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 List memberList = null; private String token = "emptyToken"; @Property private String fixed_members_seperator = ","; private static final long serialVersionUID = 4717069536900221681L; public FixedMembershipToken() { } public String getName() { return "org.jgroups.auth.FixedMembershipToken"; } @Property public void setFixedMembersSeparator(String value) { fixed_members_seperator = value; } public boolean authenticate(AuthToken token, Message msg) { if ((token != null) && (token instanceof FixedMembershipToken) && (this.memberList != null)) { PhysicalAddress src = (PhysicalAddress) auth.down(new Event(Event.GET_PHYSICAL_ADDRESS, msg.getSrc())); if (src == null) { if (log.isErrorEnabled()) log.error("didn't find physical address for " + msg.getSrc()); return false; } 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(":"); } @Property(name = "fixed_members_value") public void setMemberList(String list) { memberList = new ArrayList(); StringTokenizer memberListTokenizer = new StringTokenizer(list, fixed_members_seperator); while (memberListTokenizer.hasMoreTokens()) { memberList.add(memberListTokenizer.nextToken().replace('/', ':')); } } /** * 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); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/auth/MD5Token.java0000644000175000017500000000741511647260573025027 0ustar moellermoellerpackage org.jgroups.auth; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import org.jgroups.Message; import org.jgroups.annotations.Property; import org.jgroups.util.Util; /** *

* 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 { @Property private String auth_value = null; @Property(name = "token_hash") private String hash_type = "MD5"; private static final long serialVersionUID = -5787154335375249191L; public MD5Token() { // need an empty constructor } public MD5Token(String authvalue) { this.auth_value = hash(authvalue); } public MD5Token(String authvalue, String hash_type) { this.auth_value = hash(authvalue); this.hash_type = hash_type; } public String getHashType() { return hash_type; } public void setHashType(String hash_type) { this.hash_type = hash_type; } public String getAuthValue() { return auth_value; } public void setAuthValue(String auth_value) { this.auth_value = auth_value; } 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.auth_value != null) && (serverToken.auth_value != null) && (this.auth_value.equalsIgnoreCase(serverToken.auth_value))) { // 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.auth_value, out); } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { if (log.isDebugEnabled()) { log.debug("MD5Token readFrom()"); } this.auth_value = Util.readString(in); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/auth/RegexMembership.java0000644000175000017500000001055211647260573026523 0ustar moellermoeller/* * 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.Address; import org.jgroups.Event; import org.jgroups.Message; import org.jgroups.PhysicalAddress; import org.jgroups.annotations.Property; import org.jgroups.util.UUID; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Matches the IP address or logical name of a joiner against a regular expression and accepts or rejects based on * pattern matching * @author Bela Ban */ public class RegexMembership extends AuthToken { @Property(description="The regular expression against which the IP address or logical host of a joiner will be matched") protected String match_string=null; @Property(description="Matches the IP address of the joiner against the match string") protected boolean match_ip_address=true; @Property(description="Matches the logical name of the joiner against the match string") protected boolean match_logical_name=false; // ------------------------------------------- Fields ------------------------------------------------------ // protected Pattern pattern; private static final long serialVersionUID=4717069536900221681L; public RegexMembership() { } public String getName() { return "org.jgroups.auth.RegexMembership"; } public void init() { super.init(); if(!match_ip_address && !match_logical_name) throw new IllegalArgumentException("either match_ip_address or match_logical_address has to be true"); if(match_string == null) throw new IllegalArgumentException("match_string cannot be null"); pattern=Pattern.compile(match_string); } public boolean authenticate(AuthToken token, Message msg) { Address sender=msg.getSrc(); if(match_ip_address) { PhysicalAddress src=sender != null? (PhysicalAddress)auth.down(new Event(Event.GET_PHYSICAL_ADDRESS, sender)) : null; String ip_addr=src != null? src.toString() : null; if(ip_addr != null) { Matcher matcher=pattern.matcher(ip_addr); boolean result=matcher.matches(); if(log.isTraceEnabled()) log.trace("matching ip_address: pattern= " + pattern + ", input= " + ip_addr + ", result= " + result); if(result) return true; } } if(match_logical_name) { String logical_name=sender != null? UUID.get(sender) : null; if(logical_name != null) { Matcher matcher=pattern.matcher(logical_name); boolean result=matcher.matches(); if(log.isTraceEnabled()) log.trace("matching logical_name: pattern= " + pattern + ", input= " + logical_name + ", result= " + result); if(result) return true; } } return false; } public void writeTo(DataOutputStream out) throws IOException { } /** * Required to deserialize the object when read in from the wire * @param in * @throws java.io.IOException * @throws IllegalAccessException * @throws InstantiationException */ public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/auth/SimpleToken.java0000644000175000017500000000560711647260573025674 0ustar moellermoellerpackage org.jgroups.auth; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import org.jgroups.Message; import org.jgroups.annotations.Property; import org.jgroups.util.Util; /** *

* 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
  • *
* * @author Chris Mills * @see org.jgroups.auth.AuthToken */ public class SimpleToken extends AuthToken { @Property private String auth_value = null; private static final long serialVersionUID = 5020668015439045326L; public SimpleToken() { // need an empty constructor } public SimpleToken(String authvalue) { this.auth_value = authvalue; } public String getName() { return "org.jgroups.auth.SimpleToken"; } public String getAuthValue() { return auth_value; } public void setAuthValue(String auth_value) { this.auth_value = auth_value; } 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.auth_value != null) && (serverToken.auth_value != null) && (this.auth_value.equalsIgnoreCase(serverToken.auth_value))) { // 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.auth_value, 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.auth_value = Util.readString(in); } }libjgroups-java-2.12.2.Final.orig/src/org/jgroups/auth/X509Token.java0000644000175000017500000001615011647260573025103 0ustar moellermoellerpackage org.jgroups.auth; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.security.InvalidKeyException; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.UnrecoverableEntryException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import org.jgroups.Message; import org.jgroups.annotations.Property; import org.jgroups.util.Util; /** *

* 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
  • *
* * @author Chris Mills * @see org.jgroups.auth.AuthToken */ 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; @Property private String keystore_type = "JKS"; @Property private String cert_alias = null; @Property private String keystore_path = null; @Property private String auth_value = null; @Property private String cipher_type = "RSA"; 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 = -514501306160844271L; public X509Token() { // need an empty constructor } @Property(name = "cert_password") public void setCertPassword(String pwd) { this.cert_password = pwd.toCharArray(); } @Property(name = "keystore_password") public void setKeyStorePassword(String pwd) { this.keystore_password = pwd.toCharArray(); if (cert_password == null) cert_password = keystore_password; } 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.equalsIgnoreCase(this.auth_value))) { if (log.isDebugEnabled()) { log.debug("X509 authentication passed"); } return true; } } catch (Exception e) { if (log.isFatalEnabled()) { log.fatal(e.toString()); } } } // 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 */ public void setCertificate() throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, UnrecoverableEntryException { 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.auth_value.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(); this.valueSet=true; if (log.isDebugEnabled()) { log.debug("certPrivateKey = " + this.certPrivateKey.toString()); } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/blocks/0000755000175000017500000000000011647260573023103 5ustar moellermoellerlibjgroups-java-2.12.2.Final.orig/src/org/jgroups/blocks/AbstractConnectionMap.java0000644000175000017500000001551011647260573030171 0ustar moellermoellerpackage org.jgroups.blocks; import org.jgroups.logging.Log; import org.jgroups.logging.LogFactory; import org.jgroups.Address; import org.jgroups.Global; import org.jgroups.util.ThreadFactory; import org.jgroups.util.Util; import java.util.*; import java.util.Map.Entry; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public abstract class AbstractConnectionMap implements ConnectionMap { protected final Vector> conn_listeners=new Vector>(); protected final Map conns=new HashMap(); protected final Lock lock = new ReentrantLock(); protected final ThreadFactory factory; protected final long reaperInterval; protected final Reaper reaper; protected final Log log=LogFactory.getLog(getClass()); public AbstractConnectionMap(ThreadFactory factory) { this(factory,0); } public AbstractConnectionMap(ThreadFactory factory,long reaperInterval) { super(); this.factory=factory; this.reaperInterval = reaperInterval; if(reaperInterval > 0){ reaper = new Reaper(); }else{ reaper = null; } } public Lock getLock(){ return lock; } public boolean hasOpenConnection(Address address) { lock.lock(); try { V v=conns.get(address); return v != null && v.isOpen(); } finally { lock.unlock(); } } public boolean hasConnection(Address address) { lock.lock(); try { return conns.containsKey(address); } finally { lock.unlock(); } } public void addConnection(Address address, V conn) { lock.lock(); try { V previous = conns.put(address, conn); Util.close(previous); } finally { lock.unlock(); } notifyConnectionOpened(address, conn); } public void addConnectionMapListener(ConnectionMapListener cml) { if(cml != null && !conn_listeners.contains(cml)) conn_listeners.addElement(cml); } public int getNumConnections() { lock.lock(); try { return conns.size(); } finally { lock.unlock(); } } public String printConnections() { StringBuilder sb=new StringBuilder(); lock.lock(); try { for(Map.Entry entry: conns.entrySet()) { sb.append(entry.getKey()).append(": ").append(entry.getValue()).append("\n"); } } finally { lock.unlock(); } return sb.toString(); } public ThreadFactory getThreadFactory() { return factory; } public void removeConnection(Address address) { Connection conn = null; lock.lock(); try { conn=conns.remove(address); } finally { lock.unlock(); } Util.close(conn); } public void removeConnectionMapListener(ConnectionMapListener cml) { if(cml != null) conn_listeners.removeElement(cml); } /** * Removes all connections which are not in current_mbrs * * @param current_mbrs */ public void retainAll(Collection
current_mbrs) { if(current_mbrs == null) return; Map copy=null; lock.lock(); try { copy=new HashMap(conns); conns.keySet().retainAll(current_mbrs); } finally { lock.unlock(); } copy.keySet().removeAll(current_mbrs); for(Iterator> i = copy.entrySet().iterator();i.hasNext();) { Entry e = i.next(); Util.close(e.getValue()); } copy.clear(); } public void start() throws Exception { if(reaper != null) { reaper.start(); } } public void stop() { if(reaper != null) { reaper.stop(); } lock.lock(); try { for(Iterator> i = conns.entrySet().iterator();i.hasNext();) { Entry e = i.next(); Util.close(e.getValue()); } clear(); } finally { lock.unlock(); } conn_listeners.clear(); } protected void clear() { lock.lock(); try { conns.clear(); } finally { lock.unlock(); } } protected void notifyConnectionClosed(Address address) { for(ConnectionMapListener l:conn_listeners) { l.connectionClosed(address); } } protected void notifyConnectionOpened(Address address, V conn) { for(ConnectionMapListener l:conn_listeners) { l.connectionOpened(address, conn); } } class Reaper implements Runnable { private Thread thread; public synchronized void start() { if(thread == null || !thread.isAlive()) { thread=factory.newThread(new Reaper(), "Reaper"); thread.start(); } } public synchronized void stop() { if(thread != null && thread.isAlive()) { thread.interrupt(); try { thread.join(Global.THREAD_SHUTDOWN_WAIT_TIME); } catch(InterruptedException ignored) { } } thread=null; } public void run() { while(!Thread.currentThread().isInterrupted()) { lock.lock(); try { for(Iterator> it=conns.entrySet().iterator();it.hasNext();) { Entry entry=it.next(); V c=entry.getValue(); if(c.isExpired(System.currentTimeMillis())) { log.info("Connection " + c.toString() + " reaped"); Util.close(c); it.remove(); } } } finally { lock.unlock(); } Util.sleep(reaperInterval); } } } public interface ConnectionMapListener { public void connectionClosed(Address address); public void connectionOpened(Address address, V conn); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/blocks/BasicConnectionTable.java0000644000175000017500000007315111647260573027766 0ustar moellermoellerpackage org.jgroups.blocks; import org.jgroups.logging.Log; import org.jgroups.logging.LogFactory; import org.jgroups.Address; import org.jgroups.Global; import org.jgroups.Version; import org.jgroups.stack.IpAddress; import org.jgroups.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 */ 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 SocketFactory socket_factory=new DefaultSocketFactory(); /** * 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 SocketFactory getSocketFactory() { return socket_factory; } public void setSocketFactory(SocketFactory socket_factory) { this.socket_factory=socket_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 { ServerSocket tmp=srv_sock; srv_sock=null; socket_factory.close(tmp); 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 + ": " + 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").toString()); } 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(); 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; } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/blocks/Cache.java0000644000175000017500000002420111647260573024750 0ustar moellermoellerpackage org.jgroups.blocks; import org.jgroups.logging.Log; import org.jgroups.logging.LogFactory; import org.jgroups.annotations.Experimental; import org.jgroups.annotations.Unsupported; import org.jgroups.annotations.ManagedAttribute; import org.jgroups.annotations.ManagedOperation; import org.jgroups.util.Util; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicBoolean; import java.util.*; import java.io.*; /** * Simple cache which maintains keys and value. A reaper can be enabled which periodically evicts expired entries. * Also, when the cache is configured to be bounded, entries in excess of the max size will be evicted on put(). * @author Bela Ban */ @Experimental @Unsupported public class Cache { private static final Log log=LogFactory.getLog(Cache.class); private final ConcurrentMap> map=Util.createConcurrentMap(); private ScheduledThreadPoolExecutor timer=new ScheduledThreadPoolExecutor(1); private Future task=null; private final AtomicBoolean is_reaping=new AtomicBoolean(false); private Set change_listeners=new HashSet(); /** The maximum number of keys, When this value is exceeded we evict older entries, until we drop below this * mark again. This effectively maintains a bounded cache. A value of 0 means don't bound the cache. */ @ManagedAttribute(writable=true) private int max_num_entries=0; public int getMaxNumberOfEntries() { return max_num_entries; } public void setMaxNumberOfEntries(int max_num_entries) { this.max_num_entries=max_num_entries; } public void addChangeListener(ChangeListener l) { change_listeners.add(l); } public void removeChangeListener(ChangeListener l) { change_listeners.remove(l); } @ManagedAttribute public int getSize() { return map.size(); } @ManagedAttribute public boolean isReapingEnabled() { return task != null && !task.isCancelled(); } /** Runs the reaper every interval ms, evicts expired items */ @ManagedOperation public void enableReaping(long interval) { if(task != null) task.cancel(false); task=timer.scheduleWithFixedDelay(new Reaper(), 0, interval, TimeUnit.MILLISECONDS); } @ManagedOperation public void disableReaping() { if(task != null) { task.cancel(false); task=null; } } @ManagedOperation public void start() { if(timer == null) timer=new ScheduledThreadPoolExecutor(1); } @ManagedOperation public void stop() { if(timer != null) timer.shutdown(); timer=null; } /** * * @param key * @param val * @param caching_time Number of milliseconds to keep an entry in the cache. -1 means don't cache (if reaping * is enabled, we'll evict an entry with -1 caching time), 0 means never evict. In the latter case, we can still * evict an entry with 0 caching time: when we have a bounded cache, we evict in order of insertion no matter * what the caching time is. */ @ManagedOperation public V put(K key, V val, long caching_time) { if(log.isTraceEnabled()) log.trace("put(" + key + ", " + val + ", " + caching_time + ")"); Value value=new Value(val, caching_time); Value retval=map.put(key, value); if(max_num_entries > 0 && map.size() > max_num_entries) { boolean rc=is_reaping.compareAndSet(false, true); if(rc) { if(log.isTraceEnabled()) log.trace("reaping: max_num_entries=" + max_num_entries + ", size=" + map.size()); timer.execute(new Runnable() { public void run() { if(max_num_entries > 0) { try { if(map.size() > max_num_entries) { evict(); // see if we can gracefully evict expired items } if(map.size() > max_num_entries) { // still too many entries: now evict entries based on insertion time: oldest first int diff=map.size() - max_num_entries; // we have to evict diff entries SortedMap tmp=new TreeMap(); for(Map.Entry> entry: map.entrySet()) { tmp.put(entry.getValue().insertion_time, entry.getKey()); } Collection vals=tmp.values(); for(K k: vals) { if(diff-- > 0) { Value v=map.remove(k); if(log.isTraceEnabled()) log.trace("evicting " + k + ": " + v.value); } else break; } } if(log.isTraceEnabled()) log.trace("done reaping (size=" + map.size() + ")"); } finally { is_reaping.set(false); } } } }); } } return retval != null? retval.value : null; } @ManagedOperation public V get(K key) { if(log.isTraceEnabled()) log.trace("get(" + key + ")"); Value val=map.get(key); if(val == null) return null; if(val.timeout == -1 || (val.timeout > 0 && val.timeout < System.currentTimeMillis())) { map.remove(key); return null; } return val.value; } /** * This method should not be used to add or remove elements ! It was just added because ReplCacheDemo * requires it for its data model * @return */ public ConcurrentMap> getInternalMap() { return map; } public Value getEntry(K key) { if(log.isTraceEnabled()) log.trace("getEntry(" + key + ")"); return map.get(key); } public V remove(K key) { if(log.isTraceEnabled()) log.trace("remove(" + key + ")"); Value val=map.remove(key); return val != null? val.value : null; } public Set>> entrySet() { return map.entrySet(); } @ManagedOperation public String toString() { StringBuilder sb=new StringBuilder(); for(Map.Entry> entry: map.entrySet()) { Value val=entry.getValue(); sb.append(entry.getKey()).append(": ").append(entry.getValue().getValue()); sb.append(" (expiration_time: "); long expiration_time=val.getTimeout(); if(expiration_time <= 0) sb.append(expiration_time); else { sb.append(new Date(expiration_time)); } sb.append(")\n"); } return sb.toString(); } public String dump() { StringBuilder sb=new StringBuilder(); for(Map.Entry> entry: map.entrySet()) { sb.append(entry.getKey()).append(": "); V val=entry.getValue().getValue(); if(val != null) { if(val instanceof byte[]) sb.append(" (" + ((byte[])val).length).append(" bytes)"); else sb.append(val); } sb.append("\n"); } return sb.toString(); } private void evict() { boolean evicted=false; for(Iterator>> it=map.entrySet().iterator(); it.hasNext();) { Map.Entry> entry=it.next(); Value val=entry.getValue(); if(val != null) { if(val.timeout == -1 || (val.timeout > 0 && System.currentTimeMillis() > val.insertion_time + val.timeout)) { if(log.isTraceEnabled()) log.trace("evicting " + entry.getKey() + ": " + entry.getValue().value); it.remove(); evicted=true; } } } if(evicted) notifyChangeListeners(); } private void notifyChangeListeners() { for(ChangeListener l: change_listeners) { try { l.changed(); } catch(Throwable t) { if(log.isErrorEnabled()) log.error("failed notifying change listener", t); } } } public static class Value implements Externalizable { private V value; private long insertion_time=System.currentTimeMillis(); /** When the value can be reaped (in ms) */ private transient long timeout; private static final long serialVersionUID=-3445944261826378608L; public Value(V value, long timeout) { this.value=value; this.timeout=timeout; } public Value() { } public V getValue() {return value;} public long getInsertionTime() {return insertion_time;} public long getTimeout() {return timeout;} public void writeExternal(ObjectOutput out) throws IOException { out.writeLong(timeout); out.writeObject(value); } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { insertion_time=System.currentTimeMillis(); timeout=in.readLong(); value=(V)in.readObject(); } } private class Reaper implements Runnable { public void run() { evict(); } } public interface ChangeListener { void changed(); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/blocks/Connection.java0000644000175000017500000000034011647260573026042 0ustar moellermoellerpackage org.jgroups.blocks; import java.io.IOException; public interface Connection{ public boolean isOpen(); public boolean isExpired(long milis); public void close() throws IOException; }libjgroups-java-2.12.2.Final.orig/src/org/jgroups/blocks/ConnectionMap.java0000644000175000017500000000027511647260573026507 0ustar moellermoellerpackage org.jgroups.blocks; import org.jgroups.Address; public interface ConnectionMap { V getConnection(Address dest) throws Exception; } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/blocks/ConnectionTableNIO.java0000644000175000017500000014635011647260573027374 0ustar moellermoeller package org.jgroups.blocks; import org.jgroups.logging.Log; import org.jgroups.Address; import org.jgroups.Global; import org.jgroups.stack.IpAddress; 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. * * @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(); } /** * @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 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) */ BasicConnectionTable.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); 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.toString()); 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 BasicConnectionTable.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; } synchronized 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.toString()); 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); } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/blocks/DistributedLockManager.java0000644000175000017500000007051511647260573030344 0ustar moellermoellerpackage org.jgroups.blocks; import org.jgroups.logging.Log; import org.jgroups.logging.LogFactory; import org.jgroups.ChannelException; import org.jgroups.MembershipListener; import org.jgroups.View; import org.jgroups.Address; import org.jgroups.annotations.Unsupported; 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) * @deprecated Succeessor is {@link org.jgroups.blocks.locking.LockService}. */ @Unsupported @Deprecated 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 IllegalArgumentException("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; } public Object getRequester() { return requester; } /** * 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); } public String toString() { return "lockId=" + lockId + ", requester=" + requester + ", managerId=" + managerId + ", committed=" + commited; } } /** * 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); } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/blocks/DistributedQueue.java0000644000175000017500000005121411647260573027240 0ustar moellermoellerpackage org.jgroups.blocks; import org.jgroups.logging.Log; import org.jgroups.logging.LogFactory; import org.jgroups.*; import org.jgroups.annotations.Unsupported; 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 */ @Unsupported 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.getAddress() : 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(); } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/blocks/DistributedTree.java0000644000175000017500000005511711647260573027061 0ustar moellermoeller package org.jgroups.blocks; import org.jgroups.logging.Log; import org.jgroups.logging.LogFactory; import org.jgroups.*; import org.jgroups.annotations.Unsupported; 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 */ @Unsupported 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(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):" + "FRAG(down_thread=false;up_thread=false):" + "pbcast.GMS(join_timeout=5000;" + "print_local_addr=true):" + "pbcast.STATE_TRANSFER()"; 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.getAddress() : 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); System.out.println("*** nodeCreated(): tmp_fqn is " + tmp_fqn); 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 + ']'; } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/blocks/GridFile.java0000644000175000017500000002720711647260573025443 0ustar moellermoellerpackage org.jgroups.blocks; import org.jgroups.util.Streamable; import org.jgroups.util.Util; import org.jgroups.annotations.Experimental; import java.io.*; import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.Set; /** * Subclass of File to iterate through directories and files in a grid * @author Bela Ban */ @Experimental public class GridFile extends File { private static final long serialVersionUID=-6729548421029004260L; private final ReplCache cache; private final GridFilesystem fs; private final String name; private int chunk_size; GridFile(String pathname, ReplCache cache, int chunk_size, GridFilesystem fs) { super(pathname); this.fs=fs; this.name=trim(pathname); this.cache=cache; this.chunk_size=chunk_size; initMetadata(); } GridFile(String parent, String child, ReplCache cache, int chunk_size, GridFilesystem fs) { super(parent, child); this.fs=fs; this.name=trim(parent + File.separator + child); this.cache=cache; this.chunk_size=chunk_size; initMetadata(); } GridFile(File parent, String child, ReplCache cache, int chunk_size, GridFilesystem fs) { super(parent, child); this.fs=fs; this.name=trim(parent.getAbsolutePath() + File.separator + child); this.cache=cache; this.chunk_size=chunk_size; initMetadata(); } public String getName() { return name; } public String getPath() { String my_path=super.getPath(); if(my_path != null && my_path.endsWith(File.separator)) { int index=my_path.lastIndexOf(File.separator); if(index != -1) my_path=my_path.substring(0, index); } return my_path; } public long length() { Metadata metadata=cache.get(getPath()); if(metadata != null) return metadata.length; return 0; } void setLength(int new_length) { Metadata metadata=cache.get(getPath()); if(metadata != null) { metadata.length=new_length; metadata.setModificationTime(System.currentTimeMillis()); cache.put(getPath(), metadata, (short)-1, 0, false); } else System.err.println("metadata for " + getPath() + " not found !"); } public int getChunkSize() { return chunk_size; } public boolean createNewFile() throws IOException { if(exists()) return true; if(!checkParentDirs(getPath(), false)) return false; cache.put(getPath(), new Metadata(0, System.currentTimeMillis(), chunk_size, Metadata.FILE), (short)-1, 0, true); return true; } public boolean delete() { return delete(false); // asynchronous delete by default } public boolean delete(boolean synchronous) { if(!exists()) return false; if(isFile()) { fs.remove(getPath(), synchronous); // removes all the chunks belonging to the file cache.remove(getPath(), synchronous); // removes the metadata information return true; } if(isDirectory()) { File[] files=listFiles(); if(files != null && files.length > 0) return false; fs.remove(getPath(), synchronous); // removes all the chunks belonging to the file cache.remove(getPath(), synchronous); // removes the metadata information } return true; } public boolean mkdir() { try { boolean parents_exist=checkParentDirs(getPath(), false); if(!parents_exist) return false; cache.put(getPath(), new Metadata(0, System.currentTimeMillis(), chunk_size, Metadata.DIR), (short)-1, 0, true); return true; } catch(IOException e) { e.printStackTrace(); return false; } } public boolean mkdirs() { try { boolean parents_exist=checkParentDirs(getPath(), true); if(!parents_exist) return false; cache.put(getPath(), new Metadata(0, System.currentTimeMillis(), chunk_size, Metadata.DIR), (short)-1, 0, true); return true; } catch(IOException e) { return false; } } public boolean exists() { return cache.get(getPath()) != null; } public String[] list() { return list(null); } public String[] list(FilenameFilter filter) { return _list(filter); } public File[] listFiles() { return listFiles((FilenameFilter)null); } public File[] listFiles(FilenameFilter filter) { return _listFiles(filter); } public File[] listFiles(FileFilter filter) { return _listFiles(filter); } public boolean isDirectory() { Metadata val=cache.get(getPath()); return val.isDirectory(); } public boolean isFile() { Metadata val=cache.get(getPath()); return val.isFile(); } protected void initMetadata() { Metadata metadata=cache.get(getPath()); if(metadata != null) this.chunk_size=metadata.getChunkSize(); } protected File[] _listFiles(Object filter) { String[] files=_list(filter); File[] retval=new File[files.length]; for(int i=0; i < files.length; i++) retval[i]=new GridFile(files[i], cache, chunk_size, fs); return retval; } protected String[] _list(Object filter) { Cache> internal_cache=cache.getL2Cache(); Set keys=internal_cache.getInternalMap().keySet(); if(keys == null) return null; Collection list=new ArrayList(keys.size()); for(String str: keys) { if(isChildOf(getPath(), str)) { if(filter instanceof FilenameFilter && !((FilenameFilter)filter).accept(new File(name), filename(str))) continue; else if(filter instanceof FileFilter && !((FileFilter)filter).accept(new File(str))) continue; list.add(str); } } String[] retval=new String[list.size()]; int index=0; for(String tmp: list) retval[index++]=tmp; return retval; } /** * Verifies whether child is a child (dir or file) of parent * @param parent * @param child * @return True if child is a child, false otherwise */ protected static boolean isChildOf(String parent, String child) { if(parent == null || child == null) return false; if(!child.startsWith(parent)) return false; if(child.length() <= parent.length()) return false; int from=parent.equals(File.separator)? parent.length() : parent.length() +1; // if(from-1 > child.length()) // return false; String[] comps=Util.components(child.substring(from), File.separator); return comps != null && comps.length <= 1; } protected static String filename(String full_path) { String[] comps=Util.components(full_path, File.separator); return comps != null? comps[comps.length -1] : null; } /** * Checks whether the parent directories are present (and are directories). If create_if_absent is true, * creates missing dirs * @param path * @param create_if_absent * @return */ protected boolean checkParentDirs(String path, boolean create_if_absent) throws IOException { String[] components=Util.components(path, File.separator); if(components == null) return false; if(components.length == 1) // no parent directories to create, e.g. "data.txt" return true; StringBuilder sb=new StringBuilder(); boolean first=true; for(int i=0; i < components.length-1; i++) { String tmp=components[i]; if(!tmp.equals(File.separator)) { if(first) first=false; else sb.append(File.separator); } sb.append(tmp); String comp=sb.toString(); if(exists(comp)) { if(isFile(comp)) throw new IOException("cannot create " + path + " as component " + comp + " is a file"); } else { if(create_if_absent) cache.put(comp, new Metadata(0, System.currentTimeMillis(), chunk_size, Metadata.DIR), (short)-1, 0); else return false; } } return true; } protected static String trim(String str) { if(str == null) return null; str=str.trim(); if(str.equals(File.separator)) return str; String[] comps=Util.components(str, File.separator); return comps != null && comps.length > 0? comps[comps.length-1] : null; } private boolean exists(String key) { return cache.get(key) != null; } private boolean isFile(String key) { Metadata val=cache.get(key); return val.isFile(); } public static class Metadata implements Streamable { public static final byte FILE = 1 << 0; public static final byte DIR = 1 << 1; private int length =0; private long modification_time=0; private int chunk_size=0; private byte flags=0; public Metadata() { } public Metadata(int length, long modification_time, int chunk_size, byte flags) { this.length=length; this.modification_time=modification_time; this.chunk_size=chunk_size; this.flags=flags; } public int getLength() { return length; } public void setLength(int length) { this.length=length; } public long getModificationTime() { return modification_time; } public void setModificationTime(long modification_time) { this.modification_time=modification_time; } public int getChunkSize() { return chunk_size; } public boolean isFile() { return Util.isFlagSet(flags, FILE); } public boolean isDirectory() { return Util.isFlagSet(flags, DIR); } public String toString() { boolean is_file=Util.isFlagSet(flags, FILE); StringBuilder sb=new StringBuilder(); sb.append(getType()); if(is_file) sb.append(", len=" + Util.printBytes(length) + ", chunk_size=" + chunk_size); sb.append(", mod_time=" + new Date(modification_time)); return sb.toString(); } public void writeTo(DataOutputStream out) throws IOException { out.writeInt(length); out.writeLong(modification_time); out.writeInt(chunk_size); out.writeByte(flags); } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { length=in.readInt(); modification_time=in.readLong(); chunk_size=in.readInt(); flags=in.readByte(); } private String getType() { if(Util.isFlagSet(flags, FILE)) return "file"; if(Util.isFlagSet(flags, DIR)) return "dir"; return "n/a"; } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/blocks/GridFilesystem.java0000644000175000017500000000734211647260573026706 0ustar moellermoellerpackage org.jgroups.blocks; import org.jgroups.annotations.Experimental; import java.io.*; /** * Entry point for GridFile and GridInputStream / GridOutputStream * @author Bela Ban */ @Experimental public class GridFilesystem { protected final ReplCache data; protected final ReplCache metadata; protected final int default_chunk_size; protected final short default_repl_count; /** * Creates an instance. The data and metadata caches should already have been setup and started * @param data * @param metadata * @param default_chunk_size */ public GridFilesystem(ReplCache data, ReplCache metadata, short default_repl_count, int default_chunk_size) { this.data=data; this.metadata=metadata; this.default_chunk_size=default_chunk_size; this.default_repl_count=default_repl_count; } public GridFilesystem(ReplCache data, ReplCache metadata) { this(data, metadata, (short)1, 8000); } public File getFile(String pathname) { return getFile(pathname, default_chunk_size); } public File getFile(String pathname, int chunk_size) { return new GridFile(pathname, metadata, chunk_size, this); } public File getFile(String parent, String child) { return getFile(parent, child, default_chunk_size); } public File getFile(String parent, String child, int chunk_size) { return new GridFile(parent, child, metadata, chunk_size, this); } public File getFile(File parent, String child) { return getFile(parent, child, default_chunk_size); } public File getFile(File parent, String child, int chunk_size) { return new GridFile(parent, child, metadata, chunk_size, this); } public OutputStream getOutput(String pathname) throws IOException { return getOutput(pathname, false, default_repl_count, default_chunk_size); } public OutputStream getOutput(String pathname, boolean append) throws IOException { return getOutput(pathname, append, default_repl_count, default_chunk_size); } public OutputStream getOutput(String pathname, boolean append, short repl_count, int chunk_size) throws IOException { GridFile file=(GridFile)getFile(pathname, chunk_size); if(!file.createNewFile()) throw new IOException("creation of " + pathname + " failed"); return new GridOutputStream(file, append, data, repl_count, chunk_size); } public OutputStream getOutput(GridFile file) throws IOException { if(!file.createNewFile()) throw new IOException("creation of " + file + " failed"); return new GridOutputStream(file, false, data, default_repl_count, default_chunk_size); } public InputStream getInput(String pathname) throws FileNotFoundException { GridFile file=(GridFile)getFile(pathname); if(!file.exists()) throw new FileNotFoundException(pathname); return new GridInputStream(file, data, default_chunk_size); } public InputStream getInput(File pathname) throws FileNotFoundException { return pathname != null? getInput(pathname.getPath()) : null; } public void remove(String path, boolean synchronous) { if(path == null) return; GridFile.Metadata md=metadata.get(path); if(md == null) return; int num_chunks=md.getLength() / md.getChunkSize() + 1; for(int i=0; i < num_chunks; i++) data.remove(path + ".#" + i, synchronous); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/blocks/GridInputStream.java0000644000175000017500000000736211647260573027037 0ustar moellermoellerpackage org.jgroups.blocks; import org.jgroups.logging.Log; import org.jgroups.logging.LogFactory; import org.jgroups.annotations.Experimental; import java.io.*; /** * @author Bela Ban */ @Experimental public class GridInputStream extends InputStream { final ReplCache cache; final int chunk_size; final String name; protected final GridFile file; // file representing this input stream int index=0; // index into the file for writing int local_index=0; byte[] current_buffer=null; boolean end_reached=false; final static Log log=LogFactory.getLog(GridInputStream.class); GridInputStream(GridFile file, ReplCache cache, int chunk_size) throws FileNotFoundException { this.file=file; this.name=file.getPath(); this.cache=cache; this.chunk_size=chunk_size; } public int read() throws IOException { int bytes_remaining_to_read=getBytesRemainingInChunk(); if(bytes_remaining_to_read == 0) { if(end_reached) return -1; current_buffer=fetchNextChunk(); local_index=0; if(current_buffer == null) return -1; else if(current_buffer.length < chunk_size) end_reached=true; bytes_remaining_to_read=getBytesRemainingInChunk(); } int retval=current_buffer[local_index++]; index++; return retval; } public int read(byte[] b) throws IOException { return read(b, 0, b.length); } public int read(byte[] b, int off, int len) throws IOException { int bytes_read=0; while(len > 0) { int bytes_remaining_to_read=getBytesRemainingInChunk(); if(bytes_remaining_to_read == 0) { if(end_reached) return bytes_read > 0? bytes_read : -1; current_buffer=fetchNextChunk(); local_index=0; if(current_buffer == null) return bytes_read > 0? bytes_read : -1; else if(current_buffer.length < chunk_size) end_reached=true; bytes_remaining_to_read=getBytesRemainingInChunk(); } int bytes_to_read=Math.min(len, bytes_remaining_to_read); // bytes_to_read=Math.min(bytes_to_read, current_buffer.length - local_index); System.arraycopy(current_buffer, local_index, b, off, bytes_to_read); local_index+=bytes_to_read; off+=bytes_to_read; len-=bytes_to_read; bytes_read+=bytes_to_read; index+=bytes_to_read; } return bytes_read; } public long skip(long n) throws IOException { throw new UnsupportedOperationException(); } public int available() throws IOException { throw new UnsupportedOperationException(); } public void close() throws IOException { local_index=index=0; end_reached=false; } private int getBytesRemainingInChunk() { // return chunk_size - local_index; return current_buffer == null? 0 : current_buffer.length - local_index; } private byte[] fetchNextChunk() { int chunk_number=getChunkNumber(); String key=name + ".#" + chunk_number; byte[] val= cache.get(key); if(log.isTraceEnabled()) log.trace("fetching index=" + index + ", key=" + key +": " + (val != null? val.length + " bytes" : "null")); return val; } private int getChunkNumber() { return index / chunk_size; } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/blocks/GridOutputStream.java0000644000175000017500000000566311647260573027242 0ustar moellermoellerpackage org.jgroups.blocks; import org.jgroups.logging.Log; import org.jgroups.logging.LogFactory; import org.jgroups.annotations.Experimental; import java.io.*; /** * @author Bela Ban */ @Experimental public class GridOutputStream extends OutputStream { final ReplCache cache; final short repl_count; final int chunk_size; final String name; protected final GridFile file; // file representing this output stream int index=0; // index into the file for writing int local_index=0; final byte[] current_buffer; static final Log log=LogFactory.getLog(GridOutputStream.class); GridOutputStream(GridFile file, boolean append, ReplCache cache, short repl_count, int chunk_size) throws FileNotFoundException { this.file=file; this.name=file.getPath(); this.cache=cache; this.repl_count=repl_count; this.chunk_size=chunk_size; current_buffer=new byte[chunk_size]; } public void write(int b) throws IOException { int remaining=getBytesRemainingInChunk(); if(remaining == 0) { flush(); local_index=0; remaining=chunk_size; } current_buffer[local_index]=(byte)b; local_index++; index++; } public void write(byte[] b) throws IOException { if(b != null) write(b, 0, b.length); } public void write(byte[] b, int off, int len) throws IOException { while(len > 0) { int remaining=getBytesRemainingInChunk(); if(remaining == 0) { flush(); local_index=0; remaining=chunk_size; } int bytes_to_write=Math.min(remaining, len); System.arraycopy(b, off, current_buffer, local_index, bytes_to_write); off+=bytes_to_write; len-=bytes_to_write; local_index+=bytes_to_write; index+=bytes_to_write; } } public void close() throws IOException { flush(); reset(); } public void flush() throws IOException { int chunk_number=getChunkNumber(); String key=name + ".#" + chunk_number; byte[] val=new byte[local_index]; System.arraycopy(current_buffer, 0, val, 0, local_index); cache.put(key, val, repl_count, 0); if(log.isTraceEnabled()) log.trace("put(): index=" + index + ", key=" + key + ": " + val.length + " bytes"); file.setLength(index); } private int getBytesRemainingInChunk() { return chunk_size - local_index; } private int getChunkNumber() { return (index-1) / chunk_size; } private void reset() { index=local_index=0; } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/blocks/GroupRequest.java0000644000175000017500000002774611647260573026433 0ustar moellermoellerpackage org.jgroups.blocks; 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.Rsp; import org.jgroups.util.RspList; import java.util.*; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; /** * 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 */ public class GroupRequest extends Request { /** Correlates requests and responses */ @GuardedBy("lock") private final Map requests; @GuardedBy("lock") int num_received, num_not_received, num_suspected; /** * @param msg 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 targets The targets, which are supposed to receive the message. Any receiver not in this set will * discard the message. Targets are always a subset of the current membership * @param options The request options to be used for this call */ public GroupRequest(Message msg, RequestCorrelator corr, Collection

targets, RequestOptions options) { super(msg, corr, null, options); int size=targets.size(); requests=new HashMap(size); setTargets(targets); } public GroupRequest(Message m, RequestCorrelator corr, Address target, RequestOptions options) { super(m, corr, null, options); requests=new HashMap(1); setTarget(target); } public GroupRequest(Message m, Transport transport, Collection
mbrs, RequestOptions options) { super(m, null, transport, options); int size=mbrs.size(); requests=new HashMap(size); setTargets(mbrs); } public boolean getAnycasting() { return options.getAnycasting(); } public void setAnycasting(boolean anycasting) { options.setAnycasting(anycasting); } public void sendRequest() throws Exception { sendRequest(requests.keySet(), req_id); } /* ---------------------- 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) { if(done) return; Rsp rsp=requests.get(sender); if(rsp == null) return; RspFilter rsp_filter=options.getRspFilter(); boolean responseReceived=false; lock.lock(); try { if(!rsp.wasReceived()) { if((responseReceived=(rsp_filter == null) || rsp_filter.isAcceptable(response_value, sender))) rsp.setValue(response_value); rsp.setReceived(responseReceived); } if(responseReceived) num_received++; done=rsp_filter == null? responsesComplete() : !rsp_filter.needMoreResponses(); if(responseReceived || done) completed.signalAll(); // wakes up execute() if(done && corr != null) corr.done(req_id); } finally { lock.unlock(); } if(responseReceived || done) checkCompletion(this); } /** * 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; boolean changed=false; Rsp rsp=requests.get(suspected_member); if(rsp != null) { if(rsp.setSuspected(true)) { rsp.setValue(null); changed=true; lock.lock(); try { num_suspected++; completed.signalAll(); } finally { lock.unlock(); } } } if(changed) checkCompletion(this); } /** * 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) { Vector
mbrs=new_view != null? new_view.getMembers() : null; if(mbrs == null) return; boolean changed=false; if(requests == null || requests.isEmpty()) return; lock.lock(); try { for(Map.Entry entry: requests.entrySet()) { Address mbr=entry.getKey(); if(!mbrs.contains(mbr)) { Rsp rsp=entry.getValue(); rsp.setValue(null); if(rsp.setSuspected(true)) { num_suspected++; changed=true; } } } if(changed) completed.signalAll(); } finally { lock.unlock(); } if(changed) checkCompletion(this); } /* -------------------- End of Interface RspCollector ----------------------------------- */ /** Returns the results as a RspList */ public RspList getResults() { Collection rsps=requests.values(); return new RspList(rsps); } public RspList get() throws InterruptedException, ExecutionException { lock.lock(); try { waitForResults(0); } finally { lock.unlock(); } return getResults(); } public RspList get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { boolean ok; lock.lock(); try { ok=waitForResults(unit.toMillis(timeout)); } finally { lock.unlock(); } if(!ok) throw new TimeoutException(); return getResults(); } public String toString() { StringBuilder ret=new StringBuilder(128); ret.append(super.toString()); if(!requests.isEmpty()) { ret.append(", entries:\n"); for(Map.Entry entry: requests.entrySet()) { Address mbr=entry.getKey(); Rsp rsp=entry.getValue(); ret.append(mbr).append(": ").append(rsp).append("\n"); } } return ret.toString(); } /* --------------------------------- Private Methods -------------------------------------*/ private void setTarget(Address mbr) { requests.put(mbr, new Rsp(mbr)); num_not_received=1; } private void setTargets(Collection
mbrs) { for(Address mbr: mbrs) requests.put(mbr, new Rsp(mbr)); num_not_received=requests.size(); } private static int determineMajority(int i) { return i < 2? i : (i / 2) + 1; } private void sendRequest(final Collection
targetMembers, long requestId) throws Exception { try { if(corr != null) { corr.sendRequest(requestId, targetMembers, request_msg, options.getMode() == GET_NONE? null : this, options); } else { if(options.getAnycasting()) { 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") protected boolean responsesComplete() { if(done) return true; final int num_total=requests.size(); switch(options.getMode()) { 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: return true; case GET_NONE: return true; default : if(log.isErrorEnabled()) log.error("rsp_mode " + options.getMode() + " unknown !"); break; } return false; } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/blocks/LazyRemovalCache.java0000644000175000017500000001613011647260573027140 0ustar moellermoellerpackage org.jgroups.blocks; import org.jgroups.util.Util; import java.util.*; import java.util.concurrent.ConcurrentMap; /** * Cache which doesn't remove elements on remove(), removeAll() or retainAll(), but only removes elements when a * configurable size limit has been exceeded. In that case, all elements marked as removable and older than a * configurable time are evicted. Elements are marked as removable by remove(), removeAll() and retainAll(). When * an elements is marked as removable, but later reinserted, the mark is removed. * @author Bela Ban */ public class LazyRemovalCache { private final ConcurrentMap> map=Util.createConcurrentMap(); /** Max number of elements, if exceeded, we remove all elements marked as removable and older than max_age ms */ private final int max_elements; private final long max_age; public interface Printable { String print(K key,V val); } public LazyRemovalCache() { this(200, 5000L); } public LazyRemovalCache(int max_elements, long max_age) { this.max_elements=max_elements; this.max_age=max_age; } public void add(K key, V val) { if(key != null && val != null) map.put(key, new Entry(val)); // overwrite existing element (new timestamp, and possible removable mark erased) checkMaxSizeExceeded(); } public boolean containsKey(K key) { return map.containsKey(key); } /** Returns true if all of the keys in keys are present. Returns false if one or more of the keys are absent */ public boolean containsKeys(Collection keys) { for(K key: keys) if(!map.containsKey(key)) return false; return true; } public V get(K key) { if(key == null) return null; Entry entry=map.get(key); return entry != null? entry.val : null; } public K getByValue(V val) { if(val == null) return null; for(Map.Entry> entry: map.entrySet()) { Entry v=entry.getValue(); if(v.val != null && v.val.equals(val)) return entry.getKey(); } return null; } public void remove(K key) { remove(key, false); } public void remove(K key, boolean force) { if(key == null) return; if(force) map.remove(key); else { Entry entry=map.get(key); if(entry != null) entry.removable=true; } checkMaxSizeExceeded(); } public void removeAll(Collection keys) { removeAll(keys, false); } public void removeAll(Collection keys, boolean force) { if(keys == null || keys.isEmpty()) return; if(force) map.keySet().removeAll(keys); else { for(K key: keys) { Entry entry=map.get(key); if(entry != null) entry.removable=true; } } checkMaxSizeExceeded(); } public void clear(boolean force) { if(force) map.clear(); else { for(Map.Entry> entry: map.entrySet()) { Entry val=entry.getValue(); if(val != null) { Entry tmp=entry.getValue(); if(tmp != null) tmp.removable=true; } } } } public void retainAll(Collection keys) { retainAll(keys, false); } public void retainAll(Collection keys, boolean force) { if(keys == null || keys.isEmpty()) return; if(force) map.keySet().retainAll(keys); else { for(Map.Entry> entry: map.entrySet()) { if(!keys.contains(entry.getKey())) { Entry val=entry.getValue(); if(val != null) val.removable=true; } } } // now make sure that all elements in keys have removable=false for(K key: keys) { Entry val=map.get(key); if(val != null && val.removable) val.removable=false; } checkMaxSizeExceeded(); } public Set values() { Set retval=new HashSet(); for(Entry entry: map.values()) { retval.add(entry.val); } return retval; } /** * Adds all value which have not been marked as removable to the returned set * @return */ public Set nonRemovedValues() { Set retval=new HashSet(); for(Entry entry: map.values()) { if(!entry.removable) retval.add(entry.val); } return retval; } public Map contents() { Map retval=new HashMap(); for(Map.Entry> entry: map.entrySet()) retval.put(entry.getKey(), entry.getValue().val); return retval; } public int size() { return map.size(); } public String printCache() { StringBuilder sb=new StringBuilder(); for(Map.Entry> entry: map.entrySet()) { sb.append(entry.getKey()).append(": ").append(entry.getValue()).append("\n"); } return sb.toString(); } public String printCache(Printable print_function) { StringBuilder sb=new StringBuilder(); for(Map.Entry> entry: map.entrySet()) { K key=entry.getKey(); V val=entry.getValue().val; sb.append(print_function.print(key, val)); } return sb.toString(); } public String toString() { return printCache(); } private void checkMaxSizeExceeded() { if(map.size() > max_elements) { removeMarkedElements(); } } /** * Removes elements marked as removable * @param force If set to true, all elements marked as 'removable' will get removed, regardless of expiration */ public void removeMarkedElements(boolean force) { long curr_time=System.currentTimeMillis(); for(Iterator>> it=map.entrySet().iterator(); it.hasNext();) { Map.Entry> entry=it.next(); Entry tmp=entry.getValue(); if(tmp == null) continue; if(tmp.removable && (curr_time - tmp.timestamp) >= max_age || force) { it.remove(); } } } /** * Removes elements marked as removable */ public void removeMarkedElements() { removeMarkedElements(false); } private static class Entry { private final V val; private final long timestamp=System.currentTimeMillis(); private boolean removable=false; public Entry(V val) { this.val=val; } public String toString() { return val + " (" + (System.currentTimeMillis() - timestamp) + "ms old" + (removable? ", removable" : "") + ")"; } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/blocks/LazyRemovalSet.java0000644000175000017500000001542111647260573026672 0ustar moellermoellerpackage org.jgroups.blocks; import java.util.*; /** * Hash set which doesn't remove elements on remove(), removeAll() or retainAll(), but only removes elements when a * configurable size limit has been exceeded. In that case, all elements marked as removable and older than a * configurable time are evicted. Elements are marked as removable by remove(), removeAll() and retainAll(). When * an elements is marked as removable, but later reinserted, the mark is removed. * @author Bela Ban */ public class LazyRemovalSet { private final Set> set=new HashSet>(); /** Max number of elements, if exceeded, we remove all elements marked as removable and older than max_age ms */ private final int max_elements; private final long max_age; public interface Printable { String print(V val); } public LazyRemovalSet() { this(200, 5000L); } public LazyRemovalSet(int max_elements, long max_age) { this.max_elements=max_elements; this.max_age=max_age; } public void add(V val) { if(val != null) { final Entry entry=new Entry(val); if(set.contains(entry)) set.remove(entry); set.add(entry); // overwrite existing element (new timestamp, and possible removable mark erased) } checkMaxSizeExceeded(); } public void add(V ... vals) { add(Arrays.asList(vals)); } public void add(Collection vals) { if(vals == null || vals.isEmpty()) return; for(V val: vals) { Entry entry=find(val); if(entry != null) set.remove(entry); set.add(new Entry(val)); // overwrite existing element (new timestamp, and possible removable mark erased) } checkMaxSizeExceeded(); } public boolean contains(V val) { if(val == null) return false; Entry entry=new Entry(val); return set.contains(entry); } public void remove(V val) { remove(val, false); } public void remove(V val, boolean force) { if(val == null) return; Entry entry=new Entry(val); if(force) set.remove(entry); else { entry=find(val); if(entry != null) entry.removable=true; } checkMaxSizeExceeded(); } public void removeAll(Collection values) { removeAll(values, false); } public void removeAll(Collection values, boolean force) { if(values == null || values.isEmpty()) return; if(force) set.removeAll(values); else { for(Entry entry: set) { if(values.contains(entry.val)) entry.removable=true; } } checkMaxSizeExceeded(); } public void clear(boolean force) { if(force) set.clear(); else { for(Entry entry: set) { if(entry.val != null) entry.removable=true; } } } public void retainAll(Collection values) { retainAll(values, false); } public void retainAll(Collection values, boolean force) { if(values == null || values.isEmpty()) return; if(force) set.retainAll(values); else { for(Entry entry: set) { if(!values.contains(entry.val)) entry.removable=true; } } // now make sure that all elements in keys have removable=false for(V val: values) { Entry entry=find(val); if(entry != null) entry.removable=false; } checkMaxSizeExceeded(); } public Set values() { Set retval=new HashSet(); for(Entry entry: set) retval.add(entry.val); return retval; } /** * Adds all value which have not been marked as removable to the returned set * @return */ public Set nonRemovedValues() { Set retval=new HashSet(); for(Entry entry: set) { if(!entry.removable) retval.add(entry.val); } return retval; } public int size() { return set.size(); } public String printCache() { StringBuilder sb=new StringBuilder(); for(Entry entry: set) { sb.append(entry).append("\n"); } return sb.toString(); } public String printCache(Printable print_function) { StringBuilder sb=new StringBuilder(); boolean first=true; for(Entry entry: set) { V val=entry.val; if(first) first=false; else sb.append(", "); sb.append(print_function.print(val)); } return sb.toString(); } public String toString() { return printCache(); } private void checkMaxSizeExceeded() { if(set.size() > max_elements) { removeMarkedElements(); } } protected Entry find(V val) { if(val == null) return null; for(Entry entry: set) { if(val.equals(entry.val)) return entry; } return null; } /** * Removes elements marked as removable * @param force If set to true, all elements marked as 'removable' will get removed, regardless of expiration */ public void removeMarkedElements(boolean force) { long curr_time=System.currentTimeMillis(); for(Iterator> it=set.iterator(); it.hasNext();) { Entry entry=it.next(); if(entry == null) continue; if(entry.removable && (curr_time - entry.timestamp) >= max_age || force) { it.remove(); } } } /** * Removes elements marked as removable */ public void removeMarkedElements() { removeMarkedElements(false); } protected static class Entry { protected final V val; protected final long timestamp=System.currentTimeMillis(); protected boolean removable=false; public Entry(V val) { this.val=val; } public boolean equals(Object obj) { if(obj instanceof Entry) { return val.equals(((Entry)obj).val); } return val.equals(obj); } public int hashCode() { return val.hashCode(); } public String toString() { return val + " (" + (System.currentTimeMillis() - timestamp) + "ms old" + (removable? ", removable" : "") + ")"; } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/blocks/LockManager.java0000644000175000017500000000677411647260573026147 0ustar moellermoellerpackage 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) */ @Deprecated 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; }libjgroups-java-2.12.2.Final.orig/src/org/jgroups/blocks/LockMultiLockedException.java0000644000175000017500000000114011647260573030646 0ustar moellermoellerpackage 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) */ public class LockMultiLockedException extends Exception { private static final long serialVersionUID = 3719208228960070835L; public LockMultiLockedException() { super(); } public LockMultiLockedException(String s) { super(s); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/blocks/LockNotGrantedException.java0000644000175000017500000000067711647260573030515 0ustar moellermoellerpackage 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); } }libjgroups-java-2.12.2.Final.orig/src/org/jgroups/blocks/LockNotReleasedException.java0000644000175000017500000000070611647260573030646 0ustar moellermoellerpackage 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); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/blocks/LockingException.java0000644000175000017500000000136111647260573027214 0ustar moellermoeller 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(); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/blocks/MembershipListenerAdapter.java0000644000175000017500000000654211647260573031057 0ustar moellermoellerpackage 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()]); } }libjgroups-java-2.12.2.Final.orig/src/org/jgroups/blocks/MemcachedConnector.java0000644000175000017500000003127511647260573027477 0ustar moellermoellerpackage org.jgroups.blocks; import org.jgroups.annotations.ManagedAttribute; import org.jgroups.annotations.ManagedOperation; import org.jgroups.util.Util; import javax.management.MBeanRegistrationException; import javax.management.MalformedObjectNameException; import java.io.*; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; import java.nio.channels.ClosedSelectorException; import java.util.*; import java.util.concurrent.*; /** Class which listens on a server socket for memcached clients, reads the requests, forwards them to an instance of * PartitionedHashMap and sends the response. A memcached client should be able to work without changes once the * memcached protocol (http://code.sixapart.com/svn/memcached/trunk/server/doc/protocol.txt) has been implemented * completely.
* @author Bela Ban */ public class MemcachedConnector implements Runnable { @ManagedAttribute(writable=false) private int port=11211; @ManagedAttribute(writable=false) private InetAddress bind_addr=null; private PartitionedHashMap cache=null; private Thread thread=null; private ServerSocket srv_sock; @ManagedAttribute(writable=true) private int core_threads=1; @ManagedAttribute(writable=true) private int max_threads=500; @ManagedAttribute(writable=true) private long idle_time=5000L; private Executor thread_pool; private long start_time; private final byte[] STORED="STORED\r\n".getBytes(); private final byte[] DELETED="DELETED\r\n".getBytes(); private final byte[] END="END\r\n".getBytes(); private final byte[] RN="\r\n".getBytes(); public MemcachedConnector(InetAddress bind_addr, int port, PartitionedHashMap cache) { this.bind_addr=bind_addr; this.cache=cache; this.port=port; } public InetAddress getBindAddress() { return bind_addr; } public void setBindAddress(InetAddress bind_addr) { this.bind_addr=bind_addr; } public int getPort() { return port; } public void setPort(int port) { this.port=port; } public PartitionedHashMap getCache() { return cache; } public void setCache(PartitionedHashMap cache) { this.cache=cache; } public int getThreadPoolCoreThreads() { return core_threads; } public void setThreadPoolCoreThreads(int core_threads) { this.core_threads=core_threads; } public int getThreadPoolMaxThreads() { return max_threads; } public void setThreadPoolMaxThreads(int max_threads) { this.max_threads=max_threads; } public long getThreadPoolIdleTime() { return idle_time; } public void setThreadPoolIdleTime(long idle_time) { this.idle_time=idle_time; } public Executor getThreadPool() { return thread_pool; } public void setThreadPool(Executor thread_pool) { if(this.thread_pool instanceof ExecutorService) { ((ExecutorService)thread_pool).shutdown(); } this.thread_pool=thread_pool; } public Map getStats() { Map stats=new HashMap(); stats.put("time", System.currentTimeMillis()); stats.put("uptime", (System.currentTimeMillis() - start_time) / 1000L); return stats; } @ManagedOperation public void start() throws IOException, MalformedObjectNameException, MBeanRegistrationException { srv_sock=new ServerSocket(port, 50, bind_addr); if(thread_pool == null) { thread_pool=new ThreadPoolExecutor(core_threads, max_threads, idle_time, TimeUnit.MILLISECONDS, new SynchronousQueue(), new ThreadPoolExecutor.CallerRunsPolicy()); // thread_pool=new DirectExecutor(); } if(thread == null || !thread.isAlive()) { thread=new Thread(this, "Acceptor"); thread.start(); } start_time=System.currentTimeMillis(); } @ManagedOperation public void stop() throws IOException { Util.close(srv_sock); thread=null; if(thread_pool instanceof ExecutorService) ((ExecutorService)thread_pool).shutdown(); } public void run() { System.out.println("MemcachedConnector listening on " + srv_sock.getLocalSocketAddress()); while(thread != null && Thread.currentThread().equals(thread)) { Socket client_sock=null; try { client_sock=srv_sock.accept(); // System.out.println("ACCEPT: " + client_sock.getRemoteSocketAddress()); final RequestHandler handler=new RequestHandler(client_sock); /*new Thread() { public void run() { handler.run(); } }.start(); */ thread_pool.execute(handler); } catch(ClosedSelectorException closed) { Util.close(client_sock); break; } catch(Throwable e) { } } } private class RequestHandler implements Runnable { private final Socket client_sock; private final InputStream input; private final OutputStream output; public RequestHandler(Socket client_sock) throws IOException { this.client_sock=client_sock; this.input=new BufferedInputStream(client_sock.getInputStream()); this.output=new BufferedOutputStream(client_sock.getOutputStream()); } public void run() { byte[] val; String line; while(client_sock.isConnected()) { try { line=Util.readLine(input); if(line == null) break; // System.out.println("line = " + line); Request req=parseRequest(line); if(req == null) { break; } // System.out.println("req = " + req); switch(req.type) { case SET: byte[] data=new byte[req.number_of_bytes]; int num=input.read(data, 0, data.length); if(num == -1) throw new EOFException(); cache.put(req.key, data, req.caching_time); output.write(STORED); output.flush(); Util.discardUntilNewLine(input); break; case GET: case GETS: if(req.keys != null && !req.keys.isEmpty()) { for(String key: req.keys) { val=cache.get(key); if(val != null) { int length=val.length; output.write(("VALUE " + key + " 0 " + length + "\r\n").getBytes()); output.write(val, 0, length); output.write(RN); } } } output.write(END); output.flush(); break; case DELETE: cache.remove(req.key); output.write(DELETED); output.flush(); break; case STATS: Map stats=getStats(); StringBuilder sb=new StringBuilder(); for(Map.Entry entry: stats.entrySet()) { sb.append("STAT ").append(entry.getKey()).append(" ").append(entry.getValue()).append("\r\n"); } sb.append("END\r\n"); output.write(sb.toString().getBytes()); output.flush(); break; } } catch(StreamCorruptedException corrupted_ex) { try { output.write(("CLIENT_ERROR failed to parse request: " + corrupted_ex + ":\r\n").getBytes()); output.flush(); } catch(IOException e) {} } catch(EOFException end_of_file_ex) { break; } catch(Throwable e) { } } Util.close(client_sock); } private Request parseRequest(String line) throws IOException { Request req=new Request(); String[] args=line.trim().split(" +"); String tmp=args[0]; if(tmp == null) throw new EOFException(); if(tmp.equals("set")) req.type=Request.Type.SET; else if(tmp.equals("add")) req.type=Request.Type.ADD; else if(tmp.equals("replace")) req.type=Request.Type.REPLACE; else if(tmp.equals("prepend")) req.type=Request.Type.PREPEND; else if(tmp.equals("append")) req.type=Request.Type.APPEND; else if(tmp.equals("cas")) req.type=Request.Type.CAS; else if(tmp.equals("incr")) req.type=Request.Type.INCR; else if(tmp.equals("decr")) req.type=Request.Type.DECR; else if(tmp.equals("get")) req.type=Request.Type.GET; else if(tmp.equals("gets")) req.type=Request.Type.GETS; else if(tmp.equals("delete")) req.type=Request.Type.DELETE; else if(tmp.equals("stat")) req.type=Request.Type.STAT; else if(tmp.equals("stats")) req.type=Request.Type.STATS; else { throw new StreamCorruptedException("request \"" + line + "\" not known"); } switch(req.type) { case SET: case ADD: case REPLACE: case PREPEND: case APPEND: // key tmp=args[1]; if(tmp == null) throw new EOFException(); req.key=tmp; // read flags and discard: flags are not supported tmp=args[2]; if(tmp == null) throw new EOFException(); // expiry time tmp=args[3]; if(tmp == null) throw new EOFException(); req.caching_time=Long.parseLong(tmp) * 1000L; // convert from secs to ms // number of bytes tmp=args[4]; if(tmp == null) throw new EOFException(); req.number_of_bytes=Integer.parseInt(tmp); break; case GET: case GETS: req.keys=new ArrayList(5); req.keys.addAll(Arrays.asList(args).subList(1, args.length)); break; case DELETE: // key tmp=args[1]; if(tmp == null) throw new EOFException(); req.key=tmp; break; case STATS: break; } return req; } } public static class Request { public static enum Type {SET, ADD, REPLACE, PREPEND, APPEND, CAS, INCR, DECR, GET, GETS, DELETE, STAT, STATS}; Type type; String key; List keys=null; long caching_time; int number_of_bytes=0; public Request() { } public String toString() { StringBuilder sb=new StringBuilder(); sb.append(type + ": "); if(key != null) sb.append("key=" + key); else if(keys != null && !keys.isEmpty()) sb.append("keys=" + keys); sb.append(", caching_time=" + caching_time + ", number_of_bytes=" + number_of_bytes); return sb.toString(); } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/blocks/MessageDispatcher.java0000644000175000017500000010405511647260573027346 0ustar moellermoeller package org.jgroups.blocks; import org.jgroups.*; import org.jgroups.blocks.mux.Muxer; import org.jgroups.logging.Log; import org.jgroups.logging.LogFactory; import org.jgroups.protocols.TP; import org.jgroups.stack.Protocol; import org.jgroups.stack.StateTransferInfo; import org.jgroups.util.*; import java.io.InputStream; import java.io.OutputStream; import java.io.Serializable; import java.util.*; /** * 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 */ 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 PullPushAdapter adapter=null; protected PullPushHandler handler=null; protected Serializable id=null; protected final Log log=LogFactory.getLog(getClass()); protected boolean hardware_multicast_supported=false; public MessageDispatcher() { } public MessageDispatcher(Channel channel, MessageListener l, MembershipListener l2) { this.channel=channel; prot_adapter=new ProtocolAdapter(); if(channel != null) { local_addr=channel.getAddress(); } setMessageListener(l); setMembershipListener(l2); if(channel != null) { installUpHandler(prot_adapter, true); } start(); } @Deprecated public MessageDispatcher(Channel channel, MessageListener l, MembershipListener l2, boolean deadlock_detection) { this.channel=channel; prot_adapter=new ProtocolAdapter(); if(channel != null) { local_addr=channel.getAddress(); } setMessageListener(l); setMembershipListener(l2); if(channel != null) { installUpHandler(prot_adapter, true); } start(); } @Deprecated public MessageDispatcher(Channel channel, MessageListener l, MembershipListener l2, boolean deadlock_detection, boolean concurrent_processing) { this.channel=channel; prot_adapter=new ProtocolAdapter(); if(channel != null) { local_addr=channel.getAddress(); } setMessageListener(l); setMembershipListener(l2); if(channel != null) { installUpHandler(prot_adapter, true); } start(); } public MessageDispatcher(Channel channel, MessageListener l, MembershipListener l2, RequestHandler req_handler) { this(channel, l, l2); setRequestHandler(req_handler); } @Deprecated public MessageDispatcher(Channel channel, MessageListener l, MembershipListener l2, RequestHandler req_handler, boolean deadlock_detection) { this(channel, l, l2, deadlock_detection, false); setRequestHandler(req_handler); } @Deprecated 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. */ @Deprecated 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).getAddress(); } 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 */ @Deprecated 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).getAddress(); // fixed bug #800774 } start(); } @Deprecated public MessageDispatcher(PullPushAdapter adapter, Serializable id, MessageListener l, MembershipListener l2, RequestHandler req_handler, boolean 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).getAddress(); // fixed bug #800774 } start(); } public UpHandler getProtocolAdapter() { return prot_adapter; } /** 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); } } } @Deprecated public boolean getDeadlockDetection() {return false;} @Deprecated public void setDeadlockDetection(boolean flag) { } @Deprecated public boolean getConcurrentProcessing() {return false;} @Deprecated public void setConcurrentProcessing(boolean flag) { } public void start() { if(corr == null) { if(transport_adapter != null) { corr=createRequestCorrelator(transport_adapter, this, local_addr); } else { corr=createRequestCorrelator(prot_adapter, this, local_addr); } } 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=channel.getProtocolStack().getTransport(); corr.registerProbeHandler(transport); } TP transport=channel.getProtocolStack().getTransport(); hardware_multicast_supported=transport.supportsMulticasting(); } } protected RequestCorrelator createRequestCorrelator(Object transport, RequestHandler handler, Address local_addr) { return new RequestCorrelator(transport, handler, local_addr); } protected void correlatorStarted() { ; } public void stop() { if(corr != null) { corr.stop(); } if(channel instanceof JChannel) { TP transport=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.getAddress(); if(prot_adapter == null) prot_adapter=new ProtocolAdapter(); // Don't force installing the UpHandler so subclasses can use this // method and still integrate with a MuxUpHandler installUpHandler(prot_adapter, false); } /** * Sets the given UpHandler as the UpHandler for the channel, or, if the * channel already has a Muxer installed as it's UpHandler, sets the given * handler as the Muxer's {@link Muxer#setDefaultHandler(Object) default handler}. * If the relevant handler is already installed, the canReplace * controls whether this method replaces it (after logging a WARN) or simply * leaves handler uninstalled. *

* Passing false as the canReplace value allows * callers to use this method to install defaults without concern about * inadvertently overriding * * @param handler the UpHandler to install * @param canReplace true if an existing Channel upHandler or * Muxer default upHandler can be replaced; false * if this method shouldn't install */ protected void installUpHandler(UpHandler handler, boolean canReplace) { UpHandler existing = channel.getUpHandler(); if (existing == null) { channel.setUpHandler(handler); } else if (existing instanceof Muxer) { @SuppressWarnings("unchecked") Muxer mux = (Muxer) existing; if (mux.getDefaultHandler() == null) { mux.setDefaultHandler(handler); } else if (canReplace) { log.warn("Channel Muxer already has a default up handler installed (" + mux.getDefaultHandler() + ") but now it is being overridden"); mux.setDefaultHandler(handler); } } else if (canReplace) { log.warn("Channel already has an up handler installed (" + existing + ") but now it is being overridden"); channel.setUpHandler(handler); } } @Deprecated public void send(Message msg) throws ChannelNotConnectedException, ChannelClosedException { if(channel != null) { channel.send(msg); return; } if(adapter != null) { try { if(id != null) adapter.send(id, msg); else adapter.send(msg); } catch(Throwable ex) { log.error("exception=" + Util.print(ex)); } } else { log.error("channel == null"); } } @Deprecated public RspList castMessage(final Vector dests, Message msg, int mode, long timeout) { return castMessage(dests, msg, new RequestOptions(mode, timeout, false, null)); } @Deprecated public RspList castMessage(final Vector dests, Message msg, int mode, long timeout, boolean use_anycasting) { return castMessage(dests, msg, new RequestOptions(mode, timeout, use_anycasting, null)); } // used by Infinispan @Deprecated /** * @deprecated Use {@link #castMessage(java.util.Collection, org.jgroups.Message, RequestOptions)} instead */ public RspList castMessage(final Vector dests, Message msg, int mode, long timeout, boolean use_anycasting, RspFilter filter) { RequestOptions opts=new RequestOptions(mode, timeout, use_anycasting, filter); return castMessage(dests, msg, opts); } /** * Sends a message to the members listed in dests. If dests is null, the message is sent to all current group * members. * @param dests A list of group members. The message is sent to all members of the current group if null * @param msg The message to be sent * @param options A set of options that govern the call. See {@link org.jgroups.blocks.RequestOptions} for details * @return * @since 2.9 */ public RspList castMessage(final Collection

dests, Message msg, RequestOptions options) { GroupRequest req=cast(dests, msg, options, true); return req != null? req.getResults() : RspList.EMPTY_RSP_LIST; } @Deprecated public NotifyingFuture castMessageWithFuture(final Vector dests, Message msg, int mode, long timeout, boolean use_anycasting, RspFilter filter) { return castMessageWithFuture(dests, msg, new RequestOptions(mode, timeout, use_anycasting, filter)); } public NotifyingFuture castMessageWithFuture(final Collection
dests, Message msg, RequestOptions options) { GroupRequest req=cast(dests, msg, options, false); return req != null? req : new NullFuture(RspList.EMPTY_RSP_LIST); } protected GroupRequest cast(final Collection
dests, Message msg, RequestOptions options, boolean block_for_results) { List
real_dests; // 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=new ArrayList
(dests); real_dests.retainAll(this.members); } else { synchronized(members) { real_dests=new ArrayList(members); } } // if local delivery is off, then we should not wait for the message from the local member. // therefore remove it from the membership Channel 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.getAddress(); } if(local_addr != null) { real_dests.remove(local_addr); } } if(options != null && options.hasExclusionList()) { Collection
exclusion_list=options.getExclusionList(); real_dests.removeAll(exclusion_list); } // 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 null; } GroupRequest req=new GroupRequest(msg, corr, real_dests, options); if(options != null) { req.setResponseFilter(options.getRspFilter()); req.setAnycasting(options.getAnycasting()); } req.setBlockForResults(block_for_results); try { req.execute(); return req; } catch(Exception ex) { throw new RuntimeException("failed executing request " + req, ex); } } 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 ! * @deprecated Use {@link #sendMessage(org.jgroups.Message, RequestOptions)} instead */ @Deprecated public Object sendMessage(Message msg, int mode, long timeout) throws TimeoutException, SuspectedException { return sendMessage(msg, new RequestOptions(mode, timeout, false, null)); } public Object sendMessage(Message msg, RequestOptions opts) throws TimeoutException, SuspectedException { Address dest=msg.getDest(); if(dest == null) { if(log.isErrorEnabled()) log.error("the message's destination is null, cannot send message"); return null; } UnicastRequest req=new UnicastRequest(msg, corr, dest, opts); try { req.execute(); } catch(Exception t) { throw new RuntimeException("failed executing request " + req, t); } if(opts.getMode() == Request.GET_NONE) return null; Rsp rsp=req.getResult(); if(rsp.wasSuspected()) throw new SuspectedException(dest); if(!rsp.wasReceived()) throw new TimeoutException("timeout sending message to " + dest); return rsp.getValue(); } @Deprecated public NotifyingFuture sendMessageWithFuture(Message msg, int mode, long timeout) throws TimeoutException, SuspectedException { return sendMessageWithFuture(msg, new RequestOptions(mode, timeout, false, null)); } public NotifyingFuture sendMessageWithFuture(Message msg, RequestOptions options) throws TimeoutException, SuspectedException { Address dest=msg.getDest(); if(dest == null) { if(log.isErrorEnabled()) log.error("the message's destination is null, cannot send message"); return null; } UnicastRequest req=new UnicastRequest(msg, corr, dest, options); req.setBlockForResults(false); try { req.execute(); if(options.getMode() == Request.GET_NONE) return new NullFuture(null); return req; } catch(Exception t) { throw new RuntimeException("failed executing request " + req, t); } } /* ------------------------ 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 ------------------------ */ } @Deprecated 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 } } @Deprecated 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) } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/blocks/MessageListenerAdapter.java0000644000175000017500000001330111647260573030337 0ustar moellermoellerpackage 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; } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/blocks/MethodCall.java0000644000175000017500000004026511647260573025771 0ustar moellermoellerpackage org.jgroups.blocks; import org.jgroups.logging.Log; import org.jgroups.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.39 $ */ 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; /** 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()}. */ @Deprecated 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 */ @Deprecated protected Map payload; protected static final Log log=LogFactory.getLog(MethodCall.class); /** Which mode to use. */ protected short mode; /** 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, (Object[])null); } public MethodCall(Method method, Object... arguments) { init(method); if(arguments != null) args=arguments; } /** * * @param method_name * @param args */ @Deprecated 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; } @Deprecated 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); } @Deprecated public synchronized Object put(Object key, Object value) { if(payload == null) payload=new HashMap(); return payload.put(key, value); } @Deprecated 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; } public static Method findMethod(Class target_class, String method_name, Object[] args) throws Exception { int len=args != null? args.length : 0; Method[] methods=getAllMethods(target_class); for(int i=0; i < methods.length; i++) { Method 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. */ static 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. */ static 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; 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; } } public Object invoke(Object target, Object[] args) throws Throwable { if(args != null) this.args=args; return invoke(target); } static 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(); } } public static Object convert(String arg, Class type) { if(type == String.class) return arg; if(type == boolean.class || type == Boolean.class) return Boolean.valueOf(arg); if(type == byte.class || type == Byte.class) return Byte.valueOf(arg); if(type == short.class || type == Short.class) return Short.valueOf(arg); if(type == int.class || type == Integer.class) return Integer.valueOf(arg); if(type == long.class || type == Long.class) return Long.valueOf(arg); if(type == float.class || type == Float.class) return Float.valueOf(arg); if(type == double.class || type == Double.class) return Double.valueOf(arg); return arg; } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/blocks/MethodLookup.java0000644000175000017500000000023611647260573026361 0ustar moellermoellerpackage org.jgroups.blocks; import java.lang.reflect.Method; /** * @author Bela Ban */ public interface MethodLookup { Method findMethod(short id); } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/blocks/MultiRequest.java0000644000175000017500000002754611647260573026427 0ustar moellermoellerpackage org.jgroups.blocks; 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.Rsp; import org.jgroups.util.RspList; import java.util.*; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; /** * Sends a request to multiple destinations. Alternative implementation when we have few targets: between UnicastRequest * with 1 target and GroupRequest with many destination members. Performance is about the same as for GroupRequest, but * this class should use less memory as it doesn't create hashmaps. Don't use with many targets as we have to do * a linear search through an array of targets to match a response to a request.

* MultiRequest is currently not used * * @author Bela Ban * @since 2.9 */ public class MultiRequest extends Request { @GuardedBy("lock") private final Rsp[] responses; protected final int expected_mbrs; @GuardedBy("lock") int num_received, num_not_received, num_suspected; /** * @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 mbrs * 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 options The options to be passed to the request */ public MultiRequest(Message m, RequestCorrelator corr, Collection

mbrs, RequestOptions options, int expected_mbrs) { super(m, corr, null, options); this.expected_mbrs=expected_mbrs; responses=new Rsp[mbrs.size()]; setTargets(mbrs); } public MultiRequest(Message m, RequestCorrelator corr, Address target, RequestOptions options, int expected_mbrs) { super(m, corr, null, options); this.expected_mbrs=expected_mbrs; responses=new Rsp[1]; setTarget(target); } /** * @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 MultiRequest(Message m, Transport transport, Collection
mbrs, RequestOptions options, int expected_mbrs) { super(m, null, transport, options); this.expected_mbrs=expected_mbrs; responses=new Rsp[1]; setTargets(mbrs); } void setTarget(Address mbr) { responses[0]=new Rsp(mbr); num_not_received++; } void setTargets(Collection
mbrs) { int index=0; for(Address mbr: mbrs) { responses[index++]=new Rsp(mbr); num_not_received++; } } public boolean getAnycasting() { return options.getAnycasting(); } public void setAnycasting(boolean anycasting) { options.setAnycasting(anycasting); } public void sendRequest() throws Exception { List
targets=null; targets=new ArrayList
(responses.length); for(Rsp rsp: responses) targets.add(rsp.getSender()); sendRequest(targets, req_id, options); } Rsp findResponse(Address target) { for(Rsp rsp: responses) { if(rsp != null && target.equals(rsp.getSender())) return rsp; } return null; } /* ---------------------- 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) { if(done) return; Rsp rsp=findResponse(sender); if(rsp == null) return; RspFilter rsp_filter=options.getRspFilter(); boolean responseReceived=false; if(!rsp.wasReceived()) { if((responseReceived=(rsp_filter == null) || rsp_filter.isAcceptable(response_value, sender))) rsp.setValue(response_value); rsp.setReceived(responseReceived); } lock.lock(); try { if(responseReceived) num_received++; done=rsp_filter == null? responsesComplete() : !rsp_filter.needMoreResponses(); if(responseReceived || done) completed.signalAll(); // wakes up execute() if(done && corr != null) corr.done(req_id); } finally { lock.unlock(); } if(responseReceived || done) checkCompletion(this); } /** * 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; boolean changed=false; Rsp rsp=findResponse(suspected_member); if(rsp != null) { if(rsp.setSuspected(true)) { rsp.setValue(null); changed=true; lock.lock(); try { num_suspected++; completed.signalAll(); } finally { lock.unlock(); } } } if(changed) checkCompletion(this); } /** * 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) { Vector
mbrs=new_view != null? new_view.getMembers() : null; if(mbrs == null) return; boolean changed=false; if(responses == null || responses.length == 0) return; lock.lock(); try { for(Rsp rsp: responses) { Address mbr=rsp.getSender(); if(!mbrs.contains(mbr)) { rsp.setValue(null); if(rsp.setSuspected(true)) { num_suspected++; changed=true; } } } if(changed) completed.signalAll(); } finally { lock.unlock(); } if(changed) checkCompletion(this); } /* -------------------- End of Interface RspCollector ----------------------------------- */ /** Returns the results as a RspList */ public RspList getResults() { RspList list=new RspList(); for(Rsp rsp: responses) list.put(rsp.getSender(), rsp); return list; } public RspList get() throws InterruptedException, ExecutionException { lock.lock(); try { waitForResults(0); } finally { lock.unlock(); } return getResults(); } public RspList get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { boolean ok; lock.lock(); try { ok=waitForResults(unit.toMillis(timeout)); } finally { lock.unlock(); } if(!ok) throw new TimeoutException(); return getResults(); } public String toString() { StringBuilder ret=new StringBuilder(128); ret.append(super.toString()); lock.lock(); try { if(!(responses.length == 0)) { ret.append(", entries:\n"); for(Rsp rsp: responses) { Address mbr=rsp.getSender(); ret.append(mbr).append(": ").append(rsp).append("\n"); } } } finally { lock.unlock(); } return ret.toString(); } /* --------------------------------- Private Methods -------------------------------------*/ private static int determineMajority(int i) { return i < 2? i : (i / 2) + 1; } private void sendRequest(List
targetMembers, long requestId, RequestOptions options) 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, options.getMode() == GET_NONE? null : this, options); } else { if(options.getAnycasting()) { 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") protected boolean responsesComplete() { if(done) return true; final int num_total=responses.length; switch(options.getMode()) { 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) { 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 " + options.getMode() + " unknown !"); break; } return false; } }libjgroups-java-2.12.2.Final.orig/src/org/jgroups/blocks/NotificationBus.java0000644000175000017500000003341111647260573027050 0ustar moellermoeller package org.jgroups.blocks; import org.jgroups.logging.Log; import org.jgroups.logging.LogFactory; import org.jgroups.*; import org.jgroups.annotations.Unsupported; 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 * @deprecated Will be pulled in 3.0, use a JChannel directly instead */ @Unsupported(comment="provided as an example, use at your own risk") @Deprecated 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.getAddress(); 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(); } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/blocks/PartitionedHashMap.java0000644000175000017500000005216011647260573027476 0ustar moellermoellerpackage org.jgroups.blocks; import org.jgroups.Address; import org.jgroups.JChannel; import org.jgroups.MembershipListener; import org.jgroups.View; import org.jgroups.annotations.Experimental; import org.jgroups.annotations.ManagedAttribute; import org.jgroups.annotations.ManagedOperation; import org.jgroups.annotations.Unsupported; import org.jgroups.logging.Log; import org.jgroups.logging.LogFactory; import org.jgroups.util.Util; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.lang.reflect.Method; import java.util.*; /** Hashmap which distributes its keys and values across the cluster. A PUT/GET/REMOVE computes the cluster node to which * or from which to get/set the key/value from a hash of the key and then forwards the request to the remote cluster node. * We also maintain a local cache (L1 cache) which is a bounded cache that caches retrieved keys/values.
* Todos:
*
    *
  1. Use MarshalledValue to keep track of byte[] buffers, and be able to compute the exact size of the cache. This is * good for maintaining a bounded cache (rather than using the number of entries) *
  2. Provide a better consistent hashing algorithm than ConsistentHashFunction as default *
  3. GUI (showing at least the topology and L1 and L2 caches) *
  4. Notifications (puts, removes, gets etc) *
  5. Invalidation of L1 caches (if used) on removal/put of item *
  6. Benchmarks, comparison to memcached *
  7. Documentation, comparison to memcached *
* @author Bela Ban */ @Experimental @Unsupported public class PartitionedHashMap implements MembershipListener { /** The cache in which all partitioned entries are located */ private Cache l2_cache=new Cache(); /** The local bounded cache, to speed up access to frequently accessed entries. Can be disabled or enabled */ private Cache l1_cache=null; private static final Log log=LogFactory.getLog(PartitionedHashMap.class); private JChannel ch=null; private Address local_addr=null; private View view; private RpcDispatcher disp=null; @ManagedAttribute(writable=true) private String props="udp.xml"; @ManagedAttribute(writable=true) private String cluster_name="PartitionedHashMap-Cluster"; @ManagedAttribute(writable=true) private long call_timeout=1000L; @ManagedAttribute(writable=true) private long caching_time=30000L; // in milliseconds. -1 means don't cache, 0 means cache forever (or until changed) private HashFunction hash_function=null; private Set membership_listeners=new HashSet(); /** On a view change, if a member P1 detects that for any given key K, P1 is not the owner of K, then * it will compute the new owner P2 and transfer ownership for all Ks for which P2 is the new owner. P1 * will then also evict those keys from its L2 cache */ @ManagedAttribute(writable=true) private boolean migrate_data=false; private static final short PUT = 1; private static final short GET = 2; private static final short REMOVE = 3; protected static Map methods=Util.createConcurrentMap(8); static { try { methods.put(PUT, PartitionedHashMap.class.getMethod("_put", Object.class, Object.class, long.class)); methods.put(GET, PartitionedHashMap.class.getMethod("_get", Object.class)); methods.put(REMOVE, PartitionedHashMap.class.getMethod("_remove", Object.class)); } catch(NoSuchMethodException e) { throw new RuntimeException(e); } } public interface HashFunction { /** * Defines a hash function to pick the right node from the list of cluster nodes. Ideally, this function uses * consistent hashing, so that the same key maps to the same node despite cluster view changes. If a view change * causes all keys to hash to different nodes, then PartitionedHashMap will redirect requests to different nodes * and this causes unnecessary overhead. * @param key The object to be hashed * @param membership The membership. This value can be ignored for example if the hash function keeps * track of the membership itself, e.g. by registering as a membership * listener ({@link PartitionedHashMap#addMembershipListener(org.jgroups.MembershipListener)} ) * @return */ Address hash(K key, List
membership); } public PartitionedHashMap(String props, String cluster_name) { this.props=props; this.cluster_name=cluster_name; } public String getProps() { return props; } public void setProps(String props) { this.props=props; } public Address getLocalAddress() { return local_addr; } @ManagedAttribute public String getLocalAddressAsString() { return local_addr != null? local_addr.toString() : "null"; } @ManagedAttribute public String getView() { return view != null? view.toString() : "null"; } @ManagedAttribute public boolean isL1CacheEnabled() { return l1_cache != null; } public String getClusterName() { return cluster_name; } public void setClusterName(String cluster_name) { this.cluster_name=cluster_name; } public long getCallTimeout() { return call_timeout; } public void setCallTimeout(long call_timeout) { this.call_timeout=call_timeout; } public long getCachingTime() { return caching_time; } public void setCachingTime(long caching_time) { this.caching_time=caching_time; } public boolean isMigrateData() { return migrate_data; } public void setMigrateData(boolean migrate_data) { this.migrate_data=migrate_data; } public HashFunction getHashFunction() { return hash_function; } public void setHashFunction(HashFunction hash_function) { this.hash_function=hash_function; } public void addMembershipListener(MembershipListener l) { membership_listeners.add(l); } public void removeMembershipListener(MembershipListener l) { membership_listeners.remove(l); } public Cache getL1Cache() { return l1_cache; } public void setL1Cache(Cache cache) { if(l1_cache != null) l1_cache.stop(); l1_cache=cache; } public Cache getL2Cache() { return l2_cache; } public void setL2Cache(Cache cache) { if(l2_cache != null) l2_cache.stop(); l2_cache=cache; } @ManagedOperation public void start() throws Exception { hash_function=new ConsistentHashFunction(); addMembershipListener((MembershipListener)hash_function); ch=new JChannel(props); disp=new RpcDispatcher(ch, null, this, this); RpcDispatcher.Marshaller marshaller=new CustomMarshaller(); disp.setRequestMarshaller(marshaller); disp.setResponseMarshaller(marshaller); disp.setMethodLookup(new MethodLookup() { public Method findMethod(short id) { return methods.get(id); } }); ch.connect(cluster_name); local_addr=ch.getAddress(); view=ch.getView(); } @ManagedOperation public void stop() { if(l1_cache != null) l1_cache.stop(); if(migrate_data) { List
members_without_me=new ArrayList
(view.getMembers()); members_without_me.remove(local_addr); for(Map.Entry> entry: l2_cache.entrySet()) { K key=entry.getKey(); Address node=hash_function.hash(key, members_without_me); if(!node.equals(local_addr)) { Cache.Value val=entry.getValue(); sendPut(node, key, val.getValue(), val.getTimeout(), true); if(log.isTraceEnabled()) log.trace("migrated " + key + " from " + local_addr + " to " + node); } } } l2_cache.stop(); disp.stop(); ch.close(); } @ManagedOperation public void put(K key, V val) { put(key, val, caching_time); } /** * Adds a key/value to the cache, replacing a previous item if there was one * @param key The key * @param val The value * @param caching_time Time to live. -1 means never cache, 0 means cache forever. All other (positive) values * are the number of milliseconds to cache the item */ @ManagedOperation public void put(K key, V val, long caching_time) { Address dest_node=getNode(key); if(dest_node.equals(local_addr)) { l2_cache.put(key, val, caching_time); } else { sendPut(dest_node, key, val, caching_time, false); } if(l1_cache != null && caching_time >= 0) l1_cache.put(key, val, caching_time); } @ManagedOperation public V get(K key) { if(l1_cache != null) { V val=l1_cache.get(key); if(val != null) { if(log.isTraceEnabled()) log.trace("returned value " + val + " for " + key + " from L1 cache"); return val; } } Cache.Value val; try { Address dest_node=getNode(key); // if we are the destination, don't invoke an RPC but return the item from our L2 cache directly ! if(dest_node.equals(local_addr)) { val=l2_cache.getEntry(key); } else { val=(Cache.Value)disp.callRemoteMethod(dest_node, new MethodCall(GET, new Object[]{key}), GroupRequest.GET_FIRST, call_timeout); } if(val != null) { V retval=val.getValue(); if(l1_cache != null && val.getTimeout() >= 0) l1_cache.put(key, retval, val.getTimeout()); return retval; } return null; } catch(Throwable t) { if(log.isWarnEnabled()) log.warn("_get() failed", t); return null; } } @ManagedOperation public void remove(K key) { Address dest_node=getNode(key); try { if(dest_node.equals(local_addr)) { l2_cache.remove(key); } else { disp.callRemoteMethod(dest_node, new MethodCall(REMOVE, new Object[]{key}), GroupRequest.GET_NONE, call_timeout); } if(l1_cache != null) l1_cache.remove(key); } catch(Throwable t) { if(log.isWarnEnabled()) log.warn("_remove() failed", t); } } public V _put(K key, V val, long caching_time) { if(log.isTraceEnabled()) log.trace("_put(" + key + ", " + val + ", " + caching_time + ")"); return l2_cache.put(key, val, caching_time); } public Cache.Value _get(K key) { if(log.isTraceEnabled()) log.trace("_get(" + key + ")"); return l2_cache.getEntry(key); } public V _remove(K key) { if(log.isTraceEnabled()) log.trace("_remove(" + key + ")"); return l2_cache.remove(key); } public void viewAccepted(View new_view) { System.out.println("view = " + new_view); this.view=new_view; for(MembershipListener l: membership_listeners) { l.viewAccepted(new_view); } if(migrate_data) { migrateData(); } } public void suspect(Address suspected_mbr) { } public void block() { } public String toString() { StringBuilder sb=new StringBuilder(); if(l1_cache != null) sb.append("L1 cache: " + l1_cache.getSize() + " entries"); sb.append("\nL2 cache: " + l2_cache.getSize() + "entries()"); return sb.toString(); } @ManagedOperation public String dump() { StringBuilder sb=new StringBuilder(); if(l1_cache != null) { sb.append("L1 cache:\n").append(l1_cache.dump()); } sb.append("\nL2 cache:\n").append(l2_cache.dump()); return sb.toString(); } private void migrateData() { for(Map.Entry> entry: l2_cache.entrySet()) { K key=entry.getKey(); Address node=getNode(key); if(!node.equals(local_addr)) { Cache.Value val=entry.getValue(); put(key, val.getValue(), val.getTimeout()); l2_cache.remove(key); if(log.isTraceEnabled()) log.trace("migrated " + key + " from " + local_addr + " to " + node); } } } private void sendPut(Address dest, K key, V val, long caching_time, boolean synchronous) { try { int mode=synchronous? GroupRequest.GET_ALL : GroupRequest.GET_NONE; disp.callRemoteMethod(dest, new MethodCall(PUT, new Object[]{key, val, caching_time}), mode, call_timeout); } catch(Throwable t) { if(log.isWarnEnabled()) log.warn("_put() failed", t); } } private Address getNode(K key) { return hash_function.hash(key, null); } public static class ConsistentHashFunction extends MembershipListenerAdapter implements HashFunction { private SortedMap nodes=new TreeMap(); private final static int HASH_SPACE=2000; // must be > max number of nodes in a cluster public Address hash(K key, List
members) { int hash=Math.abs(key.hashCode()); int index=hash % HASH_SPACE; if(members != null && !members.isEmpty()) { SortedMap tmp=new TreeMap(nodes); for(Iterator> it=tmp.entrySet().iterator(); it.hasNext();) { Map.Entry entry=it.next(); if(!members.contains(entry.getValue())) { it.remove(); } } return findFirst(tmp, index); } return findFirst(nodes, index); } public void viewAccepted(View new_view) { nodes.clear(); for(Address node: new_view.getMembers()) { int hash=Math.abs(node.hashCode()) % HASH_SPACE; for(int i=hash; i < hash + HASH_SPACE; i++) { short new_index=(short)(i % HASH_SPACE); if(!nodes.containsKey(new_index)) { nodes.put(new_index, node); break; } } } if(log.isTraceEnabled()) { StringBuilder sb=new StringBuilder("node mappings:\n"); for(Map.Entry entry: nodes.entrySet()) { sb.append(entry.getKey() + ": " + entry.getValue()).append("\n"); } log.trace(sb); } } private static Address findFirst(Map map, int index) { Address retval; for(int i=index; i < index + HASH_SPACE; i++) { short new_index=(short)(i % HASH_SPACE); retval=map.get(new_index); if(retval != null) return retval; } return null; } } /** * Uses arrays to store hash values of addresses, plus addresses. */ public static class ArrayBasedConsistentHashFunction extends MembershipListenerAdapter implements HashFunction { Object[] nodes=null; private final static int HASH_SPACE=2000; // must be > max number of nodes in a cluster public Address hash(K key, List
members) { int hash=Math.abs(key.hashCode()); int index=hash % HASH_SPACE; if(members != null && !members.isEmpty()) { Object[] tmp=new Object[nodes.length]; System.arraycopy(nodes, 0, tmp, 0, nodes.length); for(int i=0; i < tmp.length; i+=2) { if(!members.contains(tmp[i+1])) { tmp[i]=tmp[i+1]=null; } } return findFirst(tmp, index); } return findFirst(nodes, index); } public void viewAccepted(View new_view) { nodes=new Object[new_view.size() * 2]; int index=0; for(Address node: new_view.getMembers()) { int hash=Math.abs(node.hashCode()) % HASH_SPACE; nodes[index++]=hash; nodes[index++]=node; } if(log.isTraceEnabled()) { StringBuilder sb=new StringBuilder("node mappings:\n"); for(int i=0; i < nodes.length; i+=2) { sb.append(nodes[i] + ": " + nodes[i+1]).append("\n"); } log.trace(sb); } } public void suspect(Address suspected_mbr) { } public void block() { } private static Address findFirst(Object[] array, int index) { Address retval=null; if(array == null) return null; for(int i=0; i < array.length; i+=2) { if(array[i] == null) continue; if(array[i+1] != null) retval=(Address)array[i+1]; if(((Integer)array[i]) >= index) return (Address)array[i+1]; } return retval; } } private static class CustomMarshaller implements RpcDispatcher.Marshaller { static final byte NULL = 0; static final byte OBJ = 1; static final byte METHOD_CALL = 2; static final byte VALUE = 3; public byte[] objectToByteBuffer(Object obj) throws Exception { ByteArrayOutputStream out_stream=new ByteArrayOutputStream(35); DataOutputStream out=new DataOutputStream(out_stream); try { if(obj == null) { out_stream.write(NULL); out_stream.flush(); return out_stream.toByteArray(); } if(obj instanceof MethodCall) { out.writeByte(METHOD_CALL); MethodCall call=(MethodCall)obj; out.writeShort(call.getId()); Object[] args=call.getArgs(); if(args == null || args.length == 0) { out.writeShort(0); } else { out.writeShort(args.length); for(int i=0; i < args.length; i++) { Util.objectToStream(args[i], out); } } } else if(obj instanceof Cache.Value) { Cache.Value value=(Cache.Value)obj; out.writeByte(VALUE); out.writeLong(value.getTimeout()); Util.objectToStream(value.getValue(), out); } else { out.writeByte(OBJ); Util.objectToStream(obj, out); } out.flush(); return out_stream.toByteArray(); } finally { Util.close(out); } } public Object objectFromByteBuffer(byte[] buf) throws Exception { if(buf == null) return null; DataInputStream in=new DataInputStream(new ByteArrayInputStream(buf)); byte type=in.readByte(); if(type == NULL) return null; if(type == METHOD_CALL) { short id=in.readShort(); short length=in.readShort(); Object[] args=length > 0? new Object[length] : null; if(args != null) { for(int i=0; i < args.length; i++) args[i]=Util.objectFromStream(in); } return new MethodCall(id, args); } else if(type == VALUE) { long expiration_time=in.readLong(); Object obj=Util.objectFromStream(in); return new Cache.Value(obj, expiration_time); } else return Util.objectFromStream(in); } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/blocks/PullPushAdapter.java0000644000175000017500000004077511647260573027040 0ustar moellermoeller package org.jgroups.blocks; import org.jgroups.logging.Log; import org.jgroups.logging.LogFactory; import org.jgroups.*; import org.jgroups.conf.ClassConfigurator; import org.jgroups.util.Util; import java.io.*; 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 extends ChannelListenerAdapter implements Runnable { 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 short PULL_HEADER_ID=ClassConfigurator.getProtocolId(PullPushAdapter.class); 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_ID, 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).getAddress(); 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).getAddress(); 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_ID); 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 static final class PullHeader extends Header { Serializable identifier=null; public PullHeader() { } 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 writeTo(DataOutputStream out) throws IOException { try { Util.objectToStream(identifier, out); } catch(Exception e) { throw new IOException(e); } } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { try { identifier=(Serializable)Util.objectFromStream(in); } catch(Exception e) { throw new IOException(e); } } } /** * @return Returns the listener. */ public MessageListener getListener() { return listener; } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/blocks/ReplCache.java0000644000175000017500000010251611647260573025601 0ustar moellermoellerpackage org.jgroups.blocks; import org.jgroups.Address; import org.jgroups.JChannel; import org.jgroups.MembershipListener; import org.jgroups.View; import org.jgroups.annotations.Experimental; import org.jgroups.annotations.ManagedAttribute; import org.jgroups.annotations.ManagedOperation; import org.jgroups.annotations.Unsupported; import org.jgroups.logging.Log; import org.jgroups.logging.LogFactory; import org.jgroups.util.Rsp; import org.jgroups.util.RspList; import org.jgroups.util.TimeScheduler; import org.jgroups.util.Util; import java.io.*; import java.lang.reflect.Method; import java.util.*; import java.util.concurrent.TimeUnit; /** * Cache which allows for replication factors per data items; the factor determines how many replicas * of a key/value we create across the cluster.
* See doc/design/ReplCache.txt for details. * @author Bela Ban */ @Experimental @Unsupported public class ReplCache implements MembershipListener, Cache.ChangeListener { /** The cache in which all entries are located. The value is a tuple, consisting of the replication count and the * actual value */ private Cache> l2_cache=new Cache>(); /** The local bounded cache, to speed up access to frequently accessed entries. Can be disabled or enabled */ private Cache l1_cache=null; private static final Log log=LogFactory.getLog(ReplCache.class); private JChannel ch=null; private Address local_addr=null; private View view; private RpcDispatcher disp=null; @ManagedAttribute(writable=true) private String props="udp.xml"; @ManagedAttribute(writable=true) private String cluster_name="ReplCache-Cluster"; @ManagedAttribute(writable=true) private long call_timeout=1000L; @ManagedAttribute(writable=true) private long caching_time=30000L; // in milliseconds. -1 means don't cache, 0 means cache forever (or until changed) @ManagedAttribute private short default_replication_count=1; // no replication by default private HashFunction hash_function=null; private HashFunctionFactory hash_function_factory=new HashFunctionFactory() { public HashFunction create() { return new ConsistentHashFunction(); } }; private Set membership_listeners=new HashSet(); private Set change_listeners=new HashSet(); /** On a view change, if a member P1 detects that for any given key K, P1 is not the owner of K, then * it will compute the new owner P2 and transfer ownership for all Ks for which P2 is the new owner. P1 * will then also evict those keys from its L2 cache */ @ManagedAttribute(writable=true) private boolean migrate_data=true; private static final short PUT = 1; private static final short PUT_FORCE = 2; private static final short GET = 3; private static final short REMOVE = 4; private static final short REMOVE_MANY = 5; protected static Map methods=Util.createConcurrentMap(8); private TimeScheduler timer; static { try { methods.put(PUT, ReplCache.class.getMethod("_put", Object.class, Object.class, short.class, long.class)); methods.put(PUT_FORCE, ReplCache.class.getMethod("_put", Object.class, Object.class, short.class, long.class, boolean.class)); methods.put(GET, ReplCache.class.getMethod("_get", Object.class)); methods.put(REMOVE, ReplCache.class.getMethod("_remove", Object.class)); methods.put(REMOVE_MANY, ReplCache.class.getMethod("_removeMany", Set.class)); } catch(NoSuchMethodException e) { throw new RuntimeException(e); } } public interface HashFunction { /** * Function that, given a key and a replication count, returns replication_count number of different * addresses of nodes. * @param key * @param replication_count * @return */ List

hash(K key, short replication_count); /** * When the topology changes, this method will be called. Implementations will typically cache the node list * @param nodes */ void installNodes(List
nodes); } public interface HashFunctionFactory { HashFunction create(); } public ReplCache(String props, String cluster_name) { this.props=props; this.cluster_name=cluster_name; } public String getProps() { return props; } public void setProps(String props) { this.props=props; } public Address getLocalAddress() { return local_addr; } @ManagedAttribute public String getLocalAddressAsString() { return local_addr != null? local_addr.toString() : "null"; } @ManagedAttribute public String getView() { return view != null? view.toString() : "null"; } @ManagedAttribute public int getClusterSize() { return view != null? view.size() : 0; } @ManagedAttribute public boolean isL1CacheEnabled() { return l1_cache != null; } public String getClusterName() { return cluster_name; } public void setClusterName(String cluster_name) { this.cluster_name=cluster_name; } public long getCallTimeout() { return call_timeout; } public void setCallTimeout(long call_timeout) { this.call_timeout=call_timeout; } public long getCachingTime() { return caching_time; } public void setCachingTime(long caching_time) { this.caching_time=caching_time; } public boolean isMigrateData() { return migrate_data; } public void setMigrateData(boolean migrate_data) { this.migrate_data=migrate_data; } public short getDefaultReplicationCount() { return default_replication_count; } public void setDefaultReplicationCount(short default_replication_count) { this.default_replication_count=default_replication_count; } public HashFunction getHashFunction() { return hash_function; } public void setHashFunction(HashFunction hash_function) { this.hash_function=hash_function; } public HashFunctionFactory getHashFunctionFactory() { return hash_function_factory; } public void setHashFunctionFactory(HashFunctionFactory hash_function_factory) { this.hash_function_factory=hash_function_factory; } public void addMembershipListener(MembershipListener l) { membership_listeners.add(l); } public void removeMembershipListener(MembershipListener l) { membership_listeners.remove(l); } public void addChangeListener(ChangeListener l) { change_listeners.add(l); } public void removeChangeListener(ChangeListener l) { change_listeners.remove(l); } public Cache getL1Cache() { return l1_cache; } public void setL1Cache(Cache cache) { if(l1_cache != null) l1_cache.stop(); l1_cache=cache; } public Cache> getL2Cache() { return l2_cache; } public void setL2Cache(Cache> cache) { if(cache != null) { l2_cache.stop(); l2_cache=cache; } } @ManagedOperation public void start() throws Exception { if(hash_function_factory != null) { hash_function=hash_function_factory.create(); } if(hash_function == null) hash_function=new ConsistentHashFunction(); ch=new JChannel(props); disp=new RpcDispatcher(ch, null, this, this); RpcDispatcher.Marshaller marshaller=new CustomMarshaller(); disp.setRequestMarshaller(marshaller); disp.setResponseMarshaller(marshaller); disp.setMethodLookup(new MethodLookup() { public Method findMethod(short id) { return methods.get(id); } }); ch.connect(cluster_name); local_addr=ch.getAddress(); view=ch.getView(); timer=ch.getProtocolStack().getTransport().getTimer(); l2_cache.addChangeListener(this); } @ManagedOperation public void stop() { if(l1_cache != null) l1_cache.stop(); if(migrate_data) { List
members_without_me=new ArrayList
(view.getMembers()); members_without_me.remove(local_addr); HashFunction tmp_hash_function=hash_function_factory.create(); tmp_hash_function.installNodes(members_without_me); for(Map.Entry>> entry: l2_cache.entrySet()) { K key=entry.getKey(); Cache.Value> val=entry.getValue(); if(val == null) continue; Value tmp=val.getValue(); if(tmp == null) continue; short repl_count=tmp.getReplicationCount(); if(repl_count != 1) // we only handle keys which are not replicated and which are stored by us continue; List
nodes=tmp_hash_function.hash(key, repl_count); if(nodes == null || nodes.isEmpty()) continue; if(!nodes.contains(local_addr)) { Address dest=nodes.get(0); // should only have 1 element anyway move(dest, key, tmp.getVal(), repl_count, val.getTimeout(), true); _remove(key); } } } l2_cache.removeChangeListener(this); l2_cache.stop(); disp.stop(); ch.close(); } /** * Places a key/value pair into one or several nodes in the cluster. * @param key The key, needs to be serializable * @param val The value, needs to be serializable * @param repl_count Number of replicas. The total number of times a data item should be present in a cluster. * Needs to be > 0 *
    *
  • -1: create key/val in all the nodes in the cluster *
  • 1: create key/val only in one node in the cluster, picked by computing the consistent hash of KEY *
  • K > 1: create key/val in those nodes in the cluster which match the consistent hashes created for KEY *
* @param timeout Expiration time for key/value. *
    *
  • -1: don't cache at all in the L1 cache *
  • 0: cache forever, until removed or evicted because we need space for newer elements *
  • > 0: number of milliseconds to keep an idle element in the cache. An element is idle when not accessed. *
* @param synchronous Whether or not to block until all cluster nodes have applied the change */ @ManagedOperation public void put(K key, V val, short repl_count, long timeout, boolean synchronous) { if(repl_count == 0) { if(log.isWarnEnabled()) log.warn("repl_count of 0 is invalid, data will not be stored in the cluster"); return; } mcastPut(key, val, repl_count, timeout, synchronous); if(l1_cache != null && timeout >= 0) l1_cache.put(key, val, timeout); } /** * Places a key/value pair into one or several nodes in the cluster. * @param key The key, needs to be serializable * @param val The value, needs to be serializable * @param repl_count Number of replicas. The total number of times a data item should be present in a cluster. * Needs to be > 0 *
    *
  • -1: create key/val in all the nodes in the cluster *
  • 1: create key/val only in one node in the cluster, picked by computing the consistent hash of KEY *
  • K > 1: create key/val in those nodes in the cluster which match the consistent hashes created for KEY *
* @param timeout Expiration time for key/value. *
    *
  • -1: don't cache at all in the L1 cache *
  • 0: cache forever, until removed or evicted because we need space for newer elements *
  • > 0: number of milliseconds to keep an idle element in the cache. An element is idle when not accessed. *
*/ @ManagedOperation public void put(K key, V val, short repl_count, long timeout) { put(key, val, repl_count, timeout, false); // don't block (asynchronous put) by default } @ManagedOperation public void put(K key, V val) { put(key, val, default_replication_count, caching_time); } /** * Returns the value associated with key * @param key The key, has to be serializable * @return The value associated with key, or null */ @ManagedOperation public V get(K key) { // 1. Try the L1 cache first if(l1_cache != null) { V val=l1_cache.get(key); if(val != null) { if(log.isTraceEnabled()) log.trace("returned value " + val + " for " + key + " from L1 cache"); return val; } } // 2. Try the local cache Cache.Value> val=l2_cache.getEntry(key); Value tmp; if(val != null) { tmp=val.getValue(); if(tmp !=null) { V real_value=tmp.getVal(); if(real_value != null && l1_cache != null && val.getTimeout() >= 0) l1_cache.put(key, real_value, val.getTimeout()); return tmp.getVal(); } } // 3. Execute a cluster wide GET try { RspList rsps=disp.callRemoteMethods(null, new MethodCall(GET, new Object[]{key}), GroupRequest.GET_ALL, call_timeout); for(Rsp rsp: rsps.values()) { Object obj=rsp.getValue(); if(obj == null || obj instanceof Throwable) continue; val=(Cache.Value>)rsp.getValue(); if(val != null) { tmp=val.getValue(); if(tmp != null) { V real_value=tmp.getVal(); if(real_value != null && l1_cache != null && val.getTimeout() >= 0) l1_cache.put(key, real_value, val.getTimeout()); return real_value; } } } return null; } catch(Throwable t) { if(log.isWarnEnabled()) log.warn("get() failed", t); return null; } } /** * Removes key in all nodes in the cluster, both from their local hashmaps and L1 caches * @param key The key, needs to be serializable */ @ManagedOperation public void remove(K key) { remove(key, false); // by default we use asynchronous removals } /** * Removes key in all nodes in the cluster, both from their local hashmaps and L1 caches * @param key The key, needs to be serializable */ @ManagedOperation public void remove(K key, boolean synchronous) { try { disp.callRemoteMethods(null, new MethodCall(REMOVE, new Object[]{key}), synchronous? GroupRequest.GET_ALL : GroupRequest.GET_NONE, call_timeout); if(l1_cache != null) l1_cache.remove(key); } catch(Throwable t) { if(log.isWarnEnabled()) log.warn("remove() failed", t); } } /** * Removes all keys and values in the L2 and L1 caches */ @ManagedOperation public void clear() { Set keys=new HashSet(l2_cache.getInternalMap().keySet()); mcastClear(keys, false); } public V _put(K key, V val, short repl_count, long timeout) { return _put(key, val, repl_count, timeout, false); } /** * * @param key * @param val * @param repl_count * @param timeout * @param force Skips acceptance checking and simply adds the key/value * @return */ public V _put(K key, V val, short repl_count, long timeout, boolean force) { if(!force) { // check if we need to host the data boolean accept=repl_count == -1; if(!accept) { if(view != null && repl_count >= view.size()) { accept=true; } else { List
selected_hosts=hash_function != null? hash_function.hash(key, repl_count) : null; if(selected_hosts != null) { if(log.isTraceEnabled()) log.trace("local=" + local_addr + ", hosts=" + selected_hosts); for(Address addr: selected_hosts) { if(addr.equals(local_addr)) { accept=true; break; } } } if(!accept) return null; } } } if(log.isTraceEnabled()) log.trace("_put(" + key + ", " + val + ", " + repl_count + ", " + timeout + ")"); Value value=new Value(val, repl_count); Value retval=l2_cache.put(key, value, timeout); if(l1_cache != null) l1_cache.remove(key); notifyChangeListeners(); return retval != null? retval.getVal() : null; } public Cache.Value> _get(K key) { if(log.isTraceEnabled()) log.trace("_get(" + key + ")"); return l2_cache.getEntry(key); } public V _remove(K key) { if(log.isTraceEnabled()) log.trace("_remove(" + key + ")"); Value retval=l2_cache.remove(key); if(l1_cache != null) l1_cache.remove(key); notifyChangeListeners(); return retval != null? retval.getVal() : null; } public void _removeMany(Set keys) { if(log.isTraceEnabled()) log.trace("_removeMany(): " + keys.size() + " entries"); for(K key: keys) _remove(key); } public void viewAccepted(final View new_view) { final List
old_nodes=this.view != null? new ArrayList
(this.view.getMembers()) : null; this.view=new_view; if(log.isInfoEnabled()) log.info("new view: " + new_view); if(hash_function != null) hash_function.installNodes(new_view.getMembers()); for(MembershipListener l: membership_listeners) l.viewAccepted(new_view); if(old_nodes != null) { timer.schedule(new Runnable() { public void run() { rebalance(old_nodes, new ArrayList
(new_view.getMembers())); } }, 100, TimeUnit.MILLISECONDS); } } public void suspect(Address suspected_mbr) { } public void block() { } public void changed() { notifyChangeListeners(); } public String toString() { StringBuilder sb=new StringBuilder(); if(l1_cache != null) sb.append("L1 cache: " + l1_cache.getSize() + " entries"); sb.append("\nL2 cache: " + l2_cache.getSize() + " entries()"); return sb.toString(); } @ManagedOperation public String dump() { StringBuilder sb=new StringBuilder(); if(l1_cache != null) { sb.append("L1 cache:\n").append(l1_cache.dump()); } sb.append("\nL2 cache:\n").append(l2_cache.dump()); return sb.toString(); } private void notifyChangeListeners() { for(ChangeListener l: change_listeners) { try { l.changed(); } catch(Throwable t) { if(log.isErrorEnabled()) log.error("failed notifying change listener", t); } } } private void rebalance(List
old_nodes, List
new_nodes) { HashFunction old_func=hash_function_factory.create(); old_func.installNodes(old_nodes); HashFunction new_func=hash_function_factory.create(); new_func.installNodes(new_nodes); boolean is_coord=Util.isCoordinator(ch); List keys=new ArrayList(l2_cache.getInternalMap().keySet()); for(K key: keys) { Cache.Value> val=l2_cache.getEntry(key); if(log.isTraceEnabled()) log.trace("==== rebalancing " + key); if(val == null) { if(log.isWarnEnabled()) log.warn(key + " has no value associated; ignoring"); continue; } Value tmp=val.getValue(); if(tmp == null) { if(log.isWarnEnabled()) log.warn(key + " has no value associated; ignoring"); continue; } V real_value=tmp.getVal(); short repl_count=tmp.getReplicationCount(); List
new_mbrs=Util.newMembers(old_nodes, new_nodes); if(repl_count == -1) { if(is_coord) { for(Address new_mbr: new_mbrs) { move(new_mbr, key, real_value, repl_count, val.getTimeout(), false); } } } else if(repl_count == 1) { List
tmp_nodes=new_func.hash(key, repl_count); if(!tmp_nodes.isEmpty()) { Address mbr=tmp_nodes.get(0); if(!mbr.equals(local_addr)) { move(mbr, key, real_value, repl_count, val.getTimeout(), false); _remove(key); } } } else if(repl_count > 1) { List
tmp_old=old_func.hash(key, repl_count); List
tmp_new=new_func.hash(key, repl_count); if(log.isTraceEnabled()) log.trace("old nodes: " + tmp_old + "\nnew nodes: " + tmp_new); if(tmp_old != null && tmp_new != null && tmp_old.equals(tmp_new)) continue; mcastPut(key, real_value, repl_count, val.getTimeout(), false); if(tmp_new != null && !tmp_new.contains(local_addr)) { _remove(key); } } else { throw new IllegalStateException("replication count is invalid (" + repl_count + ")"); } } } public void mcastEntries() { for(Map.Entry>> entry: l2_cache.entrySet()) { K key=entry.getKey(); Cache.Value> val=entry.getValue(); if(val == null) { if(log.isWarnEnabled()) log.warn(key + " has no value associated; ignoring"); continue; } Value tmp=val.getValue(); if(tmp == null) { if(log.isWarnEnabled()) log.warn(key + " has no value associated; ignoring"); continue; } V real_value=tmp.getVal(); short repl_count=tmp.getReplicationCount(); if(repl_count > 1) { _remove(key); mcastPut(key, real_value, repl_count, val.getTimeout(), false); } } } private void mcastPut(K key, V val, short repl_count, long caching_time, boolean synchronous) { try { int mode=synchronous? GroupRequest.GET_ALL : GroupRequest.GET_NONE; disp.callRemoteMethods(null, new MethodCall(PUT, new Object[]{key, val, repl_count, caching_time}), mode, call_timeout); } catch(Throwable t) { if(log.isWarnEnabled()) log.warn("put() failed", t); } } private void mcastClear(Set keys, boolean synchronous) { try { int mode=synchronous? GroupRequest.GET_ALL : GroupRequest.GET_NONE; disp.callRemoteMethods(null, new MethodCall(REMOVE_MANY, new Object[]{keys}), mode, call_timeout); } catch(Throwable t) { if(log.isWarnEnabled()) log.warn("clear() failed", t); } } private void move(Address dest, K key, V val, short repl_count, long caching_time, boolean synchronous) { try { int mode=synchronous? GroupRequest.GET_ALL : GroupRequest.GET_NONE; disp.callRemoteMethod(dest, new MethodCall(PUT_FORCE, new Object[]{key, val, repl_count, caching_time, true}), mode, call_timeout); } catch(Throwable t) { if(log.isWarnEnabled()) log.warn("move() failed", t); } } public static interface ChangeListener { void changed(); } public static class ConsistentHashFunction implements HashFunction { private SortedMap nodes=new TreeMap(); private final static int HASH_SPACE=2000; // must be > max number of nodes in a cluster private final static int FACTOR=3737; // to better spread the node out across the space public List
hash(K key, short replication_count) { int hash=Math.abs(key.hashCode()); int index=hash % HASH_SPACE; Set
results=new LinkedHashSet
(); List
retval=new ArrayList
(); SortedMap tailmap=nodes.tailMap((short)index); int count=0; for(Map.Entry entry: tailmap.entrySet()) { Address val=entry.getValue(); results.add(val); if(++count >= replication_count) break; } if(count < replication_count) { for(Map.Entry entry: nodes.entrySet()) { Address val=entry.getValue(); results.add(val); if(++count >= replication_count) break; } } retval.addAll(results); return retval; } public void installNodes(List
new_nodes) { nodes.clear(); for(Address node: new_nodes) { int hash=Math.abs(node.hashCode() * FACTOR) % HASH_SPACE; for(int i=hash; i < hash + HASH_SPACE; i++) { short new_index=(short)(i % HASH_SPACE); if(!nodes.containsKey(new_index)) { nodes.put(new_index, node); break; } } } if(log.isTraceEnabled()) { StringBuilder sb=new StringBuilder("node mappings:\n"); for(Map.Entry entry: nodes.entrySet()) { sb.append(entry.getKey() + ": " + entry.getValue()).append("\n"); } log.trace(sb); } } } /** * Uses arrays to store hash values of addresses, plus addresses. */ /* public static class ArrayBasedConsistentHashFunction extends MembershipListenerAdapter implements HashFunction { Object[] nodes=null; private final static int HASH_SPACE=2000; // must be > max number of nodes in a cluster public Address hash(K key, List
members) { int hash=Math.abs(key.hashCode()); int index=hash % HASH_SPACE; if(members != null && !members.isEmpty()) { Object[] tmp=new Object[nodes.length]; System.arraycopy(nodes, 0, tmp, 0, nodes.length); for(int i=0; i < tmp.length; i+=2) { if(!members.contains(tmp[i+1])) { tmp[i]=tmp[i+1]=null; } } return findFirst(tmp, index); } return findFirst(nodes, index); } public void viewAccepted(View new_view) { nodes=new Object[new_view.size() * 2]; int index=0; for(Address node: new_view.getMembers()) { int hash=Math.abs(node.hashCode()) % HASH_SPACE; nodes[index++]=hash; nodes[index++]=node; } if(log.isTraceEnabled()) { StringBuilder sb=new StringBuilder("node mappings:\n"); for(int i=0; i < nodes.length; i+=2) { sb.append(nodes[i] + ": " + nodes[i+1]).append("\n"); } log.trace(sb); } } public void suspect(Address suspected_mbr) { } public void block() { } private static Address findFirst(Object[] array, int index) { Address retval=null; if(array == null) return null; for(int i=0; i < array.length; i+=2) { if(array[i] == null) continue; if(array[i+1] != null) retval=(Address)array[i+1]; if(((Integer)array[i]) >= index) return (Address)array[i+1]; } return retval; } }*/ public static class Value implements Serializable { private final V val; private final short replication_count; private static final long serialVersionUID=-2892941069742740027L; public Value(V val, short replication_count) { this.val=val; this.replication_count=replication_count; } public V getVal() { return val; } public short getReplicationCount() { return replication_count; } public String toString() { return val + " (" + replication_count + ")"; } } private static class CustomMarshaller implements RpcDispatcher.Marshaller { static final byte NULL = 0; static final byte OBJ = 1; static final byte METHOD_CALL = 2; static final byte VALUE = 3; public byte[] objectToByteBuffer(Object obj) throws Exception { ByteArrayOutputStream out_stream=new ByteArrayOutputStream(35); DataOutputStream out=new DataOutputStream(out_stream); try { if(obj == null) { out_stream.write(NULL); out_stream.flush(); return out_stream.toByteArray(); } if(obj instanceof MethodCall) { out.writeByte(METHOD_CALL); MethodCall call=(MethodCall)obj; out.writeShort(call.getId()); Object[] args=call.getArgs(); if(args == null || args.length == 0) { out.writeShort(0); } else { out.writeShort(args.length); for(int i=0; i < args.length; i++) { Util.objectToStream(args[i], out); } } } else if(obj instanceof Cache.Value) { Cache.Value value=(Cache.Value)obj; out.writeByte(VALUE); out.writeLong(value.getTimeout()); Util.objectToStream(value.getValue(), out); } else { out.writeByte(OBJ); Util.objectToStream(obj, out); } out.flush(); return out_stream.toByteArray(); } finally { Util.close(out); } } public Object objectFromByteBuffer(byte[] buf) throws Exception { if(buf == null) return null; DataInputStream in=new DataInputStream(new ByteArrayInputStream(buf)); byte type=in.readByte(); if(type == NULL) return null; if(type == METHOD_CALL) { short id=in.readShort(); short length=in.readShort(); Object[] args=length > 0? new Object[length] : null; if(args != null) { for(int i=0; i < args.length; i++) args[i]=Util.objectFromStream(in); } return new MethodCall(id, args); } else if(type == VALUE) { long expiration_time=in.readLong(); Object obj=Util.objectFromStream(in); return new Cache.Value(obj, expiration_time); } else return Util.objectFromStream(in); } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/blocks/ReplicatedHashMap.java0000644000175000017500000011040611647260573027266 0ustar moellermoellerpackage org.jgroups.blocks; import org.jgroups.*; import org.jgroups.annotations.Unsupported; import org.jgroups.logging.Log; import org.jgroups.logging.LogFactory; 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.ConcurrentMap; 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 */ @Unsupported(comment="Use JBossCache instead") public class ReplicatedHashMap extends AbstractMap implements ConcurrentMap, ExtendedReceiver, ReplicatedMap { public interface Notification { void entrySet(K key, V value); void entryRemoved(K key); void viewChange(View view, Vector

mbrs_joined, Vector
mbrs_left); 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 Channel channel; protected RpcDispatcher disp=null; private String cluster_name=null; // to be notified when mbrship changes private final Set notifs=new CopyOnWriteArraySet(); private final Vector
members=new Vector
(); // keeps track of all DHTs private boolean persistent=false; // whether to use PersistenceManager to save state private 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 volatile boolean send_message=false; protected final Promise state_promise=new Promise(); /** * Whether updates across the cluster should be asynchronous (default) or * synchronous) */ protected int update_mode=Request.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()); /** * Wrapped map instance. */ protected ConcurrentMap map=null; /** * 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(clustername, factory, properties, false, state_timeout); } /** * Creates a ReplicatedHashMap. Optionally the contents can be saved to * persistent 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(createChannel(clustername, factory, properties), persistent); channel.connect(clustername); start(state_timeout); } private static Channel createChannel(String clustername, ChannelFactory factory, String properties) throws ChannelException { Channel channel; if(factory != null) { channel=properties != null? factory.createChannel((Object)properties) : factory.createChannel(); } else { channel=new JChannel(properties); } return channel; } /** * Constructs a new ReplicatedHashMap with channel. Call {@link #start} to * start this map. */ public ReplicatedHashMap(Channel channel) { this(channel, false); } /** * Constructs a new ReplicatedHashMap with channel and persistence option. * Call {@link #start} to start this map. */ public ReplicatedHashMap(Channel channel,boolean persistent) { this(new ConcurrentHashMap(), channel, persistent); } /** * Constructs a new ReplicatedHashMap using provided map instance. */ public ReplicatedHashMap(ConcurrentMap map,Channel channel,boolean persistent) { if(channel == null) throw new IllegalArgumentException("Cannot create ReplicatedHashMap with null channel"); if(map == null) throw new IllegalArgumentException("Cannot create ReplicatedHashMap with null map"); this.map=map; 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 { if(!channel.isConnected()) throw new ChannelNotConnectedException(); if(!channel.isOpen()) throw new ChannelClosedException(); send_message = channel.getView().size()>1; 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.getAddress() : 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) { 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) { 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) { try { MethodCall call=new MethodCall(PUT_ALL, 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) { try { MethodCall call=new MethodCall(CLEAR); 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) { try { MethodCall call=new MethodCall(REMOVE, 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) { try { MethodCall call=new MethodCall(REMOVE_IF_EQUALS, 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) { try { MethodCall call=new MethodCall(REPLACE_IF_EQUALS, 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) { try { MethodCall call=new MethodCall(REPLACE_IF_EXISTS, 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=map.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=map.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()) { this.map.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() { map.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=map.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=map.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=map.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=map.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); } 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) {} /*------------------- 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 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); } } } // Delegate Methods @Override public boolean containsKey(Object key) { return map.containsKey(key); } @Override public boolean containsValue(Object value) { return map.containsValue(value); } @Override public Set> entrySet() { return map.entrySet(); } @Override public V get(Object key) { return map.get(key); } @Override public Set keySet() { return map.keySet(); } @Override public int size() { return map.size(); } @Override public Collection values() { return map.values(); } /** * Legacy method. * * @deprecated */ public boolean contains(Object value) { return containsValue(value); } /** * Legacy method. * * @deprecated */ public Enumeration keys() { return Collections.enumeration(keySet()); } /** * Legacy method. * * @deprecated */ public Enumeration elements() { return Collections.enumeration(values()); } }libjgroups-java-2.12.2.Final.orig/src/org/jgroups/blocks/ReplicatedMap.java0000644000175000017500000000106511647260573026462 0ustar moellermoellerpackage org.jgroups.blocks; import java.io.Serializable; import java.util.Map; import java.util.concurrent.ConcurrentMap; /** * @author Bela Ban */ 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); } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/blocks/ReplicatedTree.java0000644000175000017500000007531111647260573026651 0ustar moellermoeller package org.jgroups.blocks; import org.jgroups.*; import org.jgroups.annotations.Unsupported; import org.jgroups.jmx.JmxConfigurator; import org.jgroups.logging.Log; import org.jgroups.logging.LogFactory; 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 */ @Unsupported public class ReplicatedTree extends ReceiverAdapter { public static final String SEPARATOR="/"; final static int INDENT=4; Node root=new Node(SEPARATOR, SEPARATOR, null, null); final Vector listeners=new Vector(); JChannel channel=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.setReceiver(this); 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.setReceiver(this); 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; channel.setReceiver(this); start(); } public void setRemoteCalls(boolean flag) { remote_calls=flag; } public void setRootNode(Node n) { root=n; } public Address getLocalAddress() { return channel != null? channel.getAddress() : 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 { 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() { Util.close(channel); } /** * 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 */ Map 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(); String 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(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; } /*------------------- End of MembershipListener ----------------------*/ /** * 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++) listeners.elementAt(i).nodeAdded(fqn); } void notifyNodeRemoved(String fqn) { for(int i=0; i < listeners.size(); i++) listeners.elementAt(i).nodeRemoved(fqn); } void notifyNodeModified(String fqn) { for(int i=0; i < listeners.size(); i++) listeners.elementAt(i).nodeModified(fqn); } void notifyViewChange(View v) { for(int i=0; i < listeners.size(); i++) 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 Map data=null; // data for current node private static final long serialVersionUID = -3077676554440038890L; private Node(String child_name, String fqn, Node parent, Map data) { name=child_name; this.fqn=fqn; this.parent=parent; if(data != null) this.data=(HashMap)((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); } Map getData() { return data; } Object getData(String key) { return data != null? data.get(key) : null; } boolean childExists(String child_name) { return child_name != null && 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=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.isEmpty()) { Collection values=children.values(); for(Iterator it=values.iterator(); it.hasNext();) { sb.append('\n'); ((Node)it.next()).print(sb, indent + INDENT); } } } static 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(mcast_addr=224.0.0.36;mcast_port=55566;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.STABLE(desired_avg_gossip=20000):" + "pbcast.NAKACK(gc_lag=50;retransmit_timeout=600,1200,2400,4800):" + "UNICAST(timeout=5000):" + "FRAG(frag_size=16000;down_thread=false;up_thread=false):" + "pbcast.GMS(join_timeout=5000;" + "print_local_addr=true):" + "pbcast.STATE_TRANSFER"; // "PERF(details=true)"; 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); } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/blocks/Request.java0000644000175000017500000001754411647260573025411 0ustar moellermoellerpackage org.jgroups.blocks; import org.jgroups.Address; import org.jgroups.Message; import org.jgroups.Transport; import org.jgroups.View; import org.jgroups.annotations.GuardedBy; import org.jgroups.logging.Log; import org.jgroups.logging.LogFactory; import org.jgroups.util.Command; import org.jgroups.util.FutureListener; import org.jgroups.util.NotifyingFuture; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * Abstract class for a unicast or multicast request * * @author Bela Ban */ public abstract class Request implements RspCollector, Command, NotifyingFuture { /** 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) */ @Deprecated public static final int GET_N=5; /** return no response (async call) */ public static final int GET_NONE=6; protected static final Log log=LogFactory.getLog(Request.class); /** To generate unique request IDs (see getRequestId()) */ protected static final AtomicLong REQUEST_ID=new AtomicLong(1); protected final Lock lock=new ReentrantLock(); /** Is set as soon as the request has received all required responses */ protected final Condition completed=lock.newCondition(); protected final Message request_msg; protected final RequestCorrelator corr; // either use RequestCorrelator or ... protected final Transport transport; // Transport (one of them has to be non-null) protected final RequestOptions options; protected volatile boolean done; protected boolean block_for_results=true; protected final long req_id; // request ID for this request protected volatile FutureListener listener; @Deprecated public Request(Message request, RequestCorrelator corr, Transport transport, RspFilter filter, int mode, long timeout) { this(request, corr, transport, new RequestOptions(mode, timeout, false, filter)); } public Request(Message request, RequestCorrelator corr, Transport transport, RequestOptions options) { this.request_msg=request; this.corr=corr; this.transport=transport; this.options=options; this.req_id=getRequestId(); } public void setResponseFilter(RspFilter filter) { options.setRspFilter(filter); } public boolean getBlockForResults() { return block_for_results; } public void setBlockForResults(boolean block_for_results) { this.block_for_results=block_for_results; } public NotifyingFuture setListener(FutureListener listener) { this.listener=listener; if(done) listener.futureDone(this); return this; } public boolean execute() throws Exception { if(corr == null && transport == null) { if(log.isErrorEnabled()) log.error("both corr and transport are null, cannot send group request"); return false; } sendRequest(); if(!block_for_results || options.getMode() == GET_NONE) return true; lock.lock(); try { return responsesComplete(options.getTimeout()); } finally { done=true; lock.unlock(); } } protected abstract void sendRequest() throws Exception; public abstract void receiveResponse(Object response_value, Address sender); public abstract void viewChange(View new_view); public abstract void suspect(Address mbr); protected abstract boolean responsesComplete(); public boolean getResponsesComplete() { lock.lock(); try { return responsesComplete(); } finally { lock.unlock(); } } public boolean cancel(boolean mayInterruptIfRunning) { lock.lock(); try { boolean retval=!done; done=true; if(corr != null) corr.done(req_id); completed.signalAll(); return retval; } finally { lock.unlock(); } } public boolean isCancelled() { lock.lock(); try { return done; } finally { lock.unlock(); } } public boolean isDone() { return done; } public String toString() { StringBuilder ret=new StringBuilder(128); ret.append(super.toString()); ret.append("req_id=").append(req_id).append(", mode=" + modeToString(options.getMode())); return ret.toString(); } /* --------------------------------- Private Methods -------------------------------------*/ protected void checkCompletion(Future future) { if(listener != null && responsesComplete()) listener.futureDone(future); } /** Generates a new unique request ID */ protected static long getRequestId() { return REQUEST_ID.incrementAndGet(); } /** This method runs with lock locked (called by execute()). */ @GuardedBy("lock") protected boolean responsesComplete(long timeout) throws InterruptedException { if(timeout <= 0) { while(!done) { /* Wait for responses: */ if(responsesComplete()) { if(corr != null) corr.done(req_id); return true; } completed.await(); } return responsesComplete(); } else { long start_time=System.currentTimeMillis(); long timeout_time=start_time + timeout; while(timeout > 0 && !done) { /* Wait for responses: */ if(responsesComplete()) { if(corr != null) corr.done(req_id); return true; } timeout=timeout_time - System.currentTimeMillis(); if(timeout > 0) { completed.await(timeout, TimeUnit.MILLISECONDS); } } if(corr != null) corr.done(req_id); return responsesComplete(); } } @GuardedBy("lock") protected boolean waitForResults(long timeout) { if(timeout <= 0) { while(true) { /* Wait for responses: */ if(responsesComplete()) return true; try {completed.await();} catch(Exception e) {} } } else { long start_time=System.currentTimeMillis(); long timeout_time=start_time + timeout; while(timeout > 0) { /* Wait for responses: */ if(responsesComplete()) return true; timeout=timeout_time - System.currentTimeMillis(); if(timeout > 0) { try {completed.await(timeout, TimeUnit.MILLISECONDS);} catch(Exception e) {} } } return false; } } public 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 + ")"; } } }libjgroups-java-2.12.2.Final.orig/src/org/jgroups/blocks/RequestCorrelator.java0000644000175000017500000007020111647260573027433 0ustar moellermoeller package org.jgroups.blocks; import org.jgroups.*; import org.jgroups.conf.ClassConfigurator; import org.jgroups.logging.Log; import org.jgroups.logging.LogFactory; import org.jgroups.protocols.TP; import org.jgroups.stack.Protocol; import org.jgroups.util.Buffer; import org.jgroups.util.Util; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.util.*; 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=Util.createConcurrentMap(); /** 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 short id=ClassConfigurator.getProtocolId(this.getClass()); /** The address of this group member */ protected Address local_addr=null; 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. */ @Deprecated public RequestCorrelator(String name, Object transport, RequestHandler handler) { this.transport = transport; request_handler = handler; start(); } @Deprecated public RequestCorrelator(String name, Object transport, RequestHandler handler, Address local_addr) { this.transport = transport; this.local_addr=local_addr; request_handler = handler; start(); } public RequestCorrelator(short id, Object transport, RequestHandler handler, Address local_addr) { this.id = id; this.transport = transport; this.local_addr = local_addr; request_handler = handler; start(); } public RequestCorrelator(Object transport, RequestHandler handler, Address local_addr) { 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. */ @Deprecated public RequestCorrelator(String name, Object transport, RequestHandler handler, boolean deadlock_detection) { this.transport = transport; request_handler = handler; start(); } @Deprecated public RequestCorrelator(String name, Object transport, RequestHandler handler, boolean deadlock_detection, boolean concurrent_processing) { this.transport = transport; request_handler = handler; start(); } @Deprecated public RequestCorrelator(String name, Object transport, RequestHandler handler, boolean deadlock_detection, Address local_addr) { this.transport = transport; this.local_addr = local_addr; request_handler = handler; start(); } @Deprecated public RequestCorrelator(String name, Object transport, RequestHandler handler, boolean deadlock_detection, Address local_addr, boolean concurrent_processing) { this.transport = transport; this.local_addr = local_addr; request_handler = handler; start(); } @Deprecated public RequestCorrelator(String name, Object transport, RequestHandler handler, Address local_addr, boolean concurrent_processing) { this.transport = transport; this.local_addr = local_addr; request_handler = handler; start(); } /** * Switch the deadlock detection mechanism on/off * @param flag the deadlock detection flag * @deprecated deadlock detection is not needed with a concurrent stack */ public void setDeadlockDetection(boolean flag) { } public void setRequestHandler(RequestHandler handler) { request_handler=handler; start(); } /** * @deprecated Not needed since the introduction of the concurrent stack * @param concurrent_processing */ public void setConcurrentProcessing(boolean concurrent_processing) { } /** * Helper method for {@link #sendRequest(long,List,Message,RspCollector)}. */ @Deprecated 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, new RequestOptions().setAnycasting(false)); } /** * Sends 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, Collection
dest_mbrs, Message msg, RspCollector coll, RequestOptions options) throws Exception { 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 (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 Header hdr=options.hasExclusionList()? new MultiDestinationHeader(Header.REQ, id, (coll != null), this.id, options.getExclusionList()) : new Header(Header.REQ, id, (coll != null), this.id); msg.putHeader(this.id, hdr); if(coll != null) addEntry(hdr.id, coll); if(transport instanceof Protocol) { if(options.getAnycasting()) { for(Address mbr: dest_mbrs) { Message 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(options.getAnycasting()) { for(Address mbr: dest_mbrs) { Message 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()); } /** * Sends a request to a single destination * @param id * @param target * @param msg * @param coll * @throws Exception */ public void sendUnicastRequest(long id, Address target, Message msg, RspCollector coll) throws Exception { 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 (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 Header hdr=new Header(Header.REQ, id, (coll != null), this.id); msg.putHeader(this.id, hdr); if(coll != null) addEntry(hdr.id, coll); if(transport instanceof Protocol) { ((Protocol)transport).down(new Event(Event.MSG, msg)); } else if(transport instanceof Transport) { ((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; } return false; } public final void start() { started=true; } public void stop() { started=false; } 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(this.id); if(hdr == null) return false; if(hdr.corrId != this.id) { if(log.isTraceEnabled()) { log.trace(new StringBuilder("id of request correlator header (").append(hdr.corrId). append(") is different from ours (").append(this.id).append("). Msg not accepted, passed up")); } return false; } if(hdr instanceof MultiDestinationHeader) { // If the header contains an exclusion list, and we are part of it, then we discard the // request (was addressed to other members) java.util.Collection exclusion_list=((MultiDestinationHeader)hdr).exclusion_list; if(exclusion_list != null && local_addr != null && exclusion_list.contains(local_addr)) { if(log.isTraceEnabled()) { log.trace(new StringBuilder("discarded request from ").append(msg.getSrc()). append(" as we are in the exclusion 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; } handleRequest(msg, hdr); break; case Header.RSP: RspCollector coll=requests.get(hdr.id); if(coll != null) { Address sender=msg.getSrc(); Object retval; 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(this.id); 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) { // 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); } /** * 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(); prepareResponse(rsp); rsp.setFlag(Message.OOB); rsp.setFlag(Message.DONT_BUNDLE); if(req.isFlagSet(Message.NO_FC)) rsp.setFlag(Message.NO_FC); if(req.isFlagSet(Message.NO_RELIABILITY)) rsp.setFlag(Message.NO_RELIABILITY); if(req.isFlagSet(Message.NO_TOTAL_ORDER)) rsp.setFlag(Message.NO_TOTAL_ORDER); 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, this.id); rsp.putHeader(this.id, 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); } } protected void prepareResponse(Message rsp) { ; } // ....................................................................... /** * The header for RequestCorrelator messages */ public static class Header extends org.jgroups.Header { public static final byte REQ = 0; public static final byte RSP = 1; /** Type of header: request or reply */ public byte type; /** * The id of this request to distinguish among other requests from the same RequestCorrelator */ public long id; /** msg is synchronous if true */ public boolean rsp_expected; /** The unique ID of the associated RequestCorrelator */ public short corrId; /** * 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 corr_id The ID of the RequestCorrelator from which */ public Header(byte type, long id, boolean rsp_expected, short corr_id) { this.type = type; this.id = id; this.rsp_expected = rsp_expected; this.corrId = corr_id; } /** */ public String toString() { StringBuilder ret=new StringBuilder(); ret.append("id=" + corrId + ", type="); ret.append(type == REQ ? "REQ" : type == RSP ? "RSP" : ""); ret.append(", id=" + id); ret.append(", rsp_expected=" + rsp_expected); return ret.toString(); } public void writeTo(DataOutputStream out) throws IOException { out.writeByte(type); out.writeLong(id); out.writeBoolean(rsp_expected); out.writeShort(corrId); } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { type=in.readByte(); id=in.readLong(); rsp_expected=in.readBoolean(); corrId=in.readShort(); } public int size() { return Global.BYTE_SIZE // type + Global.LONG_SIZE // id + Global.BYTE_SIZE // rsp_expected + Global.SHORT_SIZE; // corrId } } public static final class MultiDestinationHeader extends Header { /** Contains a list of members who should not receive the request (others will drop). Ignored if null */ public java.util.Collection exclusion_list; public MultiDestinationHeader() { } public MultiDestinationHeader(byte type, long id, boolean rsp_expected, short corr_id, Collection

exclusion_list) { super(type, id, rsp_expected, corr_id); this.exclusion_list=exclusion_list; } public void writeTo(DataOutputStream out) throws IOException { super.writeTo(out); Util.writeAddresses(exclusion_list, out); } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { super.readFrom(in); exclusion_list=Util.readAddresses(in, LinkedList.class); } public int size() { return (int)(super.size() + Util.size(exclusion_list)); } public String toString() { String str=super.toString(); if(exclusion_list != null) str=str+ ", exclusion_list=" + exclusion_list; return str; } } 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"}; } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/blocks/RequestHandler.java0000644000175000017500000000020111647260573026665 0ustar moellermoeller package org.jgroups.blocks; import org.jgroups.Message; public interface RequestHandler { Object handle(Message msg); } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/blocks/RequestOptions.java0000644000175000017500000001306311647260573026755 0ustar moellermoellerpackage org.jgroups.blocks; import org.jgroups.Message; import org.jgroups.Address; import org.jgroups.util.Util; import java.util.*; /** Class which captures a bunch of options relevant to remote method invocation or message sending * @author Bela Ban * @since 2.10 */ public class RequestOptions { /** The mode of a request. Defined in GroupRequest e.g. GET_NONE, GET_ALL */ private int mode=Request.GET_NONE; /** The max time (in ms) for a blocking call. 0 blocks until all responses have been received (if mode = GET_ALL) */ private long timeout; // used when mode != GET_NONE /** Turns on anycasting; this results in multiple unicasts rather than a multicast for group calls */ private boolean use_anycasting; /** Allows for filtering of responses */ private RspFilter rsp_filter; /** The scope of a message, allows for concurrent delivery of messages from the same sender */ private short scope; /** The flags set in the message in which a request is sent */ private byte flags; // Message.OOB, Message.DONT_BUNDLE etc /** A list of members which should be excluded from a call */ private Set
exclusion_list; /** When options are sealed, subsequent modifications will throw an exception */ protected boolean sealed=false; @Deprecated public static final RequestOptions SYNC; @Deprecated public static final RequestOptions ASYNC; static { SYNC=new RequestOptions(Request.GET_ALL, 5000).seal(); ASYNC=new RequestOptions(Request.GET_NONE, 5000).seal(); } public RequestOptions() { } public RequestOptions(int mode, long timeout, boolean use_anycasting, RspFilter rsp_filter, byte flags) { this.mode=mode; this.timeout=timeout; this.use_anycasting=use_anycasting; this.rsp_filter=rsp_filter; this.flags=flags; } public RequestOptions(int mode, long timeout, boolean use_anycasting, RspFilter rsp_filter) { this(mode, timeout, use_anycasting, rsp_filter, (byte)0); } public RequestOptions(int mode, long timeout) { this(mode, timeout, false, null); } public RequestOptions(RequestOptions opts) { this.mode=opts.mode; this.timeout=opts.timeout; this.use_anycasting=opts.use_anycasting; this.rsp_filter=opts.rsp_filter; this.scope=opts.scope; this.flags=opts.flags; this.exclusion_list=opts.exclusion_list; this.sealed=opts.sealed; } public static RequestOptions SYNC() {return new RequestOptions(Request.GET_ALL, 5000);} public static RequestOptions ASYNC() {return new RequestOptions(Request.GET_NONE, 5000);} public int getMode() { return mode; } public RequestOptions setMode(int mode) { checkSealed(); this.mode=mode; return this; } public long getTimeout() { return timeout; } public RequestOptions setTimeout(long timeout) { checkSealed(); this.timeout=timeout; return this; } public boolean getAnycasting() { return use_anycasting; } public RequestOptions setAnycasting(boolean use_anycasting) { checkSealed(); this.use_anycasting=use_anycasting; return this; } public short getScope() { return scope; } public RequestOptions setScope(short scope) { checkSealed(); this.scope=scope; return this; } public RspFilter getRspFilter() { return rsp_filter; } public RequestOptions setRspFilter(RspFilter rsp_filter) { checkSealed(); this.rsp_filter=rsp_filter; return this; } public byte getFlags() { return flags; } public RequestOptions setFlags(byte flags) { checkSealed(); this.flags=Util.setFlag(this.flags, flags); return this; } public RequestOptions clearFlags(byte flags) { checkSealed(); this.flags=Util.clearFlags(this.flags, flags); return this; } public boolean hasExclusionList() { return exclusion_list != null && !exclusion_list.isEmpty(); } public Collection
getExclusionList() { if(exclusion_list == null) return exclusion_list; else return Collections.unmodifiableCollection(exclusion_list); } public RequestOptions setExclusionList(Address ... mbrs) { checkSealed(); if(exclusion_list == null) exclusion_list=new HashSet
(); else exclusion_list.clear(); exclusion_list.addAll(Arrays.asList(mbrs)); return this; } /** Seals options against subsequent modifications * @deprecated Will get removed together with SYNC and ASYNC in 3.0*/ @Deprecated public RequestOptions seal() { sealed=true; return this; } public String toString() { StringBuilder sb=new StringBuilder(); sb.append("mode=" + Request.modeToString(mode)); sb.append(", timeout=" + timeout); if(use_anycasting) sb.append(", anycasting=true"); sb.append(", flags=" + Message.flagsToString(flags)); if(scope > 0) sb.append(", scope=" + scope); if(exclusion_list != null) sb.append(", exclusion list: " + Util.print(exclusion_list)); return sb.toString(); } protected void checkSealed() { if(sealed) throw new IllegalStateException("options are sealed, cannot modify them; use a new instance of RequestOptions"); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/blocks/RpcDispatcher.java0000644000175000017500000005326111647260573026510 0ustar moellermoeller package org.jgroups.blocks; import org.jgroups.*; import org.jgroups.util.*; import java.io.Serializable; import java.lang.reflect.Method; import java.util.*; /** * 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; } @Deprecated public RpcDispatcher(Channel channel, MessageListener l, MembershipListener l2, Object server_obj, boolean deadlock_detection) { super(channel, l, l2); channel.addChannelListener(this); this.server_obj=server_obj; } @Deprecated public RpcDispatcher(Channel channel, MessageListener l, MembershipListener l2, Object server_obj, boolean deadlock_detection, boolean concurrent_processing) { super(channel, l, l2, false, concurrent_processing); channel.addChannelListener(this); this.server_obj=server_obj; } @Deprecated 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 static 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; } @Deprecated 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); } @Deprecated 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); } @Deprecated 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, new RequestOptions(mode, timeout, use_anycasting, filter, (byte)0)); } public RspList callRemoteMethods(Collection
dests, String method_name, Object[] args, Class[] types, RequestOptions options) { MethodCall method_call=new MethodCall(method_name, args, types); return callRemoteMethods(dests, method_call, options); } @Deprecated 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); } @Deprecated 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, new RequestOptions(mode, timeout, use_anycasting, null, (byte)0)); } @Deprecated public RspList callRemoteMethods(Vector
dests, MethodCall method_call, int mode, long timeout) { return callRemoteMethods(dests, method_call, new RequestOptions().setMode(mode).setTimeout(timeout)); } /** * Invokes a method in all members contained in dests (or all members if dests is null). * @param dests A list of addresses. If null, the method will be invoked on all cluster members * @param method_call The method (plus args) to be invoked * @param options A collection of call options, e.g. sync versus async, timeout etc * @return RspList A list of return values and flags (suspected, not received) per member * @since 2.9 */ public RspList callRemoteMethods(Collection
dests, MethodCall method_call, RequestOptions options) { 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 RspList.EMPTY_RSP_LIST; } if(log.isTraceEnabled()) log.trace(new StringBuilder("dests=").append(dests).append(", method_call=").append(method_call). append(", options=").append(options)); 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 3.0 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); msg.setFlag(options.getFlags()); if(options.getScope() > 0) msg.setScope(options.getScope()); RspList retval=super.castMessage(dests, msg, options); if(log.isTraceEnabled()) log.trace("responses: " + retval); return retval; } @Deprecated public NotifyingFuture callRemoteMethodsWithFuture(Vector
dests, MethodCall method_call, int mode, long timeout, boolean use_anycasting, boolean oob, RspFilter filter) { RequestOptions options=new RequestOptions(mode, timeout, use_anycasting, filter); if(oob) options.setFlags(Message.OOB); return callRemoteMethodsWithFuture(dests, method_call, options); } @Deprecated public NotifyingFuture callRemoteMethodsWithFuture(Vector
dests, MethodCall method_call) { return callRemoteMethodsWithFuture(dests, method_call, new RequestOptions()); } public NotifyingFuture callRemoteMethodsWithFuture(Collection
dests, MethodCall method_call, RequestOptions options) { 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 NullFuture(RspList.EMPTY_RSP_LIST); } if(log.isTraceEnabled()) log.trace(new StringBuilder("dests=").append(dests).append(", method_call=").append(method_call). append(", options=").append(options)); 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); msg.setFlag(options.getFlags()); if(options.getScope() > 0) msg.setScope(options.getScope()); NotifyingFuture retval=super.castMessageWithFuture(dests, msg, options); if(log.isTraceEnabled()) log.trace("responses: " + retval); return retval; } @Deprecated 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, Class[] types, RequestOptions options) throws Throwable { MethodCall method_call=new MethodCall(method_name, args, types); return callRemoteMethod(dest, method_call, options); } @Deprecated 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); } @Deprecated public Object callRemoteMethod(Address dest, MethodCall method_call, int mode, long timeout) throws Throwable { return callRemoteMethod(dest, method_call, mode, timeout, false); } @Deprecated public Object callRemoteMethod(Address dest, MethodCall method_call, int mode, long timeout, boolean oob) throws Throwable { RequestOptions options=new RequestOptions(mode, timeout, false, null); if(oob) options.setFlags(Message.OOB); return callRemoteMethod(dest, method_call, options); } @Deprecated public Object callRemoteMethod(Address dest, MethodCall call) throws Throwable { return callRemoteMethod(dest, call, new RequestOptions()); } public Object callRemoteMethod(Address dest, MethodCall call, RequestOptions options) throws Throwable { if(log.isTraceEnabled()) log.trace("dest=" + dest + ", method_call=" + call + ", options=" + options); Object buf=req_marshaller != null? req_marshaller.objectToBuffer(call) : Util.objectToByteBuffer(call); Message msg=new Message(dest, null, null); if(buf instanceof Buffer) msg.setBuffer((Buffer)buf); else msg.setBuffer((byte[])buf); msg.setFlag(options.getFlags()); if(options.getScope() > 0) msg.setScope(options.getScope()); Object retval=super.sendMessage(msg, options); if(log.isTraceEnabled()) log.trace("retval: " + retval); if(retval instanceof Throwable) throw (Throwable)retval; return retval; } @Deprecated public NotifyingFuture callRemoteMethodWithFuture(Address dest, MethodCall method_call, int mode, long timeout, boolean oob) throws Throwable { RequestOptions options=new RequestOptions(mode, timeout, false, null); if(oob) options.setFlags(Message.OOB); return callRemoteMethodWithFuture(dest, method_call, options); } @Deprecated public NotifyingFuture callRemoteMethodWithFuture(Address dest, MethodCall call) throws Throwable { return callRemoteMethodWithFuture(dest, call, new RequestOptions()); } public NotifyingFuture callRemoteMethodWithFuture(Address dest, MethodCall call, RequestOptions options) throws Throwable { if(log.isTraceEnabled()) log.trace("dest=" + dest + ", method_call=" + call + ", options=" + options); Object buf=req_marshaller != null? req_marshaller.objectToBuffer(call) : Util.objectToByteBuffer(call); Message msg=new Message(dest, null, null); if(buf instanceof Buffer) msg.setBuffer((Buffer)buf); else msg.setBuffer((byte[])buf); msg.setFlag(options.getFlags()); if(options.getScope() > 0) msg.setScope(options.getScope()); return super.sendMessageWithFuture(msg, options); } 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; 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 found 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() { } public void channelReconnected(Address new_addr) { } /* ----------------------------------------------------------------------- */ } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/blocks/RspCollector.java0000644000175000017500000000042711647260573026364 0ustar moellermoeller 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); } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/blocks/RspFilter.java0000644000175000017500000000235611647260573025666 0ustar moellermoellerpackage 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 */ 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(); } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/blocks/TCPConnectionMap.java0000644000175000017500000007161511647260573027064 0ustar moellermoellerpackage org.jgroups.blocks; import org.jgroups.Address; import org.jgroups.Global; import org.jgroups.Version; import org.jgroups.logging.Log; import org.jgroups.logging.LogFactory; import org.jgroups.stack.IpAddress; import org.jgroups.util.DefaultSocketFactory; import org.jgroups.util.SocketFactory; import org.jgroups.util.ThreadFactory; import org.jgroups.util.Util; import java.io.*; import java.net.*; import java.util.Collection; import java.util.Map; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class TCPConnectionMap{ private final Mapper mapper; private final InetAddress bind_addr; private final Address local_addr; // bind_addr + port of srv_sock private final ThreadGroup thread_group=new ThreadGroup(Util.getGlobalThreadGroup(),"ConnectionMap"); private final ServerSocket srv_sock; private Receiver receiver; private final long conn_expire_time; private final Log log=LogFactory.getLog(getClass()); private int recv_buf_size=120000; private int send_buf_size=60000; private int send_queue_size = 0; private int sock_conn_timeout=1000; // max time in millis to wait for Socket.connect() to return private boolean tcp_nodelay=false; private int linger=-1; private final Thread acceptor; private final AtomicBoolean running = new AtomicBoolean(false); private volatile boolean use_send_queues=false; protected SocketFactory socket_factory=new DefaultSocketFactory(); public TCPConnectionMap(String service_name, ThreadFactory f, SocketFactory socket_factory, Receiver r, InetAddress bind_addr, InetAddress external_addr, int srv_port, int max_port ) throws Exception { this(service_name, f,socket_factory, r,bind_addr,external_addr,srv_port,max_port,0,0); } public TCPConnectionMap(String service_name, ThreadFactory f, Receiver r, InetAddress bind_addr, InetAddress external_addr, int srv_port, int max_port, long reaper_interval, long conn_expire_time ) throws Exception { this(service_name, f, null, r, bind_addr, external_addr, srv_port, max_port, reaper_interval, conn_expire_time); } public TCPConnectionMap(String service_name, ThreadFactory f, SocketFactory socket_factory, Receiver r, InetAddress bind_addr, InetAddress external_addr, int srv_port, int max_port, long reaper_interval, long conn_expire_time ) throws Exception { this.mapper = new Mapper(f,reaper_interval); this.receiver=r; this.bind_addr=bind_addr; this.conn_expire_time = conn_expire_time; if(socket_factory != null) this.socket_factory=socket_factory; this.srv_sock=Util.createServerSocket(this.socket_factory, service_name, bind_addr, 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()); acceptor=f.newThread(thread_group, new ConnectionAcceptor(),"ConnectionMap.Acceptor"); } public Address getLocalAddress() { return local_addr; } public Receiver getReceiver() { return receiver; } public void setReceiver(Receiver receiver) { this.receiver=receiver; } public SocketFactory getSocketFactory() { return socket_factory; } public void setSocketFactory(SocketFactory socket_factory) { this.socket_factory=socket_factory; } public void addConnectionMapListener(AbstractConnectionMap.ConnectionMapListener l) { mapper.addConnectionMapListener(l); } public void removeConnectionMapListener(AbstractConnectionMap.ConnectionMapListener l) { mapper.removeConnectionMapListener(l); } /** * 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) { receiver.receive(sender, data, offset, length); } public void send(Address dest, byte[] data, int offset, int length) throws Exception { 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.get() ) { if(log.isDebugEnabled()) log.debug("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) TCPConnection conn; conn=mapper.getConnection(dest); // 2. Send the message using that connection if(conn != null) { try { conn.send(data, offset, length); } catch(Exception ex) { mapper.removeConnection(dest); throw ex; } } } public void start() throws Exception { if(running.compareAndSet(false, true)) { acceptor.start(); mapper.start(); } } public void stop() { if(running.compareAndSet(true, false)) { try { getSocketFactory().close(srv_sock); } catch(IOException e) { } Util.interruptAndWaitToDie(acceptor); mapper.stop(); } } private void setSocketParameters(Socket client_sock) throws SocketException { 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); } /** Used for message reception. */ public interface Receiver { void receive(Address sender, byte[] data, int offset, int length); } private class ConnectionAcceptor implements Runnable { /** * 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() { while(!srv_sock.isClosed() && !Thread.currentThread().isInterrupted()) { TCPConnection conn=null; Socket client_sock = null; try { client_sock=srv_sock.accept(); conn=new TCPConnection(client_sock); Address peer_addr=conn.getPeerAddress(); mapper.getLock().lock(); try { boolean currentConnectionOpen=mapper.hasOpenConnection(peer_addr); boolean replaceWithNewConnection=false; if(currentConnectionOpen) { replaceWithNewConnection=peer_addr.compareTo(local_addr) > 0; } if(!currentConnectionOpen || replaceWithNewConnection) { mapper.removeConnection(peer_addr); mapper.addConnection(peer_addr, conn); conn.start(mapper.getThreadFactory()); // starts handler thread on this socket } else { Util.close(conn); } } finally { mapper.getLock().unlock(); } } catch(SocketException se){ boolean threadExiting=srv_sock.isClosed() || Thread.currentThread().isInterrupted(); if(threadExiting) { break; } else { if(log.isWarnEnabled()) log.warn("Could not accept connection from peer ", se); Util.close(conn); Util.close(client_sock); } } catch(Exception ex) { if(log.isWarnEnabled()) log.warn("Could not read accept connection from peer " + ex); Util.close(conn); Util.close(client_sock); } } if(log.isTraceEnabled()) log.trace(Thread.currentThread().getName() + " terminated"); } } public void setReceiveBufferSize(int recv_buf_size) { this.recv_buf_size = recv_buf_size; } public void setSocketConnectionTimeout(int sock_conn_timeout) { this.sock_conn_timeout = sock_conn_timeout; } public void setSendBufferSize(int send_buf_size) { this.send_buf_size = send_buf_size; } public void setLinger(int linger) { this.linger = linger; } public void setTcpNodelay(boolean tcp_nodelay) { this.tcp_nodelay = tcp_nodelay; } public void setSendQueueSize(int send_queue_size) { this.send_queue_size = send_queue_size; } public void setUseSendQueues(boolean use_send_queues) { this.use_send_queues=use_send_queues; } public int getNumConnections() { return mapper.getNumConnections(); } public boolean connectionEstablishedTo(Address addr) { return mapper.connectionEstablishedTo(addr); } public String printConnections() { return mapper.printConnections(); } public void retainAll(Collection
members) { mapper.retainAll(members); } public long getConnectionExpiryTimeout() { return conn_expire_time; } public int getSenderQueueSize() { return send_queue_size; } public String toString() { StringBuilder ret=new StringBuilder(); ret.append("local_addr=" + local_addr).append("\n"); ret.append("connections (" + mapper.size() + "):\n"); ret.append(mapper.toString()); ret.append('\n'); return ret.toString(); } public class TCPConnection implements Connection { private final Socket sock; // socket to/from peer (result of srv_sock.accept() or new Socket()) private final Lock send_lock=new ReentrantLock(); // serialize send() private final Log log=LogFactory.getLog(getClass()); private final byte[] cookie= { 'b', 'e', 'l', 'a' }; private final DataOutputStream out; private final DataInputStream in; private final Address peer_addr; // address of the 'other end' of the connection private final int peer_addr_read_timeout=2000; // max time in milliseconds to block on reading peer address private long last_access=System.currentTimeMillis(); // last time a message was sent or received private Sender sender; private ConnectionPeerReceiver connectionPeerReceiver; private AtomicBoolean active = new AtomicBoolean(false); TCPConnection(Address peer_addr) throws Exception { if(peer_addr == null) throw new IllegalArgumentException("Invalid parameter peer_addr="+ peer_addr); SocketAddress destAddr=new InetSocketAddress(((IpAddress)peer_addr).getIpAddress(),((IpAddress)peer_addr).getPort()); this.sock=socket_factory.createSocket(Global.TCP_SOCK); this.sock.bind(new InetSocketAddress(bind_addr, 0)); Util.connect(this.sock, destAddr, sock_conn_timeout); setSocketParameters(sock); this.out=new DataOutputStream(new BufferedOutputStream(sock.getOutputStream())); this.in=new DataInputStream(new BufferedInputStream(sock.getInputStream())); sendLocalAddress(getLocalAddress()); this.peer_addr=peer_addr; } TCPConnection(Socket s) throws Exception { if(s == null) throw new IllegalArgumentException("Invalid parameter s=" + s); setSocketParameters(s); this.out=new DataOutputStream(new BufferedOutputStream(s.getOutputStream())); this.in=new DataInputStream(new BufferedInputStream(s.getInputStream())); this.peer_addr=readPeerAddress(s); this.sock=s; } private Address getPeerAddress() { return peer_addr; } private void updateLastAccessed() { last_access=System.currentTimeMillis(); } private void start(ThreadFactory f) { //only start once.... if(active.compareAndSet(false, true)) { connectionPeerReceiver = new ConnectionPeerReceiver(f); connectionPeerReceiver.start(); if(isSenderUsed()) { sender = new Sender(f,getSenderQueueSize()); sender.start(); } } } private boolean isSenderUsed(){ return getSenderQueueSize() > 0 && use_send_queues; } private String getSockAddress() { StringBuilder sb=new StringBuilder(); if(sock != null) { sb.append(sock.getLocalAddress().getHostAddress()) .append(':') .append(sock.getLocalPort()); sb.append(" - ") .append(sock.getInetAddress().getHostAddress()) .append(':') .append(sock.getPort()); } return sb.toString(); } /** * * @param data * Guaranteed to be non null * @param offset * @param length */ private void send(byte[] data, int offset, int length) throws Exception { if (isSenderUsed()) { // 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); sender.addToQueue(tmp); } 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 * @throws Exception */ private void _send(byte[] data, int offset, int length, boolean acquire_lock) throws Exception { if(acquire_lock) send_lock.lock(); try { doSend(data, offset, length); updateLastAccessed(); } catch(InterruptedException iex) { Thread.currentThread().interrupt(); // set interrupt flag again } finally { if(acquire_lock) send_lock.unlock(); } } private void doSend(byte[] data, int offset, int length) throws Exception { // 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). 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) } /** * 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 */ private Address readPeerAddress(Socket client_sock) throws Exception { int timeout=client_sock.getSoTimeout(); client_sock.setSoTimeout(peer_addr_read_timeout); try { // read the cookie first byte[] input_cookie=new byte[cookie.length]; in.readFully(input_cookie, 0, input_cookie.length); if(!matchCookie(input_cookie)) throw new SocketException("ConnectionMap.Connection.readPeerAddress(): cookie read by " + getLocalAddress() + " does not match own cookie; terminating connection"); // then read the version short version=in.readShort(); if(!Version.isBinaryCompatible(version) ) { if(log.isWarnEnabled()) log.warn(new StringBuilder("packet from ").append(client_sock.getInetAddress()) .append(':').append(client_sock.getPort()).append(" has different version (") .append(Version.print(version)).append(") from ours (") .append(Version.printVersion()) .append("). This may cause problems").toString()); } Address 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. * * @throws IOException */ private void sendLocalAddress(Address local_addr) throws IOException { // write the cookie out.write(cookie, 0, cookie.length); // write the version out.writeShort(Version.version); local_addr.writeTo(out); out.flush(); // needed ? updateLastAccessed(); } private 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; } private class ConnectionPeerReceiver implements Runnable { private Thread recv; private final AtomicBoolean receiving = new AtomicBoolean(false); public ConnectionPeerReceiver(ThreadFactory f) { recv = f.newThread(this,"Connection.Receiver [" + getSockAddress() + "]"); } public void start() { if(receiving.compareAndSet(false, true)) { recv.start(); } } public void stop() { if(receiving.compareAndSet(true, false)) { recv.interrupt(); } } public boolean isRunning() { return receiving.get(); } public boolean canRun() { return isRunning() && isConnected(); } public void run() { try { while(!Thread.currentThread().isInterrupted() && canRun()) { try { int len=in.readInt(); byte[] buf=new byte[len]; in.readFully(buf, 0, len); updateLastAccessed(); receiver.receive(peer_addr, buf, 0, len); } catch(OutOfMemoryError mem_ex) { break; // continue; } catch(IOException io_ex) { break; } catch(Throwable e) { } } } finally{ Util.close(TCPConnection.this); } } } private class Sender implements Runnable { final BlockingQueue send_queue; final Thread runner; private final AtomicBoolean running= new AtomicBoolean(false); public Sender(ThreadFactory tf,int send_queue_size) { this.runner=tf.newThread(this, "Connection.Sender [" + getSockAddress() + "]"); this.send_queue=new LinkedBlockingQueue(send_queue_size); } public void addToQueue(byte[] data) throws Exception { if(canRun()) send_queue.put(data); } public void start() { if(running.compareAndSet(false, true)) { runner.start(); } } public void stop() { if(running.compareAndSet(true, false)) { runner.interrupt(); } } public boolean isRunning() { return running.get(); } public boolean canRun() { return isRunning() && isConnected(); } public void run() { try { while(!Thread.currentThread().isInterrupted() && canRun()) { byte[] data=null; try { data=send_queue.take(); } catch(InterruptedException e) { // Thread.currentThread().interrupt(); break; } if(data != null) { try { _send(data, 0, data.length, false); } catch(Throwable ignored) { } } } } finally { Util.close(TCPConnection.this); } if(log.isTraceEnabled()) log.trace("TCPConnection.Sender thread terminated at " + local_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(); } public boolean isExpired(long now) { return getConnectionExpiryTimeout() > 0 && now - last_access >= getConnectionExpiryTimeout(); } public boolean isConnected() { return !sock.isClosed() && sock.isConnected(); } public boolean isOpen() { return isConnected() && (!isSenderUsed() || sender.isRunning()) && (connectionPeerReceiver != null && connectionPeerReceiver.isRunning()); } public void close() throws IOException { //can close even if start was never called... send_lock.lock(); try { connectionPeerReceiver.stop(); if (isSenderUsed()) { sender.stop(); } try { socket_factory.close(sock); } catch(Throwable t) {} Util.close(out); Util.close(in); } finally { send_lock.unlock(); } mapper.notifyConnectionClosed(peer_addr); } } private class Mapper extends AbstractConnectionMap{ public Mapper(ThreadFactory factory) { super(factory); } public Mapper(ThreadFactory factory,long reaper_interval) { super(factory,reaper_interval); } public TCPConnection getConnection(Address dest) throws Exception { TCPConnection conn = null; getLock().lock(); try { conn=conns.get(dest); if(conn != null && conn.isOpen()) return conn; try { conn = new TCPConnection(dest); conn.start(getThreadFactory()); addConnection(dest, conn); if (log.isTraceEnabled()) log.trace("created socket to " + dest); } catch(Exception ex) { if(log.isTraceEnabled()) log.trace("failed creating connection to " + dest); } } finally { getLock().unlock(); } return conn; } public boolean connectionEstablishedTo(Address address) { lock.lock(); try { TCPConnection conn=conns.get(address); return conn != null && conn.isConnected(); } finally { lock.unlock(); } } public int size() {return conns.size();} public String toString() { StringBuilder sb=new StringBuilder(); getLock().lock(); try { for(Map.Entry entry: conns.entrySet()) { sb.append(entry.getKey()).append(": ").append(entry.getValue()).append("\n"); } return sb.toString(); } finally { getLock().unlock(); } } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/blocks/TwoPhaseVotingAdapter.java0000644000175000017500000001274211647260573030176 0ustar moellermoellerpackage 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) */ 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(); } } }libjgroups-java-2.12.2.Final.orig/src/org/jgroups/blocks/TwoPhaseVotingListener.java0000644000175000017500000000177211647260573030404 0ustar moellermoellerpackage 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; }libjgroups-java-2.12.2.Final.orig/src/org/jgroups/blocks/UnicastRequest.java0000644000175000017500000001373611647260573026737 0ustar moellermoellerpackage org.jgroups.blocks; 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.Rsp; import java.util.Collection; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; /** * Sends a request to a single target destination * * @author Bela Ban */ public class UnicastRequest extends Request { protected final Rsp result; protected final Address target; /** @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 UnicastRequest(Message m, RequestCorrelator corr, Address target, RequestOptions options) { super(m, corr, null, options); this.target=target; result=new Rsp(target); } /** * @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 UnicastRequest(Message m, Transport transport, Address target, RequestOptions options) { super(m, null, transport, options); this.target=target; result=new Rsp(target); } protected void sendRequest() throws Exception { try { if(log.isTraceEnabled()) log.trace(new StringBuilder("sending request (id=").append(req_id).append(')')); if(corr != null) { corr.sendUnicastRequest(req_id, target, request_msg, options.getMode() == GET_NONE? null : this); } else { transport.send(request_msg); } } catch(Exception ex) { if(corr != null) corr.done(req_id); throw ex; } } /* ---------------------- 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) { RspFilter rsp_filter=options.getRspFilter(); lock.lock(); try { if(done) return; if(!result.wasReceived()) { boolean responseReceived=(rsp_filter == null) || rsp_filter.isAcceptable(response_value, sender); result.setValue((T)response_value); result.setReceived(responseReceived); if(log.isTraceEnabled()) log.trace(new StringBuilder("received response for request ").append(req_id) .append(", sender=").append(sender).append(", val=").append(response_value)); } done=rsp_filter == null? responsesComplete() : !rsp_filter.needMoreResponses(); if(done && corr != null) corr.done(req_id); } finally { completed.signalAll(); // wakes up execute() lock.unlock(); } checkCompletion(this); } /** * 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 || !suspected_member.equals(target)) return; lock.lock(); try { if(done) return; if(result != null && !result.wasReceived()) result.setSuspected(true); done=true; if(corr != null) corr.done(req_id); completed.signalAll(); } finally { lock.unlock(); } checkCompletion(this); } /** * If the target address is not a member of the new view, we'll mark the response as not received and unblock * the caller of execute() */ public void viewChange(View new_view) { Collection
mbrs=new_view != null? new_view.getMembers() : null; if(mbrs == null) return; lock.lock(); try { if(!mbrs.contains(target)) { result.setReceived(false); done=true; if(corr != null) corr.done(req_id); completed.signalAll(); } } finally { lock.unlock(); } checkCompletion(this); } /* -------------------- End of Interface RspCollector ----------------------------------- */ public Rsp getResult() { return result; } public T get() throws InterruptedException, ExecutionException { lock.lock(); try { waitForResults(0); return result.getValue(); } finally { lock.unlock(); } } public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { boolean ok; lock.lock(); try { ok=waitForResults(unit.toMillis(timeout)); } finally { lock.unlock(); } if(!ok) throw new TimeoutException(); return result.getValue(); } public String toString() { StringBuilder ret=new StringBuilder(128); ret.append(super.toString()); ret.append(", target=" + target); return ret.toString(); } @GuardedBy("lock") protected boolean responsesComplete() { return done || options.getMode() == GET_NONE || result.wasReceived() || result.wasSuspected(); } }libjgroups-java-2.12.2.Final.orig/src/org/jgroups/blocks/UpdateException.java0000644000175000017500000000033011647260573027043 0ustar moellermoeller package org.jgroups.blocks; public class UpdateException extends Exception { private static final long serialVersionUID = -4196360091623991749L; public UpdateException(String msg) { super(msg); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/blocks/VoteException.java0000644000175000017500000000061311647260573026542 0ustar moellermoellerpackage 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); } }libjgroups-java-2.12.2.Final.orig/src/org/jgroups/blocks/VoteResponseProcessor.java0000644000175000017500000000162111647260573030302 0ustar moellermoellerpackage 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) */ 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; } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/blocks/VotingAdapter.java0000644000175000017500000004000511647260573026514 0ustar moellermoellerpackage org.jgroups.blocks; import org.jgroups.logging.Log; import org.jgroups.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) */ 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; } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/blocks/VotingListener.java0000644000175000017500000000145011647260573026722 0ustar moellermoellerpackage 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; }libjgroups-java-2.12.2.Final.orig/src/org/jgroups/blocks/executor/0000755000175000017500000000000011647260573024741 5ustar moellermoellerlibjgroups-java-2.12.2.Final.orig/src/org/jgroups/blocks/executor/ExecutionCompletionService.java0000644000175000017500000001403511647260573033125 0ustar moellermoellerpackage org.jgroups.blocks.executor; import java.util.concurrent.BlockingQueue; import java.util.concurrent.Callable; import java.util.concurrent.CompletionService; import java.util.concurrent.ExecutorCompletionService; import java.util.concurrent.Future; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; import org.jgroups.util.FutureListener; import org.jgroups.util.NotifyingFuture; /** * A {@link CompletionService} that uses a supplied {@link ExecutionService} * to execute tasks. This class arranges that submitted tasks are, * upon completion, placed on a queue accessible using take. * The class is lightweight enough to be suitable for transient use * when processing groups of tasks. *

* This class must be used instead of a {@link ExecutorCompletionService} * provided from java.util.concurrent package. The * {@link ExecutorCompletionService} may not be used since it requires the use * of a non serializable RunnableFuture object. Since a ExecutionService * may only be used with serializable request objects, this class must be used * instead. */ public class ExecutionCompletionService implements CompletionService { protected final ExecutionService executor; protected final BlockingQueue> completionQueue; protected final QueueingListener listener; protected class QueueingListener implements FutureListener { @Override public void futureDone(Future future) { // This is a safe cast since this listener should only used // in this class completionQueue.add((NotifyingFuture)future); } } /** * Creates an ExecutorCompletionService using the supplied * executor for base task execution and a * {@link LinkedBlockingQueue} as a completion queue. * * @param executor the executor to use * @throws NullPointerException if executor is null */ public ExecutionCompletionService(ExecutionService executor) { this(executor, null, null); } /** * Creates an ExecutorCompletionService using the supplied * executor for base task execution and the supplied queue as its * completion queue. * * @param executor the executor to use * @param completionQueue the queue to use as the completion queue * normally one dedicated for use by this service * @throws NullPointerException if executor is null */ public ExecutionCompletionService(ExecutionService executor, BlockingQueue> completionQueue) { this(executor, completionQueue, null); } /** * This constructor is here if someone wants to override this class and * provide their own QueueingListener to possibly listen in on futures * being finished * @param executor the executor to use * @param completionQueue the queue to use as the completion queue * normally one dedicated for use by this service * @param listener the listener to notify. To work properly this listner * should at minimum call the super.futureDone or else this * completion service may not work correctly. * @throws NullPointerException if executor is null */ protected ExecutionCompletionService(ExecutionService executor, BlockingQueue> completionQueue, QueueingListener listener) { if (executor == null) throw new NullPointerException(); this.executor = executor; if (completionQueue == null) { this.completionQueue = new LinkedBlockingQueue>(); } else { this.completionQueue = completionQueue; } if (listener == null) { this.listener = new QueueingListener(); } else { this.listener = listener; } } /** * {@inheritDoc CompletionService} *

* This future object may not be used as a NotifyingFuture. That is because * internally this class sets the listener to provide ability to add to the queue. */ public Future submit(Callable task) { if (task == null) throw new NullPointerException(); NotifyingFuture f = executor.submit(task); f.setListener(listener); return f; } /** * {@inheritDoc CompletionService} *

* This future object may not be used as a NotifyingFuture. That is because * internally this class sets the listener to provide ability to add to the queue. */ public Future submit(Runnable task, V result) { if (task == null) throw new NullPointerException(); NotifyingFuture f = executor.submit(task, result); f.setListener(listener); return f; } /** * {@inheritDoc CompletionService} *

* This future may safely be used as a NotifyingFuture if desired. This * is because if it tries to set a listener it will be called immediately * since the task has already been completed. */ public NotifyingFuture take() throws InterruptedException { return completionQueue.take(); } /** * {@inheritDoc CompletionService} *

* This future may safely be used as a NotifyingFuture if desired. This * is because if it tries to set a listener it will be called immediately * since the task has already been completed. */ public NotifyingFuture poll() { return completionQueue.poll(); } /** * {@inheritDoc CompletionService} *

* This future may safely be used as a NotifyingFuture if desired. This * is because if it tries to set a listener it will be called immediately * since the task has already been completed. */ public NotifyingFuture poll(long timeout, TimeUnit unit) throws InterruptedException { return completionQueue.poll(timeout, unit); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/blocks/executor/ExecutionRunner.java0000644000175000017500000000707311647260573030750 0ustar moellermoellerpackage org.jgroups.blocks.executor; import java.util.concurrent.atomic.AtomicBoolean; import org.jgroups.JChannel; import org.jgroups.logging.Log; import org.jgroups.logging.LogFactory; import org.jgroups.protocols.Executing; /** * This class is to be used to pick up execution requests and actually run * them. A single instance can be used across any number of threads. * * @author wburns */ public class ExecutionRunner implements Runnable { protected JChannel ch; protected Executing _execProt; public ExecutionRunner(JChannel channel) { setChannel(channel); } public void setChannel(JChannel ch) { this.ch=ch; _execProt=(Executing)ch.getProtocolStack().findProtocol(Executing.class); if(_execProt == null) throw new IllegalStateException("Channel configuration must include a executing protocol " + "(subclass of " + Executing.class.getName() + ")"); } // @see java.lang.Runnable#run() @Override public void run() { final AtomicBoolean shutdown = new AtomicBoolean(); // This thread is only spawned so that we can differentiate between // an interrupt of a task and an interrupt causing a shutdown of // runner itself. Thread executionThread = new Thread() { // @see java.lang.Thread#run() @Override public void run() { Runnable runnable = null; // This task exits by being interrupted when the task isn't running while (!shutdown.get()) { runnable = (Runnable)ch.downcall(new ExecutorEvent( ExecutorEvent.CONSUMER_READY, null)); if (Thread.interrupted()) { if (runnable != null) { // We assume that if an interrupt occurs here that // it is trying to close down the task. Since the // window is so small. Therefore if we get a // task we need to reject it so it can be passed // off to a different consumer ch.down(new ExecutorEvent(ExecutorEvent.TASK_COMPLETE, new Object[]{runnable, new InterruptedException()})); } continue; } Throwable throwable = null; try { runnable.run(); } // This can only happen if user is directly doing an execute(Runnable) catch (Throwable t) { _logger.error("Unexpected Runtime Error encountered in Runnable request", t); throwable = t; } ch.down(new ExecutorEvent(ExecutorEvent.TASK_COMPLETE, throwable != null ? new Object[]{runnable, throwable} : runnable)); } } }; executionThread.setName(Thread.currentThread().getName() + "- Task Runner"); executionThread.start(); try { executionThread.join(); } catch (InterruptedException e) { shutdown.set(true); executionThread.interrupt(); if (_logger.isTraceEnabled()) { _logger.trace("Shutting down Execution Runner"); } } } protected static final Log _logger = LogFactory.getLog(ExecutionRunner.class); } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/blocks/executor/ExecutionService.java0000644000175000017500000007253711647260573031106 0ustar moellermoellerpackage org.jgroups.blocks.executor; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.io.NotSerializableException; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.concurrent.AbstractExecutorService; import java.util.concurrent.Callable; import java.util.concurrent.CancellationException; import java.util.concurrent.CompletionService; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorCompletionService; import java.util.concurrent.Future; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.RunnableFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.locks.AbstractQueuedSynchronizer; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import org.jgroups.JChannel; import org.jgroups.protocols.Executing; import org.jgroups.util.FutureListener; import org.jgroups.util.NotifyingFuture; import org.jgroups.util.Streamable; import org.jgroups.util.Util; /** * This is a jgroups implementation of an ExecutorService, where the consumers * are running on any number of nodes. The nodes should run * {@link ExecutionRunner} to start picking up requests. *

* Every future object returned will be a {@link NotifyingFuture} which * allows for not having to query the future and have a callback instead. This * can then be used as a workflow to submit other tasks sequentially or also to * query the future for the value at that time. *

* Every callable or runnable submitted must be either {@link Serializable} or * {@link Streamable}. Also the value returned from * a callable must {@link Serializable} or * {@link Streamable}. Unfortunately if the value returned is not serializable * then a {@link NotSerializableException} will be thrown as the cause. * @author wburns * @since 2.12.0 */ public class ExecutionService extends AbstractExecutorService { protected JChannel ch; protected Executing _execProt; protected Lock _unfinishedLock = new ReentrantLock(); protected Condition _unfinishedCondition = _unfinishedLock.newCondition(); protected Set> _unfinishedFutures = new HashSet>(); protected AtomicBoolean _shutdown = new AtomicBoolean(false); public ExecutionService() { } public ExecutionService(JChannel ch) { setChannel(ch); } public void setChannel(JChannel ch) { this.ch=ch; _execProt=(Executing)ch.getProtocolStack().findProtocol(Executing.class); if(_execProt == null) throw new IllegalStateException("Channel configuration must include a executing protocol " + "(subclass of " + Executing.class.getName() + ")"); } // @see java.util.concurrent.AbstractExecutorService#submit(java.lang.Runnable, java.lang.Object) @Override public NotifyingFuture submit(Runnable task, T result) { // This cast is okay cause we control creation of the task return (NotifyingFuture)super.submit(task, result); } // @see java.util.concurrent.AbstractExecutorService#submit(java.util.concurrent.Callable) @Override public NotifyingFuture submit(Callable task) { // This cast is okay cause we control creation of the task return (NotifyingFuture)super.submit(task); } /** * This is basically a copy of the FutureTask in java.util.concurrent but * added serializable to it. Also added in the NotifyingFuture * so that the channel can update the future when the value is calculated. * * @param * @author wburns */ public static class DistributedFuture implements RunnableFuture, ExecutorNotification, NotifyingFuture { // @see java.lang.Object#toString() @Override public String toString() { return "DistributedFuture [callable=" + sync.callable + "]"; } /** Synchronization control for FutureTask */ protected final Sync sync; /** The following values are only used on the client side */ private final JChannel channel; private final Set> _unfinishedFutures; private final Lock _unfinishedLock; private final Condition _unfinishedCondition; private volatile FutureListener _listener; /** * Creates a FutureTask that will upon running, execute the * given Callable. * * @param channel The channel that messages are sent down * @param unfinishedLock The lock which protects the futuresToFinish * set object. * @param condition The condition to signal when this future finishes * @param futuresToFinish The set to remove this future from when * it is finished. * @param callable The callable to actually run on the server side */ public DistributedFuture(JChannel channel, Lock unfinishedLock, Condition condition, Set> futuresToFinish, Callable callable) { if (callable == null) throw new NullPointerException(); sync = new Sync(this, callable); this.channel = channel; // We keep the real copy to update the outside _unfinishedFutures = futuresToFinish; _unfinishedLock = unfinishedLock; _unfinishedCondition = condition; } /** * Creates a FutureTask that will upon running, execute the * given Runnable, and arrange that get will return the * given result on successful completion. * * @param channel The channel that messages are sent down * @param unfinishedLock The lock which protects the futuresToFinish * set object. * @param condition The condition to signal when this future finishes * @param futuresToFinish The set to remove this future from when * it is finished. * @param runnable the runnable task * @param result the result to return on successful completion. If * you don't need a particular result, consider using * constructions of the form: * Future<?> f = new FutureTask<Object>(runnable, null) * @throws NullPointerException if runnable is null */ public DistributedFuture(JChannel channel, Lock unfinishedLock, Condition condition, Set> futuresToFinish, Runnable runnable, V result) { sync = new Sync(this, new RunnableAdapter(runnable, result)); this.channel = channel; // We keep the real copy to update the outside _unfinishedFutures = futuresToFinish; _unfinishedLock = unfinishedLock; _unfinishedCondition = condition; } public Callable getCallable() { return sync.callable; } public boolean isCancelled() { return sync.innerIsCancelled(); } public boolean isDone() { return sync.innerIsDone(); } public boolean cancel(boolean mayInterruptIfRunning) { if (sync.innerIsDone()) { return false; } // This will only happen on calling side since it is transient if (channel != null) { return (Boolean)channel.downcall(new ExecutorEvent( ExecutorEvent.TASK_CANCEL, new Object[] {this, mayInterruptIfRunning})); } return sync.innerCancel(mayInterruptIfRunning); } /** * @throws CancellationException {@inheritDoc} */ public V get() throws InterruptedException, ExecutionException { return sync.innerGet(); } /** * @throws CancellationException {@inheritDoc} */ public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { return sync.innerGet(unit.toNanos(timeout)); } /** * Protected method invoked when this task transitions to state * isDone (whether normally or via cancellation). The * default implementation does nothing. Subclasses may override * this method to invoke completion callbacks or perform * bookkeeping. Note that you can query status inside the * implementation of this method to determine whether this task * has been cancelled. */ protected void done() { _unfinishedLock.lock(); try { _unfinishedFutures.remove(this); _unfinishedCondition.signalAll(); } finally { _unfinishedLock.unlock(); } // We assign the listener to a local variable so we don't have to // worry about it becoming null inside the if FutureListener listener = _listener; // We don't want this to run on server if (listener != null) { listener.futureDone(this); } } @Override public NotifyingFuture setListener(FutureListener listener) { _listener = listener; if (sync.innerIsDone()) { _listener.futureDone(this); } return this; } /** * Sets the result of this Future to the given value unless * this future has already been set or has been cancelled. * This method is invoked internally by the run method * upon successful completion of the computation. * @param v the value */ protected void set(V v) { sync.innerSet(v); } /** * Causes this future to report an ExecutionException * with the given throwable as its cause, unless this Future has * already been set or has been cancelled. * This method is invoked internally by the run method * upon failure of the computation. * @param t the cause of failure */ protected void setException(Throwable t) { sync.innerSetException(t); } // The following (duplicated) doc comment can be removed once // // 6270645: Javadoc comments should be inherited from most derived // superinterface or superclass // is fixed. /** * Sets this Future to the result of its computation * unless it has been cancelled. */ public void run() { sync.innerRun(); } /** * Synchronization control for FutureTask. Note that this must be * a non-static inner class in order to invoke the protected * done method. For clarity, all inner class support * methods are same as outer, prefixed with "inner". * * Uses AQS sync state to represent run status */ protected static final class Sync extends AbstractQueuedSynchronizer { private static final long serialVersionUID = -7828117401763700385L; /** State value representing that task is running */ protected static final int RUNNING = 1; /** State value representing that task ran */ protected static final int RAN = 2; /** State value representing that task was cancelled */ protected static final int CANCELLED = 4; /** The containing future */ protected final DistributedFuture future; /** The underlying callable */ protected final Callable callable; /** The result to return from get() */ protected V result; /** The exception to throw from get() */ protected Throwable exception; /** * The thread running task. When nulled after set/cancel, this * indicates that the results are accessible. Must be * volatile, to ensure visibility upon completion. */ protected transient volatile Thread runner; public Sync(DistributedFuture future, Callable callable) { this.future = future; this.callable = callable; } private static boolean ranOrCancelled(int state) { return (state & (RAN | CANCELLED)) != 0; } /** * Implements AQS base acquire to succeed if ran or cancelled */ protected int tryAcquireShared(int ignore) { return innerIsDone()? 1 : -1; } /** * Implements AQS base release to always signal after setting * final done status by nulling runner thread. */ protected boolean tryReleaseShared(int ignore) { runner = null; return true; } boolean innerIsCancelled() { return getState() == CANCELLED; } boolean innerIsDone() { return ranOrCancelled(getState()) && runner == null; } V innerGet() throws InterruptedException, ExecutionException { acquireSharedInterruptibly(0); if (getState() == CANCELLED) throw new CancellationException(); if (exception != null) throw new ExecutionException(exception); return result; } V innerGet(long nanosTimeout) throws InterruptedException, ExecutionException, TimeoutException { if (!tryAcquireSharedNanos(0, nanosTimeout)) throw new TimeoutException(); if (getState() == CANCELLED) throw new CancellationException(); if (exception != null) throw new ExecutionException(exception); return result; } void innerSet(V v) { for (;;) { int s = getState(); if (s == RAN) return; if (s == CANCELLED) { // aggressively release to set runner to null, // in case we are racing with a cancel request // that will try to interrupt runner releaseShared(0); return; } if (compareAndSetState(s, RAN)) { result = v; releaseShared(0); future.done(); return; } } } void innerSetException(Throwable t) { for (;;) { int s = getState(); if (s == RAN) return; if (s == CANCELLED) { // aggressively release to set runner to null, // in case we are racing with a cancel request // that will try to interrupt runner releaseShared(0); return; } if (compareAndSetState(s, RAN)) { exception = t; result = null; releaseShared(0); future.done(); return; } } } boolean innerCancel(boolean mayInterruptIfRunning) { for (;;) { int s = getState(); if (ranOrCancelled(s)) return false; if (compareAndSetState(s, CANCELLED)) break; } if (mayInterruptIfRunning) { Thread r = runner; if (r != null) r.interrupt(); } releaseShared(0); future.done(); return true; } void innerRun() { if (!compareAndSetState(0, RUNNING)) return; try { runner = Thread.currentThread(); if (getState() == RUNNING) // recheck after setting thread innerSet(callable.call()); else releaseShared(0); // cancel } catch (Throwable ex) { innerSetException(ex); } } boolean innerRunAndReset() { if (!compareAndSetState(0, RUNNING)) return false; try { runner = Thread.currentThread(); if (getState() == RUNNING) callable.call(); // don't set result runner = null; return compareAndSetState(RUNNING, 0); } catch (Throwable ex) { innerSetException(ex); return false; } } } // @see org.jgroups.blocks.executor.ExecutorNotification#resultReturned(java.lang.Object) @SuppressWarnings("unchecked") @Override public void resultReturned(Object obj) { set((V)obj); } // @see org.jgroups.blocks.executor.ExecutorNotification#throwableEncountered(java.lang.Throwable) @Override public void throwableEncountered(Throwable t) { setException(t); } @Override public void interrupted(Runnable runnable) { _unfinishedLock.lock(); try { _unfinishedFutures.remove(this); _unfinishedCondition.signalAll(); } finally { _unfinishedLock.unlock(); } // We assign the listener to a local variable so we don't have to // worry about it becoming null inside the if FutureListener listener = _listener; // We don't want this to run on server if (listener != null) { listener.futureDone(this); } } } // @see java.util.concurrent.ExecutorService#shutdown() @Override public void shutdown() { _realShutdown(false); } @SuppressWarnings("unchecked") private List _realShutdown(boolean interrupt) { _shutdown.set(true); _unfinishedLock.lock(); Set> futures; try { futures = new HashSet>(_unfinishedFutures); } finally { _unfinishedLock.unlock(); } return (List)ch.downcall(new ExecutorEvent( ExecutorEvent.ALL_TASK_CANCEL, new Object[]{futures, interrupt})); } // @see java.util.concurrent.ExecutorService#shutdownNow() @Override public List shutdownNow() { return _realShutdown(true); } // @see java.util.concurrent.ExecutorService#isShutdown() @Override public boolean isShutdown() { return _shutdown.get(); } // @see java.util.concurrent.ExecutorService#isTerminated() @Override public boolean isTerminated() { if (_shutdown.get()) { _unfinishedLock.lock(); try { return _unfinishedFutures.isEmpty(); } finally { _unfinishedLock.unlock(); } } return false; } // @see java.util.concurrent.ExecutorService#awaitTermination(long, java.util.concurrent.TimeUnit) @Override public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { long nanoTimeWait = unit.toNanos(timeout); _unfinishedLock.lock(); try { while (!_unfinishedFutures.isEmpty()) { if ((nanoTimeWait = _unfinishedCondition.awaitNanos( nanoTimeWait)) <= 0) { return false; } } } finally { _unfinishedLock.unlock(); } return true; } // @see java.util.concurrent.AbstractExecutorService#invokeAny(java.util.Collection) @Override public T invokeAny(Collection> tasks) throws InterruptedException, ExecutionException { try { return doInvokeAny(tasks, false, 0); } catch (TimeoutException cannotHappen) { assert false; return null; } } // @see java.util.concurrent.AbstractExecutorService#invokeAny(java.util.Collection, long, java.util.concurrent.TimeUnit) @Override public T invokeAny(Collection> tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { return doInvokeAny(tasks, true, unit.toNanos(timeout)); } /** * the main mechanics of invokeAny. * This was essentially copied from {@link AbstractExecutorService} * doInvokeAny except that we replaced the {@link ExecutorCompletionService} * with an {@link ExecutionCompletionService}. */ private T doInvokeAny(Collection> tasks, boolean timed, long nanos) throws InterruptedException, ExecutionException, TimeoutException { if (tasks == null) throw new NullPointerException(); int ntasks = tasks.size(); if (ntasks == 0) throw new IllegalArgumentException(); List> futures= new ArrayList>(ntasks); CompletionService ecs = new ExecutionCompletionService(this); // For efficiency, especially in executors with limited // parallelism, check to see if previously submitted tasks are // done before submitting more of them. This interleaving // plus the exception mechanics account for messiness of main // loop. try { // Record exceptions so that if we fail to obtain any // result, we can throw the last exception we got. ExecutionException ee = null; long lastTime = (timed)? System.nanoTime() : 0; Iterator> it = tasks.iterator(); // Start one task for sure; the rest incrementally futures.add(ecs.submit(it.next())); --ntasks; int active = 1; for (;;) { Future f = ecs.poll(); if (f == null) { if (ntasks > 0) { --ntasks; futures.add(ecs.submit(it.next())); ++active; } else if (active == 0) break; else if (timed) { f = ecs.poll(nanos, TimeUnit.NANOSECONDS); if (f == null) throw new TimeoutException(); long now = System.nanoTime(); nanos -= now - lastTime; lastTime = now; } else f = ecs.take(); } if (f != null) { --active; try { return f.get(); } catch (InterruptedException ie) { throw ie; } catch (ExecutionException eex) { ee = eex; } catch (RuntimeException rex) { ee = new ExecutionException(rex); } } } if (ee == null) ee = new ExecutionException() { private static final long serialVersionUID = 200818694545553992L; }; throw ee; } finally { for (Future f : futures) f.cancel(true); } } // @see java.util.concurrent.Executor#execute(java.lang.Runnable) @Override public void execute(Runnable command) { if (!_shutdown.get()) { Object serializeCheck; // If it is wrapped by our future, then we have to make sure to // check the actual callable/runnable given to us for serialization if (command instanceof DistributedFuture) { serializeCheck = ((DistributedFuture)command).getCallable(); if (serializeCheck instanceof RunnableAdapter) { serializeCheck = ((RunnableAdapter)serializeCheck).task; } } else { serializeCheck = command; } if (serializeCheck instanceof Serializable || serializeCheck instanceof Streamable) { ch.down(new ExecutorEvent(ExecutorEvent.TASK_SUBMIT, command)); } else { throw new IllegalArgumentException( "Command was not Serializable or Streamable - " + serializeCheck); } } else { throw new RejectedExecutionException(); } } /** * This is copied from {@see java.util.concurrent.Executors} class which * contains RunnableAdapter. However that adapter isn't serializable, and * is final and package level so we can' reference. */ protected static final class RunnableAdapter implements Callable, Streamable { protected Runnable task; protected T result; protected RunnableAdapter() { } protected RunnableAdapter(Runnable task, T result) { this.task = task; this.result = result; } public T call() { task.run(); return result; } @Override public void writeTo(DataOutputStream out) throws IOException { try { Util.writeObject(task, out); } catch (IOException e) { throw e; } catch (Exception e) { throw new IOException("Exception encountered while writing execution runnable", e); } try { Util.writeObject(result, out); } catch (IOException e) { throw e; } catch (Exception e) { throw new IOException("Exception encountered while writing execution result", e); } } @SuppressWarnings("unchecked") @Override public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { // We can't use Util.readObject since it's size is limited to 2^15-1 // The runner could be larger than that possibly try { task = (Runnable)Util.readObject(in); } catch (IOException e) { throw e; } catch (Exception e) { throw new IOException("Exception encountered while reading execution runnable", e); } try { result = (T)Util.readObject(in); } catch (IOException e) { throw e; } catch (Exception e) { throw new IOException("Exception encountered while reading execution result", e); } } } // @see java.util.concurrent.AbstractExecutorService#newTaskFor(java.lang.Runnable, java.lang.Object) @Override protected RunnableFuture newTaskFor(Runnable runnable, T value) { DistributedFuture future = new DistributedFuture(ch, _unfinishedLock, _unfinishedCondition, _unfinishedFutures, runnable, value); _execProt.addExecutorListener(future, future); _unfinishedLock.lock(); try { _unfinishedFutures.add(future); } finally { _unfinishedLock.unlock(); } return future; } // @see java.util.concurrent.AbstractExecutorService#newTaskFor(java.util.concurrent.Callable) @Override protected RunnableFuture newTaskFor(Callable callable) { DistributedFuture future = new DistributedFuture(ch, _unfinishedLock, _unfinishedCondition, _unfinishedFutures, callable); _execProt.addExecutorListener(future, future); _unfinishedLock.lock(); try { _unfinishedFutures.add(future); } finally { _unfinishedLock.unlock(); } return future; } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/blocks/executor/Executions.java0000644000175000017500000001653611647260573027745 0ustar moellermoellerpackage org.jgroups.blocks.executor; 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; import java.lang.reflect.Constructor; import java.util.Arrays; import java.util.concurrent.Callable; public class Executions { /** * This method should be used to convert a callable that would not normally * be serializable, externalizable or streamable but has serializable, * externalizable or streamable arguments to a constructor to construct it. *

* When the call method is called on the callable it will call the provided * constructor passing in the given arguments. It will then invoke the call * method on resulting callable that was created. *

* The amount of arguments cannot exceed {@link Byte#MAX_VALUE}. Also the * constructor cannot exceed {@link Byte#MAX_VALUE} position in the * constructor array returned from {@link Class#getConstructors()} *

* The amount of arguments must match the amount of arguments required * by the constructor. Also the arguments must be compatibile with the * types required of the constructor. *

* Unfortunately it isn't easy to pass a Constructor> * so we can't pass back a callable that is properly typed. Also this * forces the caller to cast their callable or returned value to the correct * type manually. * * @param constructorToUse The constructor to use when creating the callable * @param args The arguments to pass to the constructor * @return The callable that will upon being called will instantiate the * given callable using the constructor with the provided arguments * and calls the call method * @throws IllegalArgumentException This is thrown if the arguments are * not serializable, externalizable or streamable. It can be thrown * if the constructo is not accessible. It can also be thrown * if too many arguments or the constructor is to high up in the * constructo array returned by the class. */ public static Callable serializableCallable(@SuppressWarnings("rawtypes") Constructor constructorToUse, Object... args) throws IllegalArgumentException { if (args.length > (int)Byte.MAX_VALUE) { throw new IllegalArgumentException( "Max number of arguments exceeded: " + Byte.MAX_VALUE); } Class[] params = constructorToUse.getParameterTypes(); if (params.length != args.length) { throw new IllegalArgumentException("Number of arguments [" + args.length + "] doesn't match number of arguments for " + "constructor [" + params.length + "]"); } for (int i = 0; i < args.length; ++i) { Object arg = args[i]; if (arg instanceof Serializable || arg instanceof Streamable) { Class classArg = params[i]; if (!classArg.isInstance(arg)) { throw new IllegalArgumentException("Argument [" + arg + "] is not an instance of [" + classArg + "]"); } } else { throw new IllegalArgumentException( "Argument is not serializable, externalizable or streamable: " + arg); } } @SuppressWarnings("unchecked") Class> classToUse = (Class>)constructorToUse.getDeclaringClass(); Constructor[] constructors = classToUse.getConstructors(); byte constructorPosition = -1; for (int i = 0; i < constructors.length; ++i) { Constructor constructor = constructors[i]; if (constructor.equals(constructorToUse)) { if (i > (int)Byte.MAX_VALUE) { throw new IllegalArgumentException( "Constructor position in array cannot be higher than " + Byte.MAX_VALUE); } constructorPosition = (byte)i; } } if (constructorPosition == -1) { throw new IllegalArgumentException( "Constructor was not found in public constructor array on class"); } return new StreamableCallable(classToUse, constructorPosition, args); } protected static class StreamableCallable implements Callable, Streamable { protected Class> _classCallable; protected short _constructorNumber; protected Object[] _args; public StreamableCallable() { } public StreamableCallable(Class> classCallable, byte constructorNumber, Object... args) { _classCallable = classCallable; _constructorNumber = constructorNumber; _args = args; } @Override public Object call() throws Exception { @SuppressWarnings("unchecked") // Unfortunately getConstructors doesn't return typed constructors // correctly so we have to cast Constructor> constructor = (Constructor>) _classCallable .getConstructors()[_constructorNumber]; Callable callable = constructor.newInstance(_args); return callable.call(); } @Override public void writeTo(DataOutputStream out) throws IOException { Util.writeClass(_classCallable, out); out.writeByte(_constructorNumber); out.writeByte(_args.length); for (Object arg : _args) { try { Util.writeObject(arg, out); } catch (Exception e) { throw new IOException("failed to write arg " + arg); } } } @SuppressWarnings("unchecked") @Override public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { try { _classCallable = (Class>)Util.readClass(in); } catch (ClassNotFoundException e) { throw new IOException("failed to read class from classname", e); } _constructorNumber = in.readByte(); short numberOfArgs = in.readByte(); _args = new Object[numberOfArgs]; for (int i = 0; i < numberOfArgs; ++i) { try { _args[i] = Util.readObject(in); } catch (Exception e) { throw new IOException("failed to read arg", e); } } } // @see java.lang.Object#toString() @Override public String toString() { return "StreamableCallable [class=" + _classCallable + ", constructor=" + _constructorNumber + ", arguments=" + Arrays.toString(_args) + "]"; } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/blocks/executor/ExecutorEvent.java0000644000175000017500000000135711647260573030412 0ustar moellermoellerpackage org.jgroups.blocks.executor; import org.jgroups.Event; /** * Defines an event class for the execution of an entity. * * @author wburns */ public class ExecutorEvent extends Event { public static final int TASK_SUBMIT = 1024; // arg = Runnable (Serializable) public static final int CONSUMER_READY = 1025; // arg = null public static final int TASK_COMPLETE = 1026; // arg = [Runnable, Throwable] or Runnable public static final int TASK_CANCEL = 1027; // arg = [Runnable, boolean] public static final int ALL_TASK_CANCEL = 1028; // arg = [Set, boolean] /** * @param type * @param arg */ public ExecutorEvent(int type, Object arg) { super(type, arg); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/blocks/executor/ExecutorNotification.java0000644000175000017500000000040311647260573031746 0ustar moellermoellerpackage org.jgroups.blocks.executor; /** * @author wburns */ public interface ExecutorNotification { public void resultReturned(Object obj); public void throwableEncountered(Throwable t); public void interrupted(Runnable runnable); } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/blocks/locking/0000755000175000017500000000000011647260573024531 5ustar moellermoellerlibjgroups-java-2.12.2.Final.orig/src/org/jgroups/blocks/locking/AwaitInfo.java0000644000175000017500000000103711647260573027256 0ustar moellermoellerpackage org.jgroups.blocks.locking; public class AwaitInfo { protected final String name; protected final boolean all; public AwaitInfo(String name, boolean all) { this.name=name; this.all=all; } /** * @return Returns the name. */ public String getName() { return name; } /** * @return Returns whether is all. */ public boolean isAll() { return all; } public String toString() { return name + ", awaitAll=" + all; } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/blocks/locking/LockInfo.java0000644000175000017500000000240011647260573027074 0ustar moellermoellerpackage org.jgroups.blocks.locking; import java.util.concurrent.TimeUnit; /** * @author Bela Ban */ public class LockInfo { protected final String name; protected final boolean is_trylock; protected final boolean lock_interruptibly; protected final boolean use_timeout; protected final long timeout; protected final TimeUnit time_unit; public LockInfo(String name, boolean is_trylock, boolean lock_interruptibly, boolean use_timeout, long timeout, TimeUnit time_unit) { this.name=name; this.is_trylock=is_trylock; this.lock_interruptibly=lock_interruptibly; this.use_timeout=use_timeout; this.timeout=timeout; this.time_unit=time_unit; } public boolean isTrylock() { return is_trylock; } public boolean isLockInterruptibly() { return lock_interruptibly; } public boolean isUseTimeout() { return use_timeout; } public String getName() { return name; } public long getTimeout() { return timeout; } public TimeUnit getTimeUnit() { return time_unit; } public String toString() { return name + ", trylock=" + is_trylock + ", timeout=" + timeout; } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/blocks/locking/LockNotification.java0000644000175000017500000000056211647260573030636 0ustar moellermoellerpackage org.jgroups.blocks.locking; /** * @author Bela Ban */ public interface LockNotification { void lockCreated(String name); void lockDeleted(String name); void locked(String lock_name, Owner owner); void unlocked(String lock_name, Owner owner); void awaiting(String lock_name, Owner owner); void awaited(String lock_name, Owner owner); } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/blocks/locking/LockService.java0000644000175000017500000001557511647260573027622 0ustar moellermoellerpackage org.jgroups.blocks.locking; import java.util.Date; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import org.jgroups.Event; import org.jgroups.JChannel; import org.jgroups.annotations.Experimental; import org.jgroups.protocols.Locking; /** * LockService is the main class for to use for distributed locking functionality. LockService needs access to a * {@link JChannel} and interacts with a locking protocol (e.g. {@link org.jgroups.protocols.CENTRAL_LOCK}) via events.

* When no locking protocol is seen on the channel's stack, LockService will throw an exception at startup. An example * of using LockService is: *

   JChannel ch=new JChannel("/home/bela/locking.xml); // locking.xml needs to have a locking protocol towards the top
   LockService lock_service=new LockService(ch);
   ch.connect("lock-cluster");
   Lock lock=lock_service.getLock("mylock");
   lock.lock();
   try {
      // do something with the lock acquired
   }
   finally {
      lock.unlock();
   }
 * 
* Note that, contrary to the semantics of {@link java.util.concurrent.locks.Lock}, unlock() can be called multiple * times; after a lock has been released, future calls to unlock() have no effect. * @author Bela Ban * @since 2.12 */ @Experimental public class LockService { protected JChannel ch; protected Locking lock_prot; public LockService() { } public LockService(JChannel ch) { setChannel(ch); } public void setChannel(JChannel ch) { this.ch=ch; lock_prot=(Locking)ch.getProtocolStack().findProtocol(Locking.class); if(lock_prot == null) throw new IllegalStateException("Channel configuration must include a locking protocol " + "(subclass of " + Locking.class.getName() + ")"); } public Lock getLock(String lock_name) { return new LockImpl(lock_name); } public void unlockAll() { ch.down(new Event(Event.UNLOCK_ALL)); } public void addLockListener(LockNotification listener) { lock_prot.addLockListener(listener); } public void removeLockListener(LockNotification listener) { lock_prot.removeLockListener(listener); } public String printLocks() { return lock_prot.printLocks(); } protected class LockImpl implements Lock { protected final String name; protected final AtomicReference holder = new AtomicReference(); public LockImpl(String name) { this.name=name; } public void lock() { ch.down(new Event(Event.LOCK, new LockInfo(name, false, false, false, 0, TimeUnit.MILLISECONDS))); holder.set(Thread.currentThread()); } public void lockInterruptibly() throws InterruptedException { ch.down(new Event(Event.LOCK, new LockInfo(name, false, true, false, 0, TimeUnit.MILLISECONDS))); Thread currentThread = Thread.currentThread(); if(currentThread.isInterrupted()) throw new InterruptedException(); else holder.set(Thread.currentThread()); } public boolean tryLock() { Boolean retval=(Boolean)ch.downcall(new Event(Event.LOCK, new LockInfo(name, true, false, false, 0, TimeUnit.MILLISECONDS))); if (retval == Boolean.TRUE) { holder.set(Thread.currentThread()); } return retval.booleanValue(); } public boolean tryLock(long time, TimeUnit unit) throws InterruptedException { Boolean retval=(Boolean)ch.downcall(new Event(Event.LOCK, new LockInfo(name, true, true, true, time, unit))); if(Thread.currentThread().isInterrupted()) throw new InterruptedException(); if (retval == Boolean.TRUE) { holder.set(Thread.currentThread()); } return retval.booleanValue(); } public void unlock() { ch.down(new Event(Event.UNLOCK, new LockInfo(name, false, false, false, 0, TimeUnit.MILLISECONDS))); holder.set(null); } /** * This condition object is only allowed to work 1 for each lock. * If more than 1 condition is created for this lock, they both will * be awaiting/signalling on the same lock */ public Condition newCondition() { return new ConditionImpl(name, holder); } } private class ConditionImpl implements Condition { protected final String name; protected final AtomicReference holder; public ConditionImpl(String name, AtomicReference holder) { this.name=name; this.holder=holder; } @Override public void await() throws InterruptedException { ch.down(new Event(Event.LOCK_AWAIT, new LockInfo(name, false, true, false, 0, TimeUnit.MILLISECONDS))); if(Thread.currentThread().isInterrupted()) throw new InterruptedException(); } @Override public void awaitUninterruptibly() { ch.down(new Event(Event.LOCK_AWAIT, new LockInfo(name, false, false, false, 0, TimeUnit.MILLISECONDS))); } @Override public long awaitNanos(long nanosTimeout) throws InterruptedException { Long waitLeft = (Long)ch.downcall(new Event(Event.LOCK_AWAIT, new LockInfo(name, false, true, true, nanosTimeout, TimeUnit.NANOSECONDS))); if(Thread.currentThread().isInterrupted()) throw new InterruptedException(); return waitLeft.longValue(); } @Override public boolean await(long time, TimeUnit unit) throws InterruptedException { return awaitNanos(unit.toNanos(time)) > 0; } @Override public boolean awaitUntil(Date deadline) throws InterruptedException { long waitUntilTime = deadline.getTime(); long currentTime = System.currentTimeMillis(); long waitTime = waitUntilTime - currentTime; return waitTime > 0 && await(waitTime, TimeUnit.MILLISECONDS); } @Override public void signal() { if (holder.get() != Thread.currentThread()) { throw new IllegalMonitorStateException(); } ch.down(new Event(Event.LOCK_SIGNAL, new AwaitInfo(name, false))); } @Override public void signalAll() { if (holder.get() != Thread.currentThread()) { throw new IllegalMonitorStateException(); } ch.down(new Event(Event.LOCK_SIGNAL, new AwaitInfo(name, true))); } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/blocks/locking/Owner.java0000644000175000017500000000254111647260573026470 0ustar moellermoellerpackage org.jgroups.blocks.locking; import org.jgroups.Address; import org.jgroups.util.Streamable; import org.jgroups.util.Util; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; /** * Represents the owner of a lock. Wraps address and thread ID * @author Bela Ban */ public class Owner implements Streamable { protected Address address; protected long thread_id; public Owner() { } public Owner(Address address, long thread_id) { this.address=address; this.thread_id=thread_id; } public Address getAddress() { return address; } public long getThreadId() { return thread_id; } public void writeTo(DataOutputStream out) throws IOException { Util.writeAddress(address, out); out.writeLong(thread_id); } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { address=Util.readAddress(in); thread_id=in.readLong(); } public boolean equals(Object obj) { Owner other=(Owner)obj; return address.equals(other.address) && thread_id == other.thread_id; } public int hashCode() { return (int)(address.hashCode() + thread_id); } public String toString() { return address + "::" + thread_id; } }libjgroups-java-2.12.2.Final.orig/src/org/jgroups/blocks/mux/0000755000175000017500000000000011647260573023714 5ustar moellermoellerlibjgroups-java-2.12.2.Final.orig/src/org/jgroups/blocks/mux/MuxHeader.java0000644000175000017500000000143411647260573026443 0ustar moellermoellerpackage org.jgroups.blocks.mux; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import org.jgroups.Global; import org.jgroups.Header; /** * Header that identifies the target handler for multiplexed dispatches. * @author Bela Ban * @author Paul Ferraro */ public class MuxHeader extends Header { private short id; public MuxHeader() { } public MuxHeader(short id) { this.id = id; } public short getId() { return id; } public int size() { return Global.SHORT_SIZE; } public void writeTo(DataOutputStream out) throws IOException { out.writeShort(id); } public void readFrom(DataInputStream in) throws IOException { id = in.readShort(); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/blocks/mux/MuxMessageDispatcher.java0000644000175000017500000000621611647260573030651 0ustar moellermoellerpackage org.jgroups.blocks.mux; import java.util.Collection; import org.jgroups.Address; import org.jgroups.Channel; import org.jgroups.MembershipListener; import org.jgroups.Message; import org.jgroups.MessageListener; import org.jgroups.UpHandler; import org.jgroups.blocks.GroupRequest; import org.jgroups.blocks.MessageDispatcher; import org.jgroups.blocks.RequestCorrelator; import org.jgroups.blocks.RequestHandler; import org.jgroups.blocks.RequestOptions; import org.jgroups.blocks.RspFilter; /** * A multiplexed message dispatcher. * When used in conjunction with a MuxUpHandler, allows multiple dispatchers to use the same channel. *
* Usage:
* * Channel c = new JChannel(...);
* c.setUpHandler(new MuxUpHandler());
*
* MessageDispatcher d1 = new MuxMessageDispatcher((short) 1, c, ...);
* MessageDispatcher d2 = new MuxMessageDispatcher((short) 2, c, ...);
*
* c.connect(...);
*
* @author Paul Ferraro */ public class MuxMessageDispatcher extends MessageDispatcher { private final short scope_id; public MuxMessageDispatcher(short scopeId) { this.scope_id = scopeId; } public MuxMessageDispatcher(short scopeId, Channel channel, MessageListener messageListener, MembershipListener membershipListener, RequestHandler handler) { this(scopeId); setMessageListener(messageListener); setMembershipListener(membershipListener); setChannel(channel); setRequestHandler(handler); start(); } private Muxer getMuxer() { UpHandler handler = channel.getUpHandler(); return ((handler != null) && (handler instanceof MuxUpHandler)) ? (MuxUpHandler) handler : null; } @Override protected RequestCorrelator createRequestCorrelator(Object transport, RequestHandler handler, Address localAddr) { // We can't set the scope of the request correlator here // since this method is called from start() triggered in the // MessageDispatcher constructor, when this.scope is not yet defined return new MuxRequestCorrelator(scope_id, transport, handler, localAddr); } @Override public void start() { super.start(); Muxer muxer = this.getMuxer(); if (muxer != null) { muxer.add(scope_id, this.getProtocolAdapter()); } } @Override public void stop() { Muxer muxer = this.getMuxer(); if (muxer != null) { muxer.remove(scope_id); } super.stop(); } @Override protected GroupRequest cast(Collection
dests, Message msg, RequestOptions options, boolean blockForResults) { RspFilter filter=options.getRspFilter(); RequestOptions newOptions = new RequestOptions(options.getMode(), options.getTimeout(), options.getAnycasting(), (filter != null) ? new NoMuxHandlerRspFilter(filter) : new NoMuxHandlerRspFilter(), options.getFlags()); return super.cast(dests, msg, newOptions, blockForResults); } }libjgroups-java-2.12.2.Final.orig/src/org/jgroups/blocks/mux/MuxRequestCorrelator.java0000644000175000017500000000312511647260573030737 0ustar moellermoellerpackage org.jgroups.blocks.mux; import java.util.Collection; import org.jgroups.Address; import org.jgroups.Message; import org.jgroups.blocks.RequestCorrelator; import org.jgroups.blocks.RequestHandler; import org.jgroups.blocks.RspCollector; import org.jgroups.blocks.RequestOptions; import org.jgroups.conf.ClassConfigurator; /** * A request correlator that adds a mux header to incoming and outgoing messages. * @author Bela Ban * @author Paul Ferraro * @author Brian Stansberry */ public class MuxRequestCorrelator extends RequestCorrelator { protected final static short MUX_ID = ClassConfigurator.getProtocolId(MuxRequestCorrelator.class); private final org.jgroups.Header header; public MuxRequestCorrelator(short id, Object transport, RequestHandler handler, Address localAddr) { super(ClassConfigurator.getProtocolId(RequestCorrelator.class), transport, handler, localAddr); this.header = new MuxHeader(id); } @Override public void sendRequest(long requestId, Collection
dest_mbrs, Message msg, RspCollector coll, RequestOptions options) throws Exception { msg.putHeader(MUX_ID, header); super.sendRequest(requestId, dest_mbrs, msg, coll, options); } @Override public void sendUnicastRequest(long id, Address target, Message msg, RspCollector coll) throws Exception { msg.putHeader(MUX_ID, header); super.sendUnicastRequest(id, target, msg, coll); } @Override protected void prepareResponse(Message rsp) { rsp.putHeader(MUX_ID, header); super.prepareResponse(rsp); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/blocks/mux/MuxRpcDispatcher.java0000644000175000017500000000744511647260573030016 0ustar moellermoellerpackage org.jgroups.blocks.mux; import java.util.Collection; import org.jgroups.Address; import org.jgroups.Channel; import org.jgroups.MembershipListener; import org.jgroups.Message; import org.jgroups.MessageListener; import org.jgroups.UpHandler; import org.jgroups.blocks.GroupRequest; import org.jgroups.blocks.MethodLookup; import org.jgroups.blocks.RequestCorrelator; import org.jgroups.blocks.RequestHandler; import org.jgroups.blocks.RequestOptions; import org.jgroups.blocks.RpcDispatcher; import org.jgroups.blocks.RspFilter; /** * A multiplexed message dispatcher. * When used in conjunction with a MuxUpHandler, allows multiple dispatchers to use the same channel. *
* Usage:
* * Channel c = new JChannel(...);
* c.setUpHandler(new MuxUpHandler());
*
* RpcDispatcher d1 = new MuxRpcDispatcher((short) 1, c, ...);
* RpcDispatcher d2 = new MuxRpcDispatcher((short) 2, c, ...);
*
* c.connect(...);
*
* @author Bela Ban * @author Paul Ferraro */ public class MuxRpcDispatcher extends RpcDispatcher { private final short scope_id; public MuxRpcDispatcher(short scopeId) { super(); this.scope_id = scopeId; } public MuxRpcDispatcher(short scopeId, Channel channel, MessageListener messageListener, MembershipListener membershipListener, Object serverObject) { this(scopeId); setMessageListener(messageListener); setMembershipListener(membershipListener); setServerObject(serverObject); setChannel(channel); channel.addChannelListener(this); start(); } public MuxRpcDispatcher(short scopeId, Channel channel, MessageListener messageListener, MembershipListener membershipListener, Object serverObject, MethodLookup method_lookup) { this(scopeId); setMethodLookup(method_lookup); setMessageListener(messageListener); setMembershipListener(membershipListener); setServerObject(serverObject); setChannel(channel); channel.addChannelListener(this); start(); } private Muxer getMuxer() { UpHandler handler = channel.getUpHandler(); return ((handler != null) && (handler instanceof MuxUpHandler)) ? (MuxUpHandler) handler : null; } @Override protected RequestCorrelator createRequestCorrelator(Object transport, RequestHandler handler, Address localAddr) { // We can't set the scope of the request correlator here // since this method is called from start() triggered in the // MessageDispatcher constructor, when this.scope is not yet defined return new MuxRequestCorrelator(scope_id, transport, handler, localAddr); } @Override public void start() { super.start(); Muxer muxer = this.getMuxer(); if (muxer != null) { muxer.add(scope_id, this.getProtocolAdapter()); } } @Override public void stop() { Muxer muxer = this.getMuxer(); if (muxer != null) { muxer.remove(scope_id); } super.stop(); } /** @Override protected GroupRequest cast(Collection
dests, Message msg, RequestOptions options, boolean blockForResults) { RspFilter filter = options.getRspFilter(); return super.cast(dests, msg, options.setRspFilter((filter != null) ? new NoMuxHandlerRspFilter(filter) : new NoMuxHandlerRspFilter()), blockForResults); }*/ @Override protected GroupRequest cast(Collection
dests, Message msg, RequestOptions options, boolean blockForResults) { RspFilter filter = options.getRspFilter(); return super.cast(dests, msg, options.setRspFilter(NoMuxHandlerRspFilter.createInstance(filter)), blockForResults); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/blocks/mux/MuxUpHandler.java0000644000175000017500000001141711647260573027137 0ustar moellermoellerpackage org.jgroups.blocks.mux; import org.jgroups.Event; import org.jgroups.Message; import org.jgroups.UpHandler; import org.jgroups.logging.Log; import org.jgroups.logging.LogFactory; import org.jgroups.util.ImmutableReference; import org.jgroups.util.Util; import java.util.Map; /** * Allows up handler multiplexing. * * @author Bela Ban * @author Paul Ferraro */ public class MuxUpHandler implements UpHandler, Muxer { protected final Log log=LogFactory.getLog(getClass()); private final Map handlers=Util.createConcurrentMap(); private volatile UpHandler defaultHandler; private volatile Event lastFlushEvent; private final Object flushMutex = new Object(); /** * Creates a multiplexing up handler, with no default handler. */ public MuxUpHandler() { this.defaultHandler = null; } /** * Creates a multiplexing up handler using the specified default handler. * @param defaultHandler a default up handler to handle messages with no {@link MuxHeader} */ public MuxUpHandler(UpHandler defaultHandler) { this.defaultHandler = defaultHandler; } /** * {@inheritDoc} * @see org.jgroups.blocks.mux.Muxer#add(short, java.lang.Object) */ @Override public void add(short id, UpHandler handler) { synchronized (flushMutex) { if (lastFlushEvent != null) { handler.up(lastFlushEvent); } handlers.put(id, handler); } } /** * {@inheritDoc} * @see org.jgroups.blocks.mux.Muxer#get(short) */ @Override public UpHandler get(short id) { return handlers.get(id); } /** * {@inheritDoc} * @see org.jgroups.blocks.mux.Muxer#remove(short) */ @Override public void remove(short id) { handlers.remove(id); } @Override public UpHandler getDefaultHandler() { return defaultHandler; } @Override public void setDefaultHandler(UpHandler handler) { this.defaultHandler = handler; } /** * {@inheritDoc} * @see org.jgroups.UpHandler#up(org.jgroups.Event) */ @Override public Object up(Event evt) { switch (evt.getType()) { case Event.MSG: { Message msg = (Message) evt.getArg(); MuxHeader hdr = (MuxHeader) msg.getHeader(MuxRequestCorrelator.MUX_ID); if (hdr != null) { short id = hdr.getId(); UpHandler handler = handlers.get(id); return (handler != null) ? handler.up(evt) : new NoMuxHandler(id); } break; } case Event.GET_APPLSTATE: case Event.GET_STATE_OK: case Event.STATE_TRANSFER_OUTPUTSTREAM: case Event.STATE_TRANSFER_INPUTSTREAM: { ImmutableReference wrapper = handleStateTransferEvent(evt); if (wrapper != null) { return wrapper.get(); } break; } case Event.BLOCK: case Event.UNBLOCK: { synchronized (flushMutex) { this.lastFlushEvent = evt; passToAllHandlers(evt); break; } } case Event.VIEW_CHANGE: case Event.SET_LOCAL_ADDRESS: case Event.SUSPECT: { passToAllHandlers(evt); break; } default: { passToAllHandlers(evt); break; } } return (defaultHandler != null) ? defaultHandler.up(evt) : null; } /** * Extension point for subclasses called by up() when an event * related to state transfer is received, allowing the subclass * to override the default behavior of passing the event to the * default up handler. * * @return an AtomicReference containing the return value for the event * if the event was handled and no further processing * should be done in up(), or null if up() needs to * handle the event. If the event was handled but the return value * is null, an AtomicReference initialized to * null should be returned. This default * implementation always returns null */ protected ImmutableReference handleStateTransferEvent(Event evt) { return null; } private void passToAllHandlers(Event evt) { for (UpHandler handler: handlers.values()) { handler.up(evt); } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/blocks/mux/Muxer.java0000644000175000017500000000231411647260573025657 0ustar moellermoellerpackage org.jgroups.blocks.mux; /** * Allows registration/deregistrator of multiplexed handlers by mux id. * @author Paul Ferraro */ public interface Muxer { /** * Registers the specified handler to handle messages containing a mux header with the specified mux identifier. * @param id a mux id * @param handler a handler for the specified id */ void add(short id, T handler); /** * Gets the handler registered under the specified id * @param id a mux id * @return the handler, or null if no handler is registered under * id */ T get(short id); /** * Unregisters the handler associated with the specifed mux identifier * @param id a mux id */ void remove(short id); /** * Gets the handler for messages that have no mux header. * * @return the default handler, or null if no default handler * has been set */ T getDefaultHandler(); /** * Sets the handler for messages that have no mux header. * * @param handler a handler for messages that have no mux header */ void setDefaultHandler(T handler); } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/blocks/mux/NoMuxHandler.java0000644000175000017500000000146111647260573027125 0ustar moellermoellerpackage org.jgroups.blocks.mux; import java.io.Serializable; /** * Returned by {@link MuxUpHandler} when a message is received with a specific {@link MuxHeader}, but no corresponding handler is registered. * @author Paul Ferraro */ public class NoMuxHandler implements Serializable { private static final long serialVersionUID = -694135384125080323L; private short id; public NoMuxHandler(short id) { this.id = id; } public short getId() { return id; } @Override public boolean equals(Object object) { if ((object == null) || !(object instanceof NoMuxHandler)) return false; NoMuxHandler handler = (NoMuxHandler) object; return (id == handler.id); } @Override public int hashCode() { return id; } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/blocks/mux/NoMuxHandlerRspFilter.java0000644000175000017500000000203611647260573030757 0ustar moellermoellerpackage org.jgroups.blocks.mux; import org.jgroups.Address; import org.jgroups.blocks.RspFilter; /** * Response filter that reject any {@link NoMuxHandler} responses. * @author Paul Ferraro */ public class NoMuxHandlerRspFilter implements RspFilter { private final RspFilter filter; public NoMuxHandlerRspFilter() { this.filter = null; } public NoMuxHandlerRspFilter(RspFilter filter) { this.filter = filter; } public static RspFilter createInstance(RspFilter filter) { if(filter instanceof NoMuxHandlerRspFilter) return filter; return new NoMuxHandlerRspFilter(filter) ; } public RspFilter getFilter() { return filter; } @Override public boolean isAcceptable(Object response, Address sender) { return !(response instanceof NoMuxHandler) && ((filter == null) || filter.isAcceptable(response, sender)); } @Override public boolean needMoreResponses() { return (filter == null) || filter.needMoreResponses(); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/blocks/package.html0000644000175000017500000000132011647260573025360 0ustar moellermoeller 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. libjgroups-java-2.12.2.Final.orig/src/org/jgroups/client/0000755000175000017500000000000011647260573023104 5ustar moellermoellerlibjgroups-java-2.12.2.Final.orig/src/org/jgroups/client/StompConnection.java0000644000175000017500000003173211647260573027077 0ustar moellermoellerpackage org.jgroups.client; import org.jgroups.annotations.Experimental; import org.jgroups.annotations.Unsupported; import org.jgroups.logging.Log; import org.jgroups.logging.LogFactory; import org.jgroups.protocols.STOMP; import org.jgroups.util.Util; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.net.*; import java.util.*; /** * STOMP client to access the STOMP [1] protocol. Note that the full STOMP protocol is not implemented, e.g. transactions * are currently not supported. *

* The interactive client can be started with -h HOST -p PORT, which are the hostname and port of a JGroups server, running * with STOMP in its stack configuration. The interactive client supports automatic failover to a different server if * the currently connected-to server crashes, and a simple syntax for sending STOMP messages: *

 * subscribe DEST // example: subscribe /topics/a
 * send DEST message // example: send /topics/a Hello world
 * 
*

* [1] http://stomp.codehaus.org/Protocol * @author Bela Ban */ @Experimental @Unsupported public class StompConnection implements Runnable { protected Socket sock; protected DataInputStream in; protected DataOutputStream out; // collection of server addresses, we can pick any one to connect to protected final Set server_destinations=new HashSet(); protected final Set listeners=new HashSet(); protected final Set subscriptions=new HashSet(); protected Thread runner; protected volatile boolean running=true; protected String session_id; protected final Log log=LogFactory.getLog(getClass()); /** * @param dest IP address + ':' + port, e.g. "192.168.1.5:8787" */ public StompConnection(String dest) { server_destinations.add(dest); } public String getSessionId() {return session_id;} public void addListener(Listener listener) { if(listener != null) listeners.add(listener); } public void removeListener(Listener listener) { if(listener != null) listeners.add(listener); } public void connect(String userid, String password) throws IOException { String dest; if(isConnected()) return; while((dest=pickRandomDestination()) != null) { try { connect(dest); if(log.isDebugEnabled()) log.debug("connected to " + dest); break; } catch(IOException ex) { if(log.isErrorEnabled()) log.error("failed connecting to " + dest); close(); server_destinations.remove(dest); } } if(!isConnected()) throw new IOException("no target server available"); StringBuilder sb=new StringBuilder(); sb.append(STOMP.ClientVerb.CONNECT.name()).append("\n"); if(userid != null) sb.append("login: ").append(userid).append("\n"); if(password != null) sb.append("passcode: ").append(password).append("\n"); sb.append("\n"); out.write(sb.toString().getBytes()); out.write(STOMP.NULL_BYTE); out.flush(); } public void reconnect() throws IOException { if(!running) return; connect(); for(String subscription: subscriptions) subscribe(subscription); if(log.isDebugEnabled()) { log.debug("reconnected to " + sock.getInetAddress().getHostAddress() + ":" + sock.getPort()); if(!subscriptions.isEmpty()) log.debug("re-subscribed to " + subscriptions); } } public void connect() throws IOException { connect(null, null); } public void disconnect() { running=false; close(); } public void subscribe(String destination) { if(destination == null) return; subscriptions.add(destination); StringBuilder sb=new StringBuilder(); sb.append(STOMP.ClientVerb.SUBSCRIBE.name()).append("\n"); sb.append("destination: ").append(destination).append("\n"); sb.append("\n"); try { out.write(sb.toString().getBytes()); out.write(STOMP.NULL_BYTE); out.flush(); } catch(IOException ex) { log.error("failed subscribing to " + destination + ": " + ex); } } public void unsubscribe(String destination) { if(destination == null) return; subscriptions.remove(destination); StringBuilder sb=new StringBuilder(); sb.append(STOMP.ClientVerb.UNSUBSCRIBE.name()).append("\n"); sb.append("destination: ").append(destination).append("\n"); sb.append("\n"); try { out.write(sb.toString().getBytes()); out.write(STOMP.NULL_BYTE); out.flush(); } catch(IOException ex) { log.error("failed unsubscribing from " + destination + ": " + ex); } } public void send(String destination, byte[] buf, int offset, int length, String ... headers) { StringBuilder sb=new StringBuilder(); sb.append(STOMP.ClientVerb.SEND.name()).append("\n"); if(destination != null) sb.append("destination: ").append(destination).append("\n"); if(buf != null) sb.append("content-length: ").append(length).append("\n"); if(headers != null && headers.length % 2 == 0) { // must be even for(int i=0; i < headers.length; i++) sb.append(headers[i]).append(": ").append(headers[++i]).append("\n"); } sb.append("\n"); try { out.write(sb.toString().getBytes()); if(buf != null) out.write(buf, offset, length); out.write(STOMP.NULL_BYTE); out.flush(); } catch(IOException ex) { log.error("failed sending message to server: " + ex); } } /** * Sends an INFO without body */ public void send(String destination, String ... headers) { send(destination, null, 0, 0, headers); } public void send(String destination, byte[] buf, int offset, int length) { send(destination, buf, offset, length, (String[])null); } public void send(String destination, byte[] buf) { send(destination, buf, 0, buf.length); } public void run() { while(isConnected() && running) { try { STOMP.Frame frame=STOMP.readFrame(in); if(frame != null) { STOMP.ServerVerb verb=STOMP.ServerVerb.valueOf(frame.getVerb()); if(log.isTraceEnabled()) log.trace("frame: " + frame); switch(verb) { case MESSAGE: byte[] buf=frame.getBody(); notifyListeners(frame.getHeaders(), buf, 0, buf != null? buf.length : 0); break; case CONNECTED: String sess_id=frame.getHeaders().get("session-id"); if(sess_id != null) { this.session_id=sess_id; } break; case ERROR: break; case INFO: notifyListeners(frame.getHeaders()); String endpoints=frame.getHeaders().get("endpoints"); if(endpoints != null) { List list=Util.parseCommaDelimitedStrings(endpoints); if(list != null) { boolean changed=server_destinations.addAll(list); if(changed && log.isDebugEnabled()) log.debug("INFO: new server target list: " + server_destinations); } } break; case RECEIPT: break; default: throw new IllegalArgumentException("verb " + verb + " is not known"); } } } catch(IOException e) { close(); try { reconnect(); } catch(IOException e1) { log.warn("failed to reconnect; runner thread terminated, cause: " + e1); } } catch(Throwable t) { log.error("failure reading frame", t); } } } protected void notifyListeners(Map headers, byte[] buf, int offset, int length) { for(Listener listener: listeners) { try { listener.onMessage(headers, buf, offset, length); } catch(Throwable t) { log.error("failed calling listener", t); } } } protected void notifyListeners(Map info) { for(Listener listener: listeners) { try { listener.onInfo(info); } catch(Throwable t) { log.error("failed calling listener", t); } } } protected String pickRandomDestination() { return server_destinations.isEmpty()? null : server_destinations.iterator().next(); } protected void connect(String dest) throws IOException { SocketAddress saddr=parse(dest); sock=new Socket(); sock.connect(saddr); in=new DataInputStream(sock.getInputStream()); out=new DataOutputStream(sock.getOutputStream()); startRunner(); } protected static SocketAddress parse(String dest) throws UnknownHostException { int index=dest.lastIndexOf(":"); String host=dest.substring(0, index); int port=Integer.parseInt(dest.substring(index+1)); return new InetSocketAddress(host, port); } protected void close() { Util.close(in); Util.close(out); Util.close(sock); } public boolean isConnected() { return sock != null && sock.isConnected() && !sock.isClosed(); } protected synchronized void startRunner() { if(runner == null || !runner.isAlive()) { runner=new Thread(this, "StompConnection receiver"); runner.start(); } } public static interface Listener { void onMessage(Map headers, byte[] buf, int offset, int length); void onInfo(Map information); } public static void main(String[] args) throws IOException { String host="localhost"; String port="8787"; for(int i=0; i < args.length; i++) { if(args[i].equals("-h")) { host=args[++i]; continue; } if(args[i].equals("-p")) { port=args[++i]; continue; } System.out.println("StompConnection [-h host] [-p port]"); return; } StompConnection conn=new StompConnection(host+ ":" + port); conn.addListener(new Listener() { public void onMessage(Map headers, byte[] buf, int offset, int length) { System.out.println("<< " + new String(buf, offset, length) + ", headers: " + headers); } public void onInfo(Map information) { System.out.println("<< INFO: " + information); } }); conn.connect(); while(conn.isConnected()) { try { String line=Util.readStringFromStdin(": "); if(line.startsWith("subscribe")) { String dest=line.substring("subscribe".length()).trim(); conn.subscribe(dest); } else if(line.startsWith("unsubscribe")) { String dest=line.substring("unsubscribe".length()).trim(); conn.unsubscribe(dest); } else if(line.startsWith("send")) { String rest=line.substring("send".length()).trim(); int index=rest.indexOf(' '); if(index != -1) { String dest=rest.substring(0, index); String body=rest.substring(index+1); byte[] buf=body.getBytes(); conn.send(dest, buf, 0, buf.length); } } else if(line.startsWith("disconnect")) { conn.disconnect(); } } catch(Exception e) { e.printStackTrace(); } } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/conf/0000755000175000017500000000000011647260573022553 5ustar moellermoellerlibjgroups-java-2.12.2.Final.orig/src/org/jgroups/conf/ClassConfigurator.java0000644000175000017500000002644511647260573027061 0ustar moellermoeller package org.jgroups.conf; import org.jgroups.ChannelException; import org.jgroups.Global; import org.jgroups.logging.Log; import org.jgroups.logging.LogFactory; import org.jgroups.util.Tuple; import org.jgroups.util.Util; import org.w3c.dom.Document; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.*; /** * 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 */ public class ClassConfigurator { public static final String MAGIC_NUMBER_FILE = "jg-magic-map.xml"; public static final String PROTOCOL_ID_FILE = "jg-protocol-ids.xml"; private static final short MIN_CUSTOM_MAGIC_NUMBER=1024; private static final short MIN_CUSTOM_PROTOCOL_ID=512; // this is where we store magic numbers; contains data from jg-magic-map.xml private static final Map classMap=Util.createConcurrentMap(150, 0.75f, 150); // key=Class, value=magic number private static final Map magicMap=Util.createConcurrentMap(150, 0.75f, 150); // key=magic number, value=Class /** Contains data read from jg-protocol-ids.xml */ private static final Map protocol_ids=Util.createConcurrentMap(150, 0.75f, 150); private static final Map protocol_names=Util.createConcurrentMap(150, 0.75f, 150); protected static final Log log=LogFactory.getLog(ClassConfigurator.class); static { try { init(); } catch(Exception e) { throw new ExceptionInInitializerError(e); } } public ClassConfigurator() { } protected static void init() throws ChannelException { try { // make sure we have a class for DocumentBuilderFactory Util.loadClass("javax.xml.parsers.DocumentBuilderFactory", ClassConfigurator.class); String magic_number_file=null, protocol_id_file=null; try { // PropertyPermission not granted if running in an untrusted environment with JNLP magic_number_file=Util.getProperty(new String[]{Global.MAGIC_NUMBER_FILE, "org.jgroups.conf.magicNumberFile"}, null, null, false, MAGIC_NUMBER_FILE); protocol_id_file=Util.getProperty(new String[]{Global.PROTOCOL_ID_FILE, "org.jgroups.conf.protocolIDFile"}, null, null, false, PROTOCOL_ID_FILE); if(log.isDebugEnabled()) log.debug("Using " + magic_number_file + " as magic number file and " + protocol_id_file + " for protocol IDs"); } catch (SecurityException ex){ } // Read jg-magic-map.xml List> mapping=readMappings(magic_number_file); for(Tuple tuple: mapping) { short m=tuple.getVal1(); try { Class clazz=Util.loadClass(tuple.getVal2(), ClassConfigurator.class); if(magicMap.containsKey(m)) throw new ChannelException("key " + m + " (" + clazz.getName() + ')' + " is already in magic map; please make sure that all keys are unique"); magicMap.put(m, clazz); classMap.put(clazz, m); } catch(ClassNotFoundException cnf) { throw new ChannelException("failed loading class", cnf); } } // Read jg-protocol-ids.xml mapping=readMappings(protocol_id_file); for(Tuple tuple: mapping) { short m=tuple.getVal1(); try { Class clazz=Util.loadClass(tuple.getVal2(), ClassConfigurator.class); if(protocol_ids.containsKey(clazz)) throw new ChannelException("ID " + m + " (" + clazz.getName() + ')' + " is already in protocol-ID map; please make sure that all protocol IDs are unique"); protocol_ids.put(clazz, m); protocol_names.put(m, clazz); } catch(ClassNotFoundException cnf) { throw new ChannelException("failed loading class", cnf); } } } catch(ChannelException ex) { throw ex; } catch(Throwable x) { throw new ChannelException("failed reading the magic number mapping file", x); } } /** * 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 static 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); } public static void addProtocol(short id, Class protocol) { if(id <= MIN_CUSTOM_PROTOCOL_ID) throw new IllegalArgumentException("protocol ID (" + id + ") needs to be greater than " + MIN_CUSTOM_PROTOCOL_ID); if(protocol_ids.containsKey(protocol)) throw new IllegalArgumentException("Protocol " + protocol + " is already present"); protocol_ids.put(protocol, id); } /** * 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 static 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 static Class get(String clazzname) { try { // return ClassConfigurator.class.getClassLoader().loadClass(clazzname); return Util.loadClass(clazzname, ClassConfigurator.class); } 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 static short getMagicNumber(Class clazz) { Short i=classMap.get(clazz); if(i == null) return -1; else return i; } public static short getProtocolId(Class protocol) { Short retval=protocol_ids.get(protocol); if(retval != null) return retval; return 0; } public static Class getProtocol(short id) { return protocol_names.get(id); } public String toString() { return printMagicMap(); } public static 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 static 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(); } /** * 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 */ protected static List> readMappings(String name) throws Exception { InputStream stream; try { stream=Util.getResourceAsStream(name, ClassConfigurator.class); // try to load the map from file even if it is not a Resource in the class path if(stream == null) { if(log.isTraceEnabled()) log.trace("Could not read " + name + " from the CLASSPATH, will try to read it from file"); stream=new FileInputStream(name); } } catch(Exception x) { throw new ChannelException(name + " not found. Please make sure it is on the classpath", x); } return parse(stream); } protected static List> 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"); List> list=new LinkedList>(); for(int i=0; i < class_list.getLength(); i++) { if(class_list.item(i).getNodeType() == Node.ELEMENT_NODE) { list.add(parseClassData(class_list.item(i))); } } return list; } protected static Tuple parseClassData(Node protocol) throws java.io.IOException { try { protocol.normalize(); NamedNodeMap attrs=protocol.getAttributes(); String clazzname; String magicnumber; magicnumber=attrs.getNamedItem("id").getNodeValue(); clazzname=attrs.getNamedItem("name").getNodeValue(); return new Tuple(Short.valueOf(magicnumber), clazzname); } catch(Exception x) { IOException tmp=new IOException(); tmp.initCause(x); throw tmp; } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/conf/ConfiguratorFactory.java0000644000175000017500000003617511647260573027424 0ustar moellermoeller package org.jgroups.conf; import org.jgroups.logging.Log; import org.jgroups.logging.LogFactory; import org.jgroups.ChannelException; import org.jgroups.JChannel; import org.jgroups.util.Util; import org.w3c.dom.Element; import java.io.*; import java.net.MalformedURLException; import java.net.URL; import java.security.AccessControlException; import java.util.List; import java.util.Map; /** * 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 */ 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 Log log=LogFactory.getLog(ConfiguratorFactory.class); 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 { try { checkJAXPAvailability(); InputStream input=getConfigStream(file); return XmlConfigurator.getInstance(input); } catch(Exception ex) { throw createChannelConfigurationException(ex); } } /** * 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 { try { checkForNullConfiguration(url); checkJAXPAvailability(); return XmlConfigurator.getInstance(url); } catch (IOException ioe) { throw createChannelConfigurationException(ioe); } } /** * 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 { try { checkForNullConfiguration(element); return XmlConfigurator.getInstance(element); } catch (IOException ioe) { throw createChannelConfigurationException(ioe); } } /** * 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 configuration, * or a string representing a file name that contains a JGroups XML configuration. */ public static ProtocolStackConfigurator getStackConfigurator(String properties) throws ChannelException { // added by bela: for null String props we use the default properties if(properties == null) properties=JChannel.DEFAULT_PROTOCOL_STACK; // Attempt to treat the properties string as a pointer to an XML configuration. XmlConfigurator configurator = null; try { checkForNullConfiguration(properties); configurator=getXmlConfigurator(properties); } catch (IOException ioe) { throw createChannelConfigurationException(ioe); } // Did the properties string point to a JGroups XML configuration? if (configurator != null) { return configurator; } else { // Attempt to process the properties string as the old style property string. return new PlainConfigurator(properties); } } /** * 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; // 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 { try { checkForNullConfiguration(file); return new FileInputStream(file); } catch(IOException ioe) { throw createChannelConfigurationException(ioe); } } public static InputStream getConfigStream(URL url) throws Exception { 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; // 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; // 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(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) { List protocols=configurator.getProtocolStack(); for(ProtocolConfiguration data: protocols) { if(data != null) { Map parms=data.getProperties(); for(Map.Entry entry:parms.entrySet()) { String val=entry.getValue(); String replacement=Util.substituteVariable(val); if(!replacement.equals(val)) { entry.setValue(replacement); } } } } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/conf/PlainConfigurator.java0000644000175000017500000000225211647260573027045 0ustar moellermoeller package org.jgroups.conf; import org.jgroups.stack.Configurator; import java.util.List; /** * 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) * @author Bela Ban * @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; } public List getProtocolStack() { try { return Configurator.parseConfigurations(mProperties); } catch(Exception e) { throw new RuntimeException(e); } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/conf/PropertyConverter.java0000644000175000017500000000137511647260573027140 0ustar moellermoellerpackage org.jgroups.conf; import org.jgroups.annotations.Property; /** * Represents a property converter that takes an input from corresponding field * in JGroups properties file as a String and converts it to a matching Java * type. * * @see PropertyConverters * @see Property * * @author Vladimir Blagojevic */ public interface PropertyConverter { Object convert(Object obj, Class propertyFieldType, String propertyName, String propertyValue, boolean check_scope) throws Exception; /** * Converts the value to a string. The default is to simply invoke Object.toString(), however, some objects need * to be printed specially, e.g. a long array etc. * @param value * @return */ String toString(Object value); } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/conf/PropertyConverters.java0000644000175000017500000002300611647260573027316 0ustar moellermoellerpackage org.jgroups.conf; import org.jgroups.Global; import org.jgroups.View; import org.jgroups.stack.Configurator; import org.jgroups.stack.Protocol; import org.jgroups.util.StackType; import org.jgroups.util.Util; import java.lang.reflect.Field; import java.net.*; import java.util.ArrayList; import java.util.Enumeration; import java.util.List; import java.util.concurrent.Callable; /** * Groups a set of standard PropertyConverter(s) supplied by JGroups. * *

* Third parties can provide their own converters if such need arises by implementing * {@link PropertyConverter} interface and by specifying that converter as converter on a specific * Property annotation of a field or a method instance. * * @author Vladimir Blagojevic */ public class PropertyConverters { public static class NetworkInterfaceList implements PropertyConverter { public Object convert(Object obj, Class propertyFieldType, String propertyName, String propertyValue, boolean check_scope) throws Exception { return Util.parseInterfaceList(propertyValue); } public String toString(Object value) { List list=(List)value; return Util.print(list); } } public static class FlushInvoker implements PropertyConverter { public Object convert(Object obj, Class propertyFieldType, String propertyName, String propertyValue, boolean check_scope) throws Exception { if (propertyValue == null) { return null; } else { Class> invoker = (Class>) Class.forName(propertyValue); invoker.getDeclaredConstructor(View.class); return invoker; } } public String toString(Object value) { return value.getClass().getName(); } } public static class InitialHosts implements PropertyConverter { public Object convert(Object obj, Class propertyFieldType, String propertyName, String prop_val, boolean check_scope) throws Exception { int port_range = getPortRange((Protocol)obj) ; return Util.parseCommaDelimitedHosts(prop_val, port_range); } public String toString(Object value) { return value.getClass().getName(); } private static int getPortRange(Protocol protocol) throws Exception { Field f = protocol.getClass().getDeclaredField("port_range") ; return ((Integer) Configurator.getField(f,protocol)).intValue(); } } public static class InitialHosts2 implements PropertyConverter { public Object convert(Object obj, Class propertyFieldType, String propertyName, String prop_val, boolean check_scope) throws Exception { // port range is 1 return Util.parseCommaDelimitedHosts2(prop_val, 1); } public String toString(Object value) { return value.getClass().getName(); } } public static class BindInterface implements PropertyConverter { public Object convert(Object obj, Class propertyFieldType, String propertyName, String propertyValue, boolean check_scope) throws Exception { // get the existing bind address - possibly null InetAddress old_bind_addr = (InetAddress)Configurator.getValueFromProtocol((Protocol)obj, "bind_addr"); // apply a bind interface constraint InetAddress new_bind_addr = Util.validateBindAddressFromInterface(old_bind_addr, propertyValue); if (new_bind_addr != null) setBindAddress((Protocol)obj, new_bind_addr) ; // if no bind_interface specified, set it to the empty string to avoid exception // from @Property processing if (propertyValue != null) return propertyValue ; else return "" ; } private static void setBindAddress(Protocol protocol, InetAddress bind_addr) throws Exception { Field f=Util.getField(protocol.getClass(), "bind_addr"); Configurator.setField(f, protocol, bind_addr) ; } // return a String version of the converted value public String toString(Object value) { return (String) value ; } } public static class LongArray implements PropertyConverter { public Object convert(Object obj, Class propertyFieldType, String propertyName, String propertyValue, boolean check_scope) throws Exception { long tmp [] = Util.parseCommaDelimitedLongs(propertyValue); if(tmp != null && tmp.length > 0){ return tmp; }else{ // throw new Exception ("Invalid long array specified in " + propertyValue); return null; } } public String toString(Object value) { if(value == null) return null; long[] val=(long[])value; StringBuilder sb=new StringBuilder(); boolean first=true; for(long l: val) { if(first) first=false; else sb.append(","); sb.append(l); } return sb.toString(); } } public static class Default implements PropertyConverter { static final String prefix; static { String tmp="FF0e::"; try { tmp=System.getProperty(Global.IPV6_MCAST_PREFIX); } catch(Throwable t) { tmp="FF0e::"; } prefix=tmp != null? tmp : "FF0e::"; } public Object convert(Object obj, Class propertyFieldType, String propertyName, String propertyValue, boolean check_scope) throws Exception { if(propertyValue == null) throw new NullPointerException("Property value cannot be null"); if(Boolean.TYPE.equals(propertyFieldType)) { return Boolean.parseBoolean(propertyValue); } else if (Integer.TYPE.equals(propertyFieldType)) { // return Integer.parseInt(propertyValue); return Util.readBytesInteger(propertyValue); } else if (Long.TYPE.equals(propertyFieldType)) { // return Long.parseLong(propertyValue); return Util.readBytesLong(propertyValue); } else if (Byte.TYPE.equals(propertyFieldType)) { return Byte.parseByte(propertyValue); } else if (Double.TYPE.equals(propertyFieldType)) { // return Double.parseDouble(propertyValue); return Util.readBytesDouble(propertyValue); } else if (Short.TYPE.equals(propertyFieldType)) { return Short.parseShort(propertyValue); } else if (Float.TYPE.equals(propertyFieldType)) { return Float.parseFloat(propertyValue); } else if(InetAddress.class.equals(propertyFieldType)) { InetAddress retval=null; Util.AddressScope addr_scope=null; try { addr_scope=Util.AddressScope.valueOf(propertyValue.toUpperCase()); } catch(Throwable ex) { } if(addr_scope != null) retval=Util.getAddress(addr_scope); else retval=InetAddress.getByName(propertyValue); if(retval instanceof Inet4Address && retval.isMulticastAddress() && Util.getIpStackType() == StackType.IPv6) { String tmp=prefix + propertyValue; retval=InetAddress.getByName(tmp); return retval; } if(check_scope && retval instanceof Inet6Address && retval.isLinkLocalAddress()) { // check scope Inet6Address addr=(Inet6Address)retval; int scope=addr.getScopeId(); if(scope == 0) { // fix scope Inet6Address ret=getScopedInetAddress(addr); if(ret != null) { retval=ret; } } } return retval; } return propertyValue; } protected static Inet6Address getScopedInetAddress(Inet6Address addr) { if(addr == null) return null; Enumeration en; List retval=new ArrayList(); try { en=NetworkInterface.getNetworkInterfaces(); while(en.hasMoreElements()) { NetworkInterface intf=en.nextElement(); Enumeration addrs=intf.getInetAddresses(); while(addrs.hasMoreElements()) { InetAddress address=addrs.nextElement(); if(address.isLinkLocalAddress() && address instanceof Inet6Address && address.equals(addr) && ((Inet6Address)address).getScopeId() != 0) { retval.add(address); } } } if(retval.size() == 1) { return (Inet6Address)retval.get(0); } else return null; } catch(SocketException e) { return null; } } public String toString(Object value) { return value != null? value.toString() : null; } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/conf/PropertyHelper.java0000644000175000017500000001756111647260573026414 0ustar moellermoellerpackage org.jgroups.conf ; import org.jgroups.annotations.Property; import org.jgroups.logging.Log; import org.jgroups.logging.LogFactory; import org.jgroups.stack.Configurator; import org.jgroups.stack.Protocol; import org.jgroups.util.Util; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.Map; /* * A class of static methods for performing commonly used functions with @Property annotations. */ public class PropertyHelper { protected static final Log log=LogFactory.getLog(PropertyHelper.class); public static String getPropertyName(Field field, Map props) throws IllegalArgumentException { if (field == null) { throw new IllegalArgumentException("Cannot get property name: field is null") ; } if (props == null) { throw new IllegalArgumentException("Cannot get property name: properties map is null") ; } Property annotation=field.getAnnotation(Property.class); if (annotation == null) { throw new IllegalArgumentException("Cannot get property name for field " + field.getName() + " which is not annotated with @Property") ; } String propertyName=field.getName(); if(props.containsKey(annotation.name())) { propertyName=annotation.name(); boolean isDeprecated=annotation.deprecatedMessage().length() > 0; if(isDeprecated && log.isWarnEnabled()) { log.warn(propertyName + " has been deprecated: " + annotation.deprecatedMessage()); } } return propertyName ; } public static String getPropertyName(Method method) throws IllegalArgumentException { if (method == null) { throw new IllegalArgumentException("Cannot get property name: field is null") ; } Property annotation=method.getAnnotation(Property.class); if (annotation == null) { throw new IllegalArgumentException("Cannot get property name for method " + method.getName() + " which is not annotated with @Property") ; } String propertyName=annotation.name().length() > 0? annotation.name() : method.getName(); propertyName=Util.methodNameToAttributeName(propertyName); return propertyName ; } public static Object getConvertedValue(Object obj, Field field, Map props, String prop, boolean check_scope) throws Exception { if (obj == null) throw new IllegalArgumentException("Cannot get converted value: Object is null") ; if (field == null) throw new IllegalArgumentException("Cannot get converted value: Field is null") ; if (props == null) throw new IllegalArgumentException("Cannot get converted value: Properties is null") ; Property annotation=field.getAnnotation(Property.class); if (annotation == null) { throw new IllegalArgumentException("Cannot get property name for field " + field.getName() + " which is not annotated with @Property") ; } String propertyName = getPropertyName(field, props) ; String name = obj instanceof Protocol? ((Protocol)obj).getName() : obj.getClass().getName(); PropertyConverter propertyConverter=(PropertyConverter)annotation.converter().newInstance(); if(propertyConverter == null) { throw new Exception("Could not find property converter for field " + propertyName + " in " + name); } Object converted = null ; try { String tmp=obj instanceof Protocol? ((Protocol)obj).getName() + "." + propertyName : propertyName; converted=propertyConverter.convert(obj, field.getType(), tmp, prop, check_scope); } catch(Exception e) { throw new Exception("Conversion of " + propertyName + " in " + name + " with original property value " + prop + " failed", e); } return converted ; } public static Object getConvertedValue(Object obj, Field field, String value, boolean check_scope) throws Exception { if(obj == null) throw new IllegalArgumentException("Cannot get converted value: Object is null"); if(field == null) throw new IllegalArgumentException("Cannot get converted value: Field is null"); Property annotation=field.getAnnotation(Property.class); if(annotation == null) { throw new IllegalArgumentException("Cannot get property name for field " + field.getName() + " which is not annotated with @Property"); } String propertyName=field.getName(); String name=obj instanceof Protocol? ((Protocol)obj).getName() : obj.getClass().getName(); PropertyConverter propertyConverter=(PropertyConverter)annotation.converter().newInstance(); if(propertyConverter == null) { throw new Exception("Could not find property converter for field " + propertyName + " in " + name); } Object converted=null; try { String tmp=obj instanceof Protocol? ((Protocol)obj).getName() + "." + propertyName : propertyName; converted=propertyConverter.convert(obj, field.getType(), tmp, value, check_scope); } catch(Exception e) { throw new Exception("Conversion of " + propertyName + " in " + name + " with original property value " + value + " failed", e); } return converted; } public static Object getConvertedValue(Object obj, Method method, Map props, String prop, boolean check_scope) throws Exception { if (obj == null) { throw new IllegalArgumentException("Cannot get converted value: Object is null") ; } if (method == null) { throw new IllegalArgumentException("Cannot get converted value: Method is null") ; } if (!Configurator.isSetPropertyMethod(method)) { throw new IllegalArgumentException("Cannot get converted value: Method is not set property method") ; } if (props == null) { throw new IllegalArgumentException("Cannot get converted value: Properties is null") ; } Property annotation=method.getAnnotation(Property.class); if (annotation == null) { throw new IllegalArgumentException("Cannot get property name for method " + method.getName() + " which is not annotated with @Property") ; } String propertyName = getPropertyName(method) ; String name = obj instanceof Protocol? ((Protocol)obj).getName() : obj.getClass().getName(); PropertyConverter propertyConverter=(PropertyConverter)annotation.converter().newInstance(); if(propertyConverter == null) { throw new Exception("Could not find property converter for method " + propertyName + " in " + name); } Object converted = null ; try { String tmp=obj instanceof Protocol? ((Protocol)obj).getName() + "." + propertyName : propertyName; converted=propertyConverter.convert(obj, method.getParameterTypes()[0], tmp, prop, check_scope); } catch(Exception e) { throw new Exception("Conversion of " + propertyName + " in " + name + " with original property value " + prop + " failed. Exception is " +e, e); } return converted ; } public static boolean usesDefaultConverter(Field field) throws IllegalArgumentException { if (field == null) { throw new IllegalArgumentException("Cannot check converter: field is null") ; } Property annotation=field.getAnnotation(Property.class); if (annotation == null) { throw new IllegalArgumentException("Cannot check converter for field " + field.getName() + " which is not annotated with @Property") ; } return annotation.converter().equals(PropertyConverters.Default.class) ; } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/conf/ProtocolConfiguration.java0000644000175000017500000001417711647260573027761 0ustar moellermoellerpackage org.jgroups.conf; import org.jgroups.logging.Log; import org.jgroups.logging.LogFactory; import org.jgroups.util.Util; import java.util.HashMap; import java.util.Iterator; import java.util.Map; /** * Parses and encapsulates the specification for 1 protocol of the protocol stack, e.g. * UNICAST(timeout=5000) * @author Bela Ban */ public class ProtocolConfiguration { private final String protocol_name; private String properties_str; private final Map properties=new HashMap(); public static final String protocol_prefix="org.jgroups.protocols"; public static final Log log=LogFactory.getLog(ProtocolConfiguration.class); /** * 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 { int index=config_str.indexOf('('); // e.g. "UDP(in_port=3333)" int end_index=config_str.lastIndexOf(')'); if(index == -1) { protocol_name=config_str; properties_str=""; } else { if(end_index == -1) { throw new Exception("Configurator.ProtocolConfiguration(): 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); } } parsePropertiesString(properties); } public ProtocolConfiguration(String protocol_name, Map properties) { this.protocol_name=protocol_name; if(!properties.isEmpty()) this.properties.putAll(properties); } public String getProtocolName() { return protocol_name; } public Map getProperties() { return properties; } public String getPropertiesString() { return properties_str; } public Map getOriginalProperties() throws Exception { Map props=new HashMap(); parsePropertiesString(props); return props; } public void substituteVariables() { for(Iterator> it=properties.entrySet().iterator(); it.hasNext();) { Map.Entry entry=it.next(); String key=entry.getKey(); String val=entry.getValue(); String tmp=Util.substituteVariable(val); if(!val.equals(tmp)) { properties.put(key, tmp); } else { if(tmp.contains("${")) { if(log.isWarnEnabled()) log.warn("variable \"" + val + "\" in " + protocol_name + " could not be substituted; " + key + " is removed from properties"); it.remove(); } } } properties_str=propertiesToString(); } public String toString() { StringBuilder retval=new StringBuilder(); if(protocol_name == null) retval.append(""); else retval.append(protocol_name); if(properties != null) retval.append("(" + Util.print(properties) + ')'); return retval.toString(); } public String propertiesToString() { return Util.printMapWithDelimiter(properties, ";"); } public String getProtocolString(boolean new_format) { return new_format? getProtocolStringNewXml() : getProtocolString(); } public String getProtocolString() { StringBuilder buf=new StringBuilder(protocol_name); if(!properties.isEmpty()) { boolean first=true; buf.append('('); for(Map.Entry entry: properties.entrySet()) { String key=entry.getKey(); String val=entry.getValue(); if(first) first=false; else buf.append(';'); buf.append(getParameterString(key, val)); } buf.append(')'); } return buf.toString(); } public String getProtocolStringNewXml() { StringBuilder buf=new StringBuilder(protocol_name + ' '); if(!properties.isEmpty()) { boolean first=true; for(Map.Entry entry: properties.entrySet()) { String key=entry.getKey(); String val=entry.getValue(); if(first) first=false; else buf.append(' '); buf.append(getParameterStringXml(key, val)); } } return buf.toString(); } protected static String getParameterString(String name, String value) { StringBuilder buf=new StringBuilder(name); if(value != null) buf.append('=').append(value); return buf.toString(); } protected static String getParameterStringXml(String name, String val) { StringBuilder buf=new StringBuilder(name); if(val != null) buf.append("=\"").append(val).append('\"'); return buf.toString(); } protected void parsePropertiesString(Map properties) throws Exception { int index=0; /* "in_port=5555;out_port=6666" */ if(properties_str.length() > 0) { String[] components=properties_str.split(";"); for(String property : components) { String name, value; index=property.indexOf('='); if(index == -1) { throw new Exception("Configurator.ProtocolConfiguration(): '=' not found in " + property + " of " + protocol_name); } name=property.substring(0, index); value=property.substring(index + 1, property.length()); properties.put(name, value); } } } }libjgroups-java-2.12.2.Final.orig/src/org/jgroups/conf/ProtocolStackConfigurator.java0000644000175000017500000000051011647260573030564 0ustar moellermoeller package org.jgroups.conf; import java.util.List; /** * @author Filip Hanik (filip@filip.net) * @author Bela Ban * @version 1.0 */ public interface ProtocolStackConfigurator { String getProtocolStackString(); List getProtocolStack(); } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/conf/XmlConfigurator.java0000644000175000017500000003330511647260573026545 0ustar moellermoeller package org.jgroups.conf; /** * Uses XML to configure a protocol stack * @author Vladimir Blagojevic */ import org.jgroups.Global; import org.jgroups.logging.Log; import org.jgroups.logging.LogFactory; import org.w3c.dom.*; import org.xml.sax.EntityResolver; import org.xml.sax.ErrorHandler; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; import org.jgroups.stack.Configurator; import org.jgroups.util.Util; 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.*; import java.util.concurrent.atomic.AtomicReference; public class XmlConfigurator implements ProtocolStackConfigurator { private static final String JAXP_SCHEMA_LANGUAGE = "http://java.sun.com/xml/jaxp/properties/schemaLanguage"; private static final String W3C_XML_SCHEMA = "http://www.w3.org/2001/XMLSchema"; private final List configuration=new ArrayList(); protected static final Log log=LogFactory.getLog(XmlConfigurator.class); protected XmlConfigurator(List protocols) { configuration.addAll(protocols); } public static XmlConfigurator getInstance(URL url) throws java.io.IOException { return getInstance(url, null); } public static XmlConfigurator getInstance(InputStream stream) throws java.io.IOException { return getInstance(stream, null); } public static XmlConfigurator getInstance(Element el) throws java.io.IOException { return parse(el); } public static XmlConfigurator getInstance(URL url, Boolean validate) throws java.io.IOException { InputStream is = url.openStream(); try { return getInstance(is, validate); } finally { try { is.close(); } catch (IOException e) { log.warn("Failed to close InputStream", e); } } } public static XmlConfigurator getInstance(InputStream stream, Boolean validate) throws java.io.IOException { return parse(stream, validate); } /** * * @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=configuration.iterator(); if(convert) buf.append("\n"); while(it.hasNext()) { ProtocolConfiguration d=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 List getProtocolStack() { return configuration; } protected static XmlConfigurator parse(InputStream stream, Boolean validate) 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(); boolean validation = false; String tmp = Util.getProperty(new String[] { Global.XML_VALIDATION }, null, null, false, null); if (tmp != null) { validation = Boolean.valueOf(tmp).booleanValue(); } else if (validate != null) { validation = validate.booleanValue(); } factory.setValidating(validation); factory.setNamespaceAware(validation); if (validation) { factory.setAttribute(JAXP_SCHEMA_LANGUAGE, W3C_XML_SCHEMA); } DocumentBuilder builder = factory.newDocumentBuilder(); builder.setEntityResolver(new EntityResolver() { public InputSource resolveEntity(String publicId, String systemId) throws IOException { if (systemId != null && systemId.startsWith("http://www.jgroups.org/schema/JGroups-")) { String schemaName = systemId.substring("http://www.jgroups.org/".length()); InputStream schemaIs = getAsInputStreamFromClassLoader(schemaName); if (schemaIs == null) { throw new IOException("Schema not found from classloader: " + schemaName); } InputSource source = new InputSource(schemaIs); source.setPublicId(publicId); source.setSystemId(systemId); return source; } return null; } }); // Use AtomicReference to allow make variable final, not for atomicity // We store only last exception final AtomicReference exceptionRef = new AtomicReference(); builder.setErrorHandler(new ErrorHandler() { public void warning(SAXParseException exception) throws SAXException { log.warn("Warning during parse", exception); } public void fatalError(SAXParseException exception) throws SAXException { log.fatal("Error during parse", exception); exceptionRef.set(exception); } public void error(SAXParseException exception) throws SAXException { log.error("Error during parse", exception); exceptionRef.set(exception); } }); Document document = builder.parse(stream); if (exceptionRef.get() != null) { throw exceptionRef.get(); } // 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; } } } private static InputStream getAsInputStreamFromClassLoader(String filename) { ClassLoader cl = Thread.currentThread().getContextClassLoader(); InputStream is = cl == null ? null : cl.getResourceAsStream(filename); if (is == null) { // check system class loader is = XmlConfigurator.class.getClassLoader().getResourceAsStream(filename); } return is; } protected static XmlConfigurator parse(Element root_element) throws java.io.IOException { XmlConfigurator configurator=null; final 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 { 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(); Map params=new HashMap(); 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(); params.put(name, value); } ProtocolConfiguration cfg=new ProtocolConfiguration(protocol, params); prot_data.add(cfg); } configurator=new XmlConfigurator(prot_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; } 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) { String cfg=inputAsString(input); List tmp=Configurator.parseConfigurations(cfg); System.out.println(dump(tmp)); } else { conf=XmlConfigurator.getInstance(input); String tmp=conf.getProtocolStackString(); System.out.println("\n" + tmp); } } else { log.error("no input file given"); } } private static String dump(Collection configs) { StringBuilder sb=new StringBuilder(); String indent=" "; sb.append("\n"); for(ProtocolConfiguration cfg: configs) { sb.append(indent).append("<").append(cfg.getProtocolName()); Map props=cfg.getProperties(); if(props.isEmpty()) { sb.append(" />\n"); } else { sb.append("\n").append(indent).append(indent); for(Map.Entry entry: props.entrySet()) { String key=entry.getKey(); String val=entry.getValue(); key=trim(key); val=trim(val); sb.append(key).append("=\"").append(val).append("\" "); } 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)"); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/conf/package.html0000644000175000017500000000011611647260573025032 0ustar moellermoeller Provides ways to configure a protocol stack. libjgroups-java-2.12.2.Final.orig/src/org/jgroups/debug/0000755000175000017500000000000011647260573022714 5ustar moellermoellerlibjgroups-java-2.12.2.Final.orig/src/org/jgroups/debug/Profiler.java0000644000175000017500000000717411647260573025352 0ustar moellermoeller package org.jgroups.debug; import org.jgroups.logging.Log; import org.jgroups.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.toString()); } } public static void setFilename(String filename) { try { if(os != null) { os.close(); } os=new FileOutputStream(filename); } catch(Exception e) { log.error(e.toString()); } } 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.toString()); } 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.toString()); } } } 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.toString()); } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/debug/ProtocolTester.java0000644000175000017500000001203311647260573026546 0ustar moellermoeller package org.jgroups.debug; import org.jgroups.Event; import org.jgroups.JChannel; import org.jgroups.Message; import org.jgroups.logging.Log; import org.jgroups.logging.LogFactory; 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(); JChannel mock_channel=new JChannel() {}; ProtocolStack stack=new ProtocolStack(mock_channel); stack.setup(Configurator.parseConfigurations(props)); stack.insertProtocol(harness, ProtocolStack.ABOVE, stack.getTopProtocol().getClass()); bottom=stack.getBottomProtocol(); // 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(); } p=harness; while(p != null) { p.destroy(); p=p.getDownProtocol(); } } else if(top != null) { p=top; List protocols=new LinkedList(); while(p != null) { protocols.add(p); p.stop(); p=p.getDownProtocol(); } p=top; while(p != null) { p.destroy(); p=p.getDownProtocol(); } } } 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; } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/debug/Simulator.java0000644000175000017500000002561311647260573025545 0ustar moellermoellerpackage org.jgroups.debug; import org.jgroups.*; import org.jgroups.protocols.TP; import org.jgroups.stack.Protocol; import org.jgroups.stack.ProtocolStack; import org.jgroups.util.DefaultTimeScheduler; import org.jgroups.util.Queue; import org.jgroups.util.QueueClosedException; import org.jgroups.util.TimeScheduler; import java.util.HashMap; import java.util.Iterator; import java.util.HashSet ; import java.util.Set ; /** * Tests one or more protocols independently. Look at org.jgroups.tests.FCTest for an example of how to use it. * @author Bela Ban */ public class Simulator { private Protocol[] protStack=null; private ProtocolAdapter ad=new ProtocolAdapter(); private 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; /** fault-injection elements */ private boolean crashFailureEnabled = false ; private boolean partitionEnabled = false ; private Set
partition = new HashSet
() ; private boolean slowProcessEnabled = false ; private long delay = 0 ; // in ms private boolean droppedMessagesEnabled = false ; private Set droppedMessages = new HashSet() ; public interface Receiver { void receive(Event evt); } public ProtocolStack getProtocolStack() { return prot_stack; } 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]; prot_stack=new ProtocolStack(); 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 Address getLocalAddress() { return this.local_addr ; } public void setView(View v) { this.view=v; } public void setReceiver(Receiver r) { this.r=r; } public Receiver getReceiver() { return this.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"); 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(); } // bottom.up(new Event(Event.SET_LOCAL_ADDRESS, local_addr)); protStack[0].down(new Event(Event.SET_LOCAL_ADDRESS, local_addr)); for(int i=0; i < protStack.length; i++) { Protocol p=protStack[i]; p.start(); } // moved event processing to follow stack init (JGRP-843) if(view != null) { Event view_evt=new Event(Event.VIEW_CHANGE, view); bottom.up(view_evt); top.down(view_evt); } send_thread = new SendThread() ; send_thread.start(); recv_thread = new ReceiveThread() ; recv_thread.start(); } public void stop() { recv_thread=null; recv_queue.close(false); send_thread=null; send_queue.close(false); if(ad != null) { ad.getTimer().stop(); } } // // fault-injection methods // /* * Simulate a crash failed process: no messages sent or received forever */ public void simulateCrashFailure() { crashFailureEnabled = true ; System.out.println("CRASH! at peer " + getLocalAddress()); } /* * Simulate a slow process. * Received messages are delayed by delay factor. */ public void simulateSlowProcess(long delay) { slowProcessEnabled = true ; this.delay = delay ; } /* * Simulate a network partition * No messages are transferred between distinct partitions * Specify the partition we belong to. */ public void simulatePartition(Address[] partition) { partitionEnabled = true ; // clear out existing elements this.partition.clear(); for (int i = 0; i < partition.length; i++) { this.partition.add(partition[i]) ; } // Object[] elements = this.partition.toArray() ; // System.out.print("<" + local_addr + ">: partition = {") ; // for(int i = 0; i < elements.length; i++) { // System.out.print(" <" + (Address) elements[i] + "> ") ; // } // System.out.println("}") ; } /* * Simulate a network partition merge. */ public void simulateMerge() { if (!partitionEnabled) return ; partitionEnabled = false ; // clear out existing elements this.partition.clear(); } /* * Simulate dropped messages by registering a callback which determines * if a message is to be dropped. */ public void registerDropMessage(DropMessage d) { if (d != null) { droppedMessagesEnabled = true ; // add the DropMessage description to the list of callbacks droppedMessages.add(d) ; } } /* * Remove the drop message rule. */ public void deRegisterDropMessage(DropMessage d) { if (d != null) { // remove the DropMessage description from the list of callbacks droppedMessages.remove(d) ; if (droppedMessages.size() == 0) { droppedMessagesEnabled = false ; } } } /* * Returns true if a message is to be dropped. */ public boolean checkForDropMessage(Message msg, Address dest) { // iterate over the set of DropMessage callbacks and // check if a message is to be dropped Address src = getLocalAddress() ; Iterator it = droppedMessages.iterator(); while (it.hasNext()) { DropMessage d =it.next(); if (d.drop(msg, dest)) return true ; } return false ; } /* * Method to determine if a message should be dropped before sending. */ public boolean senderDropFault(Message msg, Address dest) { Address a = getLocalAddress() ; // 1. crash failure - don't send messages if (crashFailureEnabled) { return true ; } // 2. partition - don't send messages to dest peers not in our partition if (partitionEnabled) { if (!partition.contains(dest)) { return true ; } } // 3. dropped messages - don't send if drop description exists if (droppedMessagesEnabled) { if (checkForDropMessage(msg, dest)) { return true ; } } return false ; } /* * Method to determine if a message should be dropped before receiving. */ public boolean receiverDropFault(Message msg, Address src) { Address a = getLocalAddress() ; // 1. crash failure - don't receive messages if (crashFailureEnabled) { return true ; } // 2. slow process - delay processing if (slowProcessEnabled) { try { Thread.sleep(delay) ; return false ; } catch(InterruptedException e) { } } // 3. partition - don't receive messages from src peers not in our partition if (partitionEnabled) { // look up message in partition table and drop if not present if (!partition.contains(src)) { return true ; } } return false ; } class ProtocolAdapter extends TP { ProtocolAdapter() { timer=new DefaultTimeScheduler(); } public boolean supportsMulticasting() { return false; } public TimeScheduler getTimer() { return timer; } public void setTimer(TimeScheduler timer) { this.timer=timer; } public String getName() { return "ProtocolAdapter"; } public void sendMulticast(byte[] data, int offset, int length) throws Exception { } public void sendUnicast(PhysicalAddress dest, byte[] data, int offset, int length) throws Exception { } public String getInfo() { return null; } protected PhysicalAddress getPhysicalAddress() { throw new UnsupportedOperationException("not implemented"); } 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; } } class SendThread extends Thread { public SendThread() { // System.out.println("send thread started") ; } public void run() { Event evt; while(send_thread != null) { try { // standard message processing evt=(Event)send_queue.remove(); if(evt.getType() == Event.MSG) { Message msg=(Message)evt.getArg(); Address dst=msg.getDest(); Address src = msg.getSrc(); // record the source address of the message if(src == null) ((Message)evt.getArg()).setSrc(local_addr); Simulator s; Address d ; if(dst == null) { for(Iterator it=addrTable.values().iterator(); it.hasNext();) { s=(Simulator)it.next(); // inject drop faults here d = s.getLocalAddress(); if (!senderDropFault(msg, d)) { s.receive(evt); } } } else { s=addrTable.get(dst); if(s != null) { // inject drop faults here if (!senderDropFault(msg,dst)) { s.receive(evt); } } } } } catch(QueueClosedException e) { send_thread=null; break; } } } } class ReceiveThread extends Thread { ReceiveThread() { // System.out.println("receive thread started") ; } public void run() { Event evt; while(recv_thread != null) { try { evt=(Event)recv_queue.remove(); Message msg=(Message)evt.getArg(); Address dst=msg.getDest(); Address src=msg.getSrc(); // inject faults here if (!receiverDropFault(msg, src)) { bottom.up(evt); } } catch(QueueClosedException e) { recv_thread=null; break; } } } } /** * Interface for a class which determines if a message should be * dropped or not. Describes messages to be dropped. */ public interface DropMessage { public boolean drop(Message msg, Address dest) ; } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/debug/package.html0000644000175000017500000000020111647260573025166 0ustar moellermoeller Provides debug support, including testing, profiling, and a graphical view of a protocol stack. libjgroups-java-2.12.2.Final.orig/src/org/jgroups/demos/0000755000175000017500000000000011647260573022735 5ustar moellermoellerlibjgroups-java-2.12.2.Final.orig/src/org/jgroups/demos/CausalDemo.java0000644000175000017500000001220111647260573025611 0ustar moellermoellerpackage org.jgroups.demos; import org.jgroups.*; import org.jgroups.logging.Log; import org.jgroups.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", 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.getAddress())) { int nextTarget = r.nextInt(members.size()); //chose someone other than yourself while (nextTarget == members.indexOf(channel.getAddress())) { 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.toString()); } } } 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 + ']'; } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/demos/DistributedQueueDemo.java0000644000175000017500000001712311647260573027700 0ustar moellermoellerpackage 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]"); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/demos/DistributedTreeDemo.java0000644000175000017500000003034211647260573027511 0ustar moellermoeller package org.jgroups.demos; import org.jgroups.blocks.DistributedTree; 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.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.TreeNode; import javax.swing.tree.TreePath; import java.awt.*; import java.awt.event.*; import java.io.Serializable; import java.util.Enumeration; import java.util.Properties; import java.util.StringTokenizer; import java.util.Vector; class MyNode extends DefaultMutableTreeNode { String name=""; MyNode(String name) { this.name=name; } MyNode(String name, Serializable user_obj) { super(user_obj); this.name=name; } void add(String fqn) { add(fqn, null); } public void add(String fqn, Serializable user_obj) { MyNode curr, n; StringTokenizer tok; String child_name; if(fqn == null) return; curr=this; tok=new StringTokenizer(fqn, "/"); while(tok.hasMoreTokens()) { child_name=tok.nextToken(); n=curr.findChild(child_name); if(n == null) { n=new MyNode(child_name, user_obj); curr.add(n); } curr=n; } curr.userObject=user_obj; } void modify(String fqn, Serializable new_element) { if(fqn == null || new_element == null) return; MyNode n=findNode(fqn); if(n != null) n.userObject=new_element; } void remove(String fqn) { System.out.println("MyNode.remove(" + fqn + ')'); removeFromParent(); } public MyNode findNode(String fqn) { MyNode curr, n; StringTokenizer tok; String child_name; if(fqn == null) return null; curr=this; tok=new StringTokenizer(fqn, "/"); 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) { System.err.println("MyNode.findChild(" + relative_name + "): child.name is 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('/' + 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 { // we need state transfer here // props="UDP:PING:FD:STABLE:NAKACK:UNICAST:FRAG:FLUSH:GMS:VIEW_ENFORCER:STATE_TRANSFER:QUEUE"; // test for pbcast 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); } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/demos/Draw.java0000644000175000017500000004773111647260573024511 0ustar moellermoeller 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.util.List; 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; private boolean use_state=false; private long state_timeout=5000; private boolean use_unicasts=false; private final List

members=new ArrayList
(); public Draw(String props, boolean no_channel, boolean jmx, boolean use_state, long state_timeout, boolean use_blocking, boolean use_unicasts, String name) throws Exception { this.no_channel=no_channel; this.jmx=jmx; this.use_state=use_state; this.state_timeout=state_timeout; this.use_unicasts=use_unicasts; if(no_channel) return; channel=new JChannel(props); if(name != null) channel.setName(name); if(use_blocking) channel.setOpt(Channel.BLOCK, Boolean.TRUE); channel.setReceiver(this); channel.addChannelListener(this); } public Draw(Channel channel) throws Exception { this.channel=channel; channel.setReceiver(this); channel.addChannelListener(this); } public Draw(Channel channel, boolean use_state, long state_timeout) throws Exception { this.channel=channel; 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; boolean use_unicasts=false; String name=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("-no_channel".equals(args[i])) { no_channel=true; continue; } if("-jmx".equals(args[i])) { jmx=Boolean.parseBoolean(args[++i]); 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; } if("-use_unicasts".equals(args[i])) { use_unicasts=true; continue; } if("-name".equals(args[i])) { name=args[++i]; continue; } help(); return; } try { draw=new Draw(props, no_channel, jmx, use_state, state_timeout, use_blocking, use_unicasts, name); if(group_name != null) draw.setGroupName(group_name); draw.go(); } catch(Throwable e) { System.err.println("fatal error: " + e.getLocalizedMessage() + ", cause: "); Throwable t=e.getCause(); if(t != null) t.printStackTrace(System.err); System.exit(0); } } static void help() { System.out.println("\nDraw [-help] [-no_channel] [-props ]" + " [-groupname ] [-state] [-use_blocking] [-timeout ] [-use_unicasts] " + "[-bind_addr ] [-jmx ] [-name ]"); 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); } private void sendToAll(byte[] buf) throws Exception { for(Address mbr: members) { Message msg=new Message(mbr, null, buf); channel.send(msg); } } public void go() throws Exception { if(!no_channel && !use_state) { channel.connect(groupname); } 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.getAddress() != null) tmp+=channel.getAddress(); 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.getAddress() + "] 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) { member_size=v.size(); if(mainFrame != null) setTitle(); members.clear(); members.addAll(v.getMembers()); if(v instanceof MergeView) { System.out.println("** MergeView=" + v); // This is an example of a simple merge function, which fetches the state from the coordinator // on a merge and overwrites all of its own state if(use_state && !members.isEmpty()) { Address coord=members.get(0); Address local_addr=channel.getAddress(); if(local_addr != null && !local_addr.equals(coord)) { try { System.out.println("fetching state from " + coord); channel.getState(coord, 5000); } catch(Exception e) { e.printStackTrace(); } } } } else System.out.println("** View=" + v); } 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); if(use_unicasts) sendToAll(buf); else 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) { if(jmx) { Util.registerChannel((JChannel)channel, "jgroups"); } } public void channelDisconnected(Channel channel) { if(jmx) { MBeanServer server=Util.getMBeanServer(); if(server != null) { try { JmxConfigurator.unregisterChannel((JChannel)channel,server, groupname); } catch(Exception e) { e.printStackTrace(); } } } } public void channelClosed(Channel channel) { } public void channelShunned() { } public void channelReconnected(Address addr) { } /* --------------------------- 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; } @SuppressWarnings("unchecked") 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); if(use_unicasts) sendToAll(buf); else channel.send(new Message(null, null, buf)); } 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); } } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/demos/Draw2Channels.java0000644000175000017500000002557611647260573026252 0ustar moellermoeller 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.xml"; 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.getAddress() != null) title+=control_channel.getAddress(); 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() { } public void channelReconnected(Address addr) { } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/demos/DrawCommand.java0000644000175000017500000000317211647260573025777 0ustar moellermoeller 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(); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/demos/DrawRepl.java0000644000175000017500000002176611647260573025334 0ustar moellermoeller 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"); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/demos/ExecutionServiceDemo.java0000644000175000017500000003076611647260573027705 0ustar moellermoellerpackage org.jgroups.demos; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.util.ArrayDeque; import java.util.Arrays; import java.util.Queue; import java.util.Random; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import org.jgroups.JChannel; import org.jgroups.blocks.executor.ExecutionCompletionService; import org.jgroups.blocks.executor.ExecutionRunner; import org.jgroups.blocks.executor.ExecutionService; import org.jgroups.jmx.JmxConfigurator; import org.jgroups.util.Streamable; import org.jgroups.util.Util; public class ExecutionServiceDemo { protected String props; protected JChannel ch; protected ExecutionService execution_service; protected String name; protected ExecutionRunner runner; protected int size; protected boolean printValues; protected Random random; protected ExecutorService executor; protected Queue> queue; public ExecutionServiceDemo(String props, String name, int size) { this.props=props; this.name=name; queue=new ArrayDeque>(); executor = Executors.newCachedThreadPool(new ThreadFactory() { @Override public Thread newThread(Runnable r) { Thread thread = new Thread(r, "Consumer-" + poolNumber.getAndIncrement()); thread.setDaemon(true); return thread; } AtomicInteger poolNumber = new AtomicInteger(); }); this.size=size; } public static void main(String[] args) throws Exception { String props=null; String name=null; String size="1000"; for(int i=0; i < args.length; i++) { if(args[i].equals("-props")) { props=args[++i]; continue; } if(args[i].equals("-name")) { name=args[++i]; continue; } help(); return; } ExecutionServiceDemo demo=new ExecutionServiceDemo(props, name, Integer.valueOf(size)); demo.start(); } protected static class ByteBufferStreamable implements Streamable { protected ByteBuffer buffer; public ByteBufferStreamable() { } protected ByteBufferStreamable(ByteBuffer buffer) { this.buffer = buffer; } @Override public void writeTo(DataOutputStream out) throws IOException { int size = buffer.limit() - buffer.position(); out.writeInt(size); out.write(buffer.array(), buffer.position(), size); } @Override public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { buffer = ByteBuffer.allocate(in.readInt()); in.readFully(buffer.array()); } } public void start() throws Exception { ch=new JChannel(props); if(name != null) ch.setName(name); execution_service=new ExecutionService(ch); runner=new ExecutionRunner(ch); ch.connect("executing-cluster"); JmxConfigurator.registerChannel(ch, Util.getMBeanServer(), "execution-service", ch.getClusterName(), true); // Start a consumer queue.add(executor.submit(runner)); random = new Random(); printValues = false; try { loop(); } catch(Exception e) { e.printStackTrace(); } finally { Util.close(ch); } } public static class SortingByteCallable implements Callable, Streamable { public SortingByteCallable() { } public SortingByteCallable(byte[] bytes, int offset, int size) { buffer = ByteBuffer.wrap(bytes, offset, size); } @Override public ByteBufferStreamable call() throws Exception { Arrays.sort(buffer.array(), buffer.position(), buffer.limit()); return new ByteBufferStreamable(buffer); } protected ByteBuffer buffer; // We copy over as a single array with no offset @Override public void writeTo(DataOutputStream out) throws IOException { Util.writeStreamable(new ByteBufferStreamable(buffer), out); } @Override public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { buffer = ((ByteBufferStreamable)Util.readStreamable( ByteBufferStreamable.class, in)).buffer; } } /** * Sorts 2 byte arrys into a larger byte array * * @author wburns */ public static class SortingTwoByteCallable implements Callable, Streamable { protected ByteBuffer bytes1; protected ByteBuffer bytes2; public SortingTwoByteCallable() { } public SortingTwoByteCallable(ByteBufferStreamable bytes1, ByteBufferStreamable bytes2) { this.bytes1=bytes1.buffer; this.bytes2=bytes2.buffer; } @Override public ByteBufferStreamable call() throws Exception { ByteBuffer results = ByteBuffer.allocate(bytes1.remaining() + bytes2.remaining()); int i = bytes1.position(); int j = bytes2.position(); byte[] byteArray1 = bytes1.array(); byte[] byteArray2 = bytes2.array(); int byte1Max = bytes1.limit(); int byte2Max = bytes2.limit(); while (i < byte1Max && j < byte2Max) { if (byteArray1[i] < byteArray2[j]) { results.put(byteArray1[i++]); } else { results.put(byteArray2[j++]); } } if (i < byte1Max) { results.put(byteArray1, i, byte1Max - i); } else if (j < byte2Max) { results.put(byteArray2, j, byte2Max - j); } results.flip(); return new ByteBufferStreamable(results); } @Override public void writeTo(DataOutputStream out) throws IOException { Util.writeStreamable(new ByteBufferStreamable(bytes1), out); Util.writeStreamable(new ByteBufferStreamable(bytes2), out); } @Override public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { bytes1 = ((ByteBufferStreamable)Util.readStreamable( ByteBufferStreamable.class, in)).buffer; bytes2 = ((ByteBufferStreamable)Util.readStreamable( ByteBufferStreamable.class, in)).buffer; } } protected void loop() throws Exception { while(ch.isConnected()) { String line=Util.readStringFromStdin(": "); if(line.startsWith("quit") || line.startsWith("exit")) break; if(line.startsWith("submit")) { int randomNumbers = Integer.parseInt(line.substring("submit".length()).trim()); // Parse numbers and break into parts byte[] numbers = new byte[randomNumbers]; for (int i = 0; i < randomNumbers; ++i) { numbers[i] = (byte)random.nextInt(256); } if (printValues) System.out.println("Original Numbers: " + Arrays.toString(numbers)); ExecutionCompletionService completion = new ExecutionCompletionService(execution_service); long beginDistributed = System.nanoTime(); int chunks = numbers.length / size; for (int i = 0; i < chunks; ++i) { completion.submit(new SortingByteCallable(numbers, size * i, size)); } int futureNumber = chunks; int leftOver = numbers.length % size; if (leftOver != 0) { completion.submit(new SortingByteCallable(numbers, numbers.length - leftOver, leftOver)); futureNumber++; } Future finalValue; if (futureNumber > 1) { Future result = null; while (true) { result = completion.take(); if (--futureNumber >= 1) { Future result2 = completion.take(); completion.submit(new SortingTwoByteCallable(result.get(), result2.get())); } else { break; } } finalValue = result; } else { finalValue = completion.take(); } ByteBufferStreamable results = finalValue.get(); long totalDistributed = System.nanoTime() - beginDistributed; if (printValues) { System.out.println("Sorted values: " + Arrays.toString( results.buffer.array())); } System.out.println("Distributed Sort Took: " + Util.printTime(totalDistributed, TimeUnit.NANOSECONDS)); long beginLocal = System.nanoTime(); Arrays.sort(numbers); System.out.println(" Local Sort Took: " + Util.printTime((System.nanoTime() - beginLocal), TimeUnit.NANOSECONDS)); } else if(line.startsWith("consumer")) { // Parse stop start and add or remove if (line.contains("start")) { queue.add(executor.submit(runner)); System.out.println("Started Consumer - running " + queue.size() + " consumers"); } else if (line.contains("stop")) { queue.remove().cancel(true); System.out.println("Stopped Consumer - running " + queue.size() + " consumers"); } else { System.out.println("Consumers Running Locally: " + queue.size()); } } else if(line.startsWith("size")) { String thresholdSize = line.substring("size".length()).trim(); if (thresholdSize.length() > 0) { int size = Integer.parseInt(thresholdSize); this.size = size; System.out.println("Changed sort threshold size to " + size); } else { System.out.println("Threshold Size: " + size); } } else if(line.startsWith("print")) { printValues = !printValues; System.out.println("Print Arrays: " + printValues); } else if(line.startsWith("view")) System.out.println("View: " + ch.getView()); else if(line.startsWith("help")) help(); } } protected static void help() { System.out.println("\nExecutionServiceDemo [-props properties] [-name name]\n" + "Default Values:\n\n" + "One Consumer\n" + "Threshold size: 1000\n" + "Print disabled\n\n" + "Valid commands:\n\n" + "submit (amount of numbers to generate)\n" + "consumer (start) | (stop)\n" + "size (value)\n" + "print"); System.out.println("\nExample:\nsubmit 2000000\nconsumer start\nconsumer stop\nsize 1000000\nprint"); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/demos/JmxDemo.java0000644000175000017500000000624411647260573025151 0ustar moellermoellerpackage org.jgroups.demos; import org.jgroups.annotations.MBean; import org.jgroups.annotations.ManagedAttribute; import org.jgroups.annotations.ManagedOperation; import org.jgroups.jmx.JmxConfigurator; import org.jgroups.util.Util; import javax.management.MBeanServer; /** * Shows how annotations can be used to expose attributes and operations * @author Bela Ban */ @MBean public class JmxDemo { @ManagedAttribute private int age; // exposed as read-only 'age' @ManagedAttribute private static final String last_name="Ban"; // read-only (because final) 'last_name' @ManagedAttribute private static final String first_name="Bela"; // read-only (final) 'first_name' @ManagedAttribute(description="social security number") // read-only private static final long id=322649L; public void foo() { // must be exposed because we have @MBean on the class System.out.println("foo(" + number + "): age=" + age + ", name=" + first_name + " " + last_name); } @ManagedAttribute private int number=10; // writeable because we have the (non-annnotated) setter below !! @ManagedAttribute public void setNumber(int num) {number=num;} @ManagedAttribute(name="NumberAsString") public String getNumberAsString() { return String.valueOf(number); } @ManagedAttribute public static int getMyFoo() {return 22;} // exposed as 'myFoo' *not* 'getMyFoo()' !! @ManagedAttribute(writable=true) private static int my_other_number_is_here=999; // exposed as writable myOtherNumberIsHere @ManagedAttribute private int other_number=20; // exposed as 'otherNumber' ? @ManagedAttribute public void setOtherNumber(int num) {other_number=num;} @ManagedAttribute public void foobar() {} // doesn't start with setXXX() or getXXX(), ignored @ManagedAttribute public static boolean isFlag() {return true;} // exposed as Flag, *not* 'isFlag()' !! @ManagedAttribute(description="my number attribute") private long my_number=322649L; @ManagedAttribute public void setMyNumber(long new_number) { my_number=new_number; } private int accountNumber=10; @ManagedAttribute public void setAccountNumber(int num) {accountNumber=num;} // exposes accountNumber as writable @ManagedAttribute public int getAccountNumber() {return accountNumber;} int max_age=100; @ManagedAttribute public void setMaxAge(int age) {max_age=age;} @ManagedAttribute public int getMaxAge() {return max_age;} @ManagedOperation public String sayName() { return "I'm " + first_name + " " + last_name; } public int add(int a, int b) {return a+b;} // exposed because @MBean is on the class public static void main(String[] args) { JmxDemo demo=new JmxDemo(); MBeanServer server=Util.getMBeanServer(); if(server != null) { try { JmxConfigurator.register(demo, server, "demo:name=DemoObject"); while(true) { Util.sleep(10000); } } catch(Exception e) { e.printStackTrace(); } } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/demos/KeyStoreGenerator.java0000644000175000017500000000772011647260573027222 0ustar moellermoeller package org.jgroups.demos; import org.jgroups.util.Util; 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="AES"; static int keySize=128; static String keyStoreName="defaultStore.keystore"; static String storePass="changeit"; static String alias="myKey"; public static void main(String[] args) { int i=0; String arg=null; while(i < args.length && args[i].startsWith("-")) { arg=args[i++]; System.out.println("Found arg of " + arg); if(arg.equalsIgnoreCase("--alg")) { if(i < args.length) { symAlg=args[i++]; } else { System.out.println("No Algorithm supplied using default of " + symAlg); } } else if(arg.equalsIgnoreCase("--size")) { if(i < args.length) { keySize=Integer.parseInt(args[i++]); } else { System.out.println("No Size supplied using default of " + keySize); } } else if(arg.equalsIgnoreCase("--storeName")) { if(i < args.length) { keyStoreName=args[i++]; } else { System.out.println("No keystore supplied using default of " + keyStoreName); } } else if(arg.equalsIgnoreCase("--storePass")) { if(i < args.length) { storePass=args[i++]; } else { System.out.println("No password supplied using default of " + storePass); } } else if(arg.equalsIgnoreCase("--alias")) { if(i < args.length) { alias=args[i++]; } else { System.out.println("No alias supplied using default of " + alias); } } } System.out.println("Creating file '" + keyStoreName + "' using Algorithm '" + symAlg + "' size '" + keySize + "'"); OutputStream stream=null; try { stream=new FileOutputStream(keyStoreName); SecretKey key=initSymKey(); KeyStore store=KeyStore.getInstance("JCEKS"); store.load(null, null); store.setKeyEntry(alias, key, storePass.toCharArray(), null); store.store(stream, storePass.toCharArray()); } catch(Exception e) { e.printStackTrace(); } finally { try { Util.close(stream); } catch(Exception e) { } } System.out.println("Finished keystore creation"); } public static SecretKey initSymKey() throws Exception { KeyGenerator keyGen=KeyGenerator.getInstance(getAlgorithm(symAlg)); keyGen.init(keySize); return keyGen.generateKey(); } private static String getAlgorithm(String s) { int index=s.indexOf("/"); if(index == -1) return s; return s.substring(0, index); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/demos/LockServiceDemo.java0000644000175000017500000001321511647260573026620 0ustar moellermoellerpackage org.jgroups.demos; import java.util.ArrayList; import java.util.List; import java.util.StringTokenizer; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; import org.jgroups.JChannel; import org.jgroups.blocks.locking.LockNotification; import org.jgroups.blocks.locking.LockService; import org.jgroups.blocks.locking.Owner; import org.jgroups.jmx.JmxConfigurator; import org.jgroups.util.Util; /** * Demos the LockService */ public class LockServiceDemo implements LockNotification { protected String props; protected JChannel ch; protected LockService lock_service; protected String name; public LockServiceDemo(String props, String name) { this.props=props; this.name=name; } public void start() throws Exception { ch=new JChannel(props); if(name != null) ch.setName(name); lock_service=new LockService(ch); lock_service.addLockListener(this); ch.connect("lock-cluster"); JmxConfigurator.registerChannel(ch, Util.getMBeanServer(), "lock-service", ch.getClusterName(), true); try { loop(); } catch(Exception e) { e.printStackTrace(); } finally { Util.close(ch); } } public void lockCreated(String name) { } public void lockDeleted(String name) { } public void locked(String lock_name, Owner owner) { System.out.println("\"" + lock_name + "\" locked by " + owner); } public void unlocked(String lock_name, Owner owner) { System.out.println("\"" + lock_name + "\" unlocked by " + owner); } public void awaiting(String lock_name, Owner owner) { System.out.println("awaiting \"" + lock_name + "\" by " + owner); } public void awaited(String lock_name, Owner owner) { System.out.println("awaited \"" + lock_name + "\" by " + owner); } protected void loop() throws Exception { List lock_names; while(ch.isConnected()) { String line=Util.readStringFromStdin(": "); if(line.startsWith("quit") || line.startsWith("exit")) break; if(line.startsWith("lock")) { lock_names=parseLockNames(line.substring("lock".length()).trim()); for(String lock_name: lock_names) { Lock lock=lock_service.getLock(lock_name); lock.lock(); } } else if(line.startsWith("trylock")) { lock_names=parseLockNames(line.substring("trylock".length()).trim()); String tmp=lock_names.get(lock_names.size() -1); Long timeout=new Long(-1); try { timeout=Long.parseLong(tmp); lock_names.remove(lock_names.size() -1); } catch(NumberFormatException e) { } for(String lock_name: lock_names) { Lock lock=lock_service.getLock(lock_name); boolean rc; if(timeout.longValue() < 0) rc=lock.tryLock(); else rc=lock.tryLock(timeout.longValue(), TimeUnit.MILLISECONDS); if(!rc) System.err.println("Failed locking \"" + lock_name + "\""); } } else if(line.startsWith("unlock")) { lock_names=parseLockNames(line.substring("unlock".length()).trim()); for(String lock_name: lock_names) { if(lock_name.equalsIgnoreCase("all")) { lock_service.unlockAll(); break; } else { Lock lock=lock_service.getLock(lock_name); if(lock != null) lock.unlock(); } } } else if(line.startsWith("view")) System.out.println("View: " + ch.getView()); else if(line.startsWith("help")) help(); printLocks(); } } protected static List parseLockNames(String line) { List lock_names=new ArrayList(); if(line == null || line.length() == 0) return lock_names; StringTokenizer tokenizer=new StringTokenizer(line); while(tokenizer.hasMoreTokens()) lock_names.add(tokenizer.nextToken()); return lock_names; } protected void printLocks() { System.out.println("\n" + lock_service.printLocks()); } public static void main(String[] args) throws Exception { String props=null; String name=null; for(int i=0; i < args.length; i++) { if(args[i].equals("-props")) { props=args[++i]; continue; } if(args[i].equals("-name")) { name=args[++i]; continue; } help(); return; } LockServiceDemo demo=new LockServiceDemo(props, name); demo.start(); } protected static void help() { System.out.println("\nLockServiceDemo [-props properties] [-name name]\n" + "Valid commands:\n\n" + "lock ()+\n" + "unlock ( | \"ALL\")+\n" + "trylock ()+ []\n"); System.out.println("Example:\nlock lock lock2 lock3\nunlock all\ntrylock bela michelle 300"); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/demos/MemcachedServer.java0000644000175000017500000001365211647260573026644 0ustar moellermoellerpackage org.jgroups.demos; import org.jgroups.blocks.Cache; import org.jgroups.blocks.MemcachedConnector; import org.jgroups.blocks.PartitionedHashMap; import org.jgroups.jmx.JmxConfigurator; import javax.management.MBeanServer; import java.net.InetAddress; import java.lang.management.ManagementFactory; /** Server process which listens for memcached requests and forwards them to an instance of PartitionedHashMap. * Uses MemcachedConnector and PartitionedHashMap. * @author Bela Ban */ public class MemcachedServer { private MemcachedConnector connector; private PartitionedHashMap cache; private static final String BASENAME="memcached"; private void start(String props, InetAddress bind_addr, int port, int min_threads, int max_threads, long rpc_timeout, long caching_time, boolean migrate_data, boolean use_l1_cache, int l1_max_entries, long l1_reaping_interval, int l2_max_entries, long l2_reaping_interval) throws Exception { MBeanServer server=ManagementFactory.getPlatformMBeanServer(); connector=new MemcachedConnector(bind_addr, port, null); connector.setThreadPoolCoreThreads(min_threads); connector.setThreadPoolMaxThreads(max_threads); JmxConfigurator.register(connector, server, BASENAME + ":name=connector"); cache=new PartitionedHashMap(props, "memcached-cluster"); cache.setCallTimeout(rpc_timeout); cache.setCachingTime(caching_time); cache.setMigrateData(migrate_data); JmxConfigurator.register(cache, server, BASENAME + ":name=cache"); JmxConfigurator.register(cache.getL2Cache(), server, BASENAME + ":name=l2-cache"); if(use_l1_cache) { Cache l1_cache=new Cache(); cache.setL1Cache(l1_cache); if(l1_reaping_interval > 0) l1_cache.enableReaping(l1_reaping_interval); if(l1_max_entries > 0) l1_cache.setMaxNumberOfEntries(l1_max_entries); JmxConfigurator.register(cache.getL1Cache(), server, BASENAME + ":name=l1-cache"); } if(l2_max_entries > 0 || l2_reaping_interval > 0) { Cache l2_cache=cache.getL2Cache(); if(l2_max_entries > 0) l2_cache.setMaxNumberOfEntries(l2_max_entries); if(l2_reaping_interval > 0) l2_cache.enableReaping(l2_reaping_interval); } connector.setCache(cache); Runtime.getRuntime().addShutdownHook(new Thread() { public void run() { cache.stop(); try {connector.stop();} catch(Exception e) {} } }); cache.start(); connector.start(); } public static void main(String[] args) throws Exception { InetAddress bind_addr=null; int port=11211; String props="udp.xml"; int min_threads=1, max_threads=500; long rpc_timeout=1500L, caching_time=30000L; boolean migrate_data=true, use_l1_cache=true; int l1_max_entries=5000, l2_max_entries=-1; long l1_reaping_interval=-1, l2_reaping_interval=30000L; for(int i=0; i < args.length; i++) { if(args[i].equals("-bind_addr")) { bind_addr=InetAddress.getByName(args[++i]); continue; } if(args[i].equals("-port") || args[i].equals("-p")) { port=Integer.parseInt(args[++i]); continue; } if(args[i].equals("-props")) { props=args[++i]; continue; } if(args[i].equals("-min_threads")) { min_threads=Integer.parseInt(args[++i]); continue; } if(args[i].equals("-max_threads")) { max_threads=Integer.parseInt(args[++i]); continue; } if(args[i].equals("-rpc_timeout")) { rpc_timeout=Long.parseLong(args[++i]); continue; } if(args[i].equals("-caching_time")) { caching_time=Long.parseLong(args[++i]); continue; } if(args[i].equals("-migrate_data")) { migrate_data=Boolean.parseBoolean(args[++i]); continue; } if(args[i].equals("-use_l1_cache")) { use_l1_cache=Boolean.parseBoolean(args[++i]); continue; } if(args[i].equals("-l1_max_entries")) { l1_max_entries=Integer.parseInt(args[++i]); continue; } if(args[i].equals("-l1_reaping_interval")) { l1_reaping_interval=Long.parseLong(args[++i]); continue; } if(args[i].equals("-l2_max_entries")) { l2_max_entries=Integer.parseInt(args[++i]); continue; } if(args[i].equals("-l2_reaping_interval")) { l2_reaping_interval=Long.parseLong(args[++i]); continue; } help(); return; } new MemcachedServer().start(props, bind_addr, port, min_threads, max_threads, rpc_timeout, caching_time, migrate_data, use_l1_cache, l1_max_entries, l1_reaping_interval, l2_max_entries, l2_reaping_interval); } private static void help() { System.out.println("MemcachedServer [-help] [-bind_addr
] [-port ] [-props ] " + "[-min_threads ] [-max_threads ] [-rpc_timeout ] [-caching_time ] " + "[-migrate_data ] [-use_l1_cache ] " + "[-l1_max_entries ] [-l1_reaping_interval ] " + "[-l2_max_entries ] [-l2_reaping_interval ] "); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/demos/NotificationBusDemo.java0000644000175000017500000000623211647260573027510 0ustar moellermoeller package org.jgroups.demos; import org.jgroups.logging.Log; import org.jgroups.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.toString()); } } } catch(Exception ex) { log.error(ex.toString()); } 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); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/demos/PartitionedHashMapDemo.java0000644000175000017500000000557511647260573030145 0ustar moellermoellerpackage org.jgroups.demos; import org.jgroups.blocks.PartitionedHashMap; import org.jgroups.blocks.Cache; import org.jgroups.util.Util; import java.io.BufferedReader; import java.io.InputStreamReader; /** * @author Bela Ban */ public class PartitionedHashMapDemo { public static void main(String[] args) throws Exception { String props="udp.xml"; boolean migrate_data=false; for(int i=0; i < args.length; i++) { if(args[i].equals("-props")) { props=args[++i]; continue; } if(args[i].equals("-migrate_data")) { migrate_data=true; continue; } help(); return; } PartitionedHashMap map=new PartitionedHashMap(props, "demo-cluster"); Cache l1_cache=new Cache(); l1_cache.setMaxNumberOfEntries(5); l1_cache.disableReaping(); map.setL1Cache(l1_cache); Cache l2_cache=map.getL2Cache(); l2_cache.enableReaping(10000); map.setMigrateData(migrate_data); map.start(); while(true) { int ch=Util.keyPress("[1] put [2] get [3] remove [4] print [q] quit"); switch(ch) { case '1': String key=readLine("key: "); String val=readLine("val: "); String caching_time=readLine("ttl: "); map.put(key, val, Long.parseLong(caching_time)); break; case '2': key=readLine("key: "); val=map.get(key); System.out.println("val = " + val); break; case '3': key=readLine("key: "); map.remove(key); break; case '4': System.out.println("address: " + map.getLocalAddress()); System.out.println("L1 cache:\n" + map.getL1Cache()); System.out.println("L2 cache:\n" + map.getL2Cache()); break; case 'q': l1_cache.stop(); map.stop(); return; } } } private static void help() { System.out.println("PartitionedHashMapDemo [-props ] [-migrate_data]"); } static String readLine(String msg) { BufferedReader reader=null; String tmp=null; try { System.out.print(msg); System.out.flush(); System.in.skip(System.in.available()); reader=new BufferedReader(new InputStreamReader(System.in)); tmp=reader.readLine().trim(); return tmp; } catch(Exception e) { return null; } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/demos/ProgrammaticChat.java0000644000175000017500000000355211647260573027032 0ustar moellermoellerpackage org.jgroups.demos; import org.jgroups.JChannel; import org.jgroups.Message; import org.jgroups.ReceiverAdapter; import org.jgroups.View; import org.jgroups.protocols.*; import org.jgroups.protocols.pbcast.GMS; import org.jgroups.protocols.pbcast.NAKACK; import org.jgroups.protocols.pbcast.STABLE; import org.jgroups.stack.ProtocolStack; import org.jgroups.util.Util; import java.net.InetAddress; /** * @author Bela Ban */ public class ProgrammaticChat { public static void main(String[] args) throws Exception { JChannel ch=new JChannel(false); ProtocolStack stack=new ProtocolStack(); ch.setProtocolStack(stack); stack.addProtocol(new UDP().setValue("bind_addr", InetAddress.getByName("192.168.1.5"))) .addProtocol(new PING()) .addProtocol(new MERGE2()) .addProtocol(new FD_SOCK()) .addProtocol(new FD_ALL().setValue("timeout", 12000).setValue("interval", 3000)) .addProtocol(new VERIFY_SUSPECT()) .addProtocol(new BARRIER()) .addProtocol(new NAKACK()) .addProtocol(new UNICAST2()) .addProtocol(new STABLE()) .addProtocol(new GMS()) .addProtocol(new UFC()) .addProtocol(new MFC()) .addProtocol(new FRAG2()); stack.init(); ch.setReceiver(new ReceiverAdapter() { public void viewAccepted(View new_view) { System.out.println("view: " + new_view); } public void receive(Message msg) { System.out.println("<< " + msg.getObject() + " [" + msg.getSrc() + "]"); } }); ch.connect("ChatCluster"); for(;;) { String line=Util.readStringFromStdin(": "); ch.send(null, null, line); } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/demos/QuoteClient.java0000644000175000017500000002116011647260573026034 0ustar moellermoeller 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; 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 Class[]{String.class}, 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", 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(); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/demos/QuoteServer.java0000644000175000017500000000741211647260573026070 0ustar moellermoeller package org.jgroups.demos; import org.jgroups.*; import org.jgroups.blocks.RpcDispatcher; import org.jgroups.util.Util; import org.jgroups.logging.Log; import org.jgroups.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(); } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/demos/RelayDemo.java0000644000175000017500000000462311647260573025466 0ustar moellermoellerpackage org.jgroups.demos; import org.jgroups.*; import org.jgroups.util.Util; /** Demos RELAY. Create 2 *separate* clusters with RELAY as top protocol. Each RELAY has bridge_props="tcp.xml" (tcp.xml * needs to be present). Then start 2 instances in the first cluster and 2 instances in the second cluster. They should * find each other, and typing in a window should send the text to everyone, plus we should get 4 responses. * @author Bela Ban */ public class RelayDemo { public static void main(String[] args) throws Exception { String props="udp.xml"; String name=null; for(int i=0; i < args.length; i++) { if(args[i].equals("-props")) { props=args[++i]; continue; } if(args[i].equals("-name")) { name=args[++i]; continue; } System.out.println("RelayDemo [-props props] [-name name]"); return; } final JChannel ch=new JChannel(props); if(name != null) ch.setName(name); ch.setReceiver(new ReceiverAdapter() { public void receive(Message msg) { Address sender=msg.getSrc(); System.out.println("<< " + msg.getObject() + " from " + sender); Address dst=msg.getDest(); if(dst == null || dst.isMulticastAddress()) { Message rsp=new Message(msg.getSrc(), null, "this is a response"); try { ch.send(rsp); } catch(Exception e) { e.printStackTrace(); } } } public void viewAccepted(View new_view) { System.out.println(print(new_view)); } }); ch.connect("RelayDemo"); for(;;) { String line=Util.readStringFromStdin(": "); ch.send(null, null, line); } } static String print(View view) { StringBuilder sb=new StringBuilder(); boolean first=true; sb.append(view.getClass().getSimpleName() + ": ").append(view.getViewId()).append(": "); for(Address mbr: view.getMembers()) { if(first) first=false; else sb.append(", "); sb.append(mbr); } return sb.toString(); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/demos/RelayDemoRpc.java0000644000175000017500000000674511647260573026142 0ustar moellermoellerpackage org.jgroups.demos; import org.jgroups.*; import org.jgroups.blocks.MethodCall; import org.jgroups.blocks.Request; import org.jgroups.blocks.RequestOptions; import org.jgroups.blocks.RpcDispatcher; import org.jgroups.util.Rsp; import org.jgroups.util.RspList; import org.jgroups.util.Util; /** Demos RELAY. Create 2 *separate* clusters with RELAY as top protocol. Each RELAY has bridge_props="tcp.xml" (tcp.xml * needs to be present). Then start 2 instances in the first cluster and 2 instances in the second cluster. They should * find each other, and typing in a window should send the text to everyone, plus we should get 4 responses. * @author Bela Ban */ public class RelayDemoRpc extends ReceiverAdapter { protected JChannel ch; protected RpcDispatcher disp; protected Address local_addr; protected View view; public static void main(String[] args) throws Exception { String props="udp.xml"; String name=null; for(int i=0; i < args.length; i++) { if(args[i].equals("-props")) { props=args[++i]; continue; } if(args[i].equals("-name")) { name=args[++i]; continue; } System.out.println("RelayDemo [-props props] [-name name]"); return; } RelayDemoRpc demo=new RelayDemoRpc(); demo.start(props, name); } public void start(String props, String name) throws Exception { ch=new JChannel(props); if(name != null) ch.setName(name); disp=new RpcDispatcher(ch, null, this, this); ch.connect("RelayDemo"); local_addr=ch.getAddress(); MethodCall call=new MethodCall(getClass().getMethod("handleMessage", String.class, Address.class)); for(;;) { String line=Util.readStringFromStdin(": "); call.setArgs(new Object[]{line, local_addr}); if(line.equalsIgnoreCase("unicast")) { for(Address dest: view.getMembers()) { System.out.println("invoking method in " + dest + ": "); try { Object rsp=disp.callRemoteMethod(dest, call, new RequestOptions(Request.GET_ALL, 5000)); System.out.println("rsp from " + dest + ": " + rsp); } catch(Throwable throwable) { throwable.printStackTrace(); } } continue; } RspList rsps=disp.callRemoteMethods(null, call, new RequestOptions(Request.GET_ALL, 5000).setAnycasting(true)); for(Rsp rsp: rsps.values()) System.out.println("<< " + rsp.getValue() + " from " + rsp.getSender()); } } public static String handleMessage(String msg, Address sender) { System.out.println("<< " + msg + " from " + sender); return "this is a response"; } public void viewAccepted(View new_view) { System.out.println(print(new_view)); view=new_view; } static String print(View view) { StringBuilder sb=new StringBuilder(); boolean first=true; sb.append(view.getClass().getSimpleName() + ": ").append(view.getViewId()).append(": "); for(Address mbr: view.getMembers()) { if(first) first=false; else sb.append(", "); sb.append(mbr); } return sb.toString(); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/demos/ReplCacheDemo.java0000644000175000017500000003700411647260573026237 0ustar moellermoeller package org.jgroups.demos; import org.jgroups.View; import org.jgroups.blocks.Cache; import org.jgroups.blocks.MembershipListenerAdapter; import org.jgroups.blocks.ReplCache; import org.jgroups.jmx.JmxConfigurator; import javax.management.MBeanServer; import javax.swing.*; import javax.swing.table.AbstractTableModel; import javax.swing.table.TableModel; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; import java.lang.management.ManagementFactory; import java.util.Map; import java.util.concurrent.ConcurrentMap; /** * GUI demo of ReplCache * @author Bela Ban */ public class ReplCacheDemo extends JPanel implements ActionListener { private ReplCache cache; private static final String BASENAME="replcache"; private JFrame frame; private JTabbedPane root_pane=new JTabbedPane(); private JTable table; private JTextField key_field=createTextField(null, 10); private JTextField value_field=createTextField(null, 10); private JTextField repl_count_field=createTextField("1", 3); private JTextField timeout_field=createTextField("0", 5); private JTextField perf_key_prefix=createTextField("key", 5); private JTextField perf_num_keys=createTextField("1000", 5); private JTextField perf_size=createTextField("1000", 5); private JTextField perf_repl_count_field=createTextField("1", 3); private JTextField perf_timeout_field=createTextField("0", 5); private JTextArea status=new JTextArea("Status area", 10, 5); private JLabel num_elements=new JLabel("0 elements"); private MyTableModel model=null; public void actionPerformed(ActionEvent event) { String command = event.getActionCommand(); if(command.equals("Put")) { String key=key_field.getText(); String value=value_field.getText(); String repl_count=repl_count_field.getText(); String timeout=timeout_field.getText(); if(key == null || value == null) return; if(repl_count == null) repl_count="1"; if(timeout == null) timeout="0"; cache.put(key, value, Short.valueOf(repl_count), Long.valueOf(timeout)); } else if(command.equals("Remove")) { int[] rows=table.getSelectedRows(); if(rows != null) { for(int row: rows) { String key=(String)model.getValueAt(row, 0); if(key != null) cache.remove(key); } } } else if(command.equals("Clear")) { clear(); } else if(command.equals("Rebalance")) { cache.mcastEntries(); } else if(command.equals("Reset")) { status.setText(""); } else if(command.equals("Start")) { startPerfTest(); } else if(command.equals("Stop")) { } else if(command.equals("Exit")) { if(cache != null) cache.stop(); frame.dispose(); System.exit(1); // or can we break out of mainLoop() somehow else ? } } private void clear() { cache.clear(); } private void startPerfTest() { int num_puts=1000; int size=1000; short repl_count=1; long timeout=0; String key_prefix="key"; String tmp=perf_key_prefix.getText(); if(tmp != null) key_prefix=tmp; tmp=perf_num_keys.getText(); if(tmp != null) num_puts=Integer.valueOf(tmp); tmp=perf_size.getText(); if(tmp != null) size=Integer.valueOf(tmp); tmp=perf_repl_count_field.getText(); if(tmp != null) repl_count=Short.valueOf(tmp); tmp=perf_timeout_field.getText(); if(tmp != null) timeout=Long.valueOf(tmp); long start=System.currentTimeMillis(); for(int i=0; i < num_puts; i++) { String key=key_prefix + "-" + i; String value="val-" + i; cache.put(key, value, repl_count, timeout); } long diff=System.currentTimeMillis() - start; status.setText("It took " + diff + " ms to insert " + num_puts + " elements"); } private void start(String props, String cluster_name, long rpc_timeout, long caching_time, boolean migrate_data, boolean use_l1_cache, int l1_max_entries, long l1_reaping_interval, int l2_max_entries, long l2_reaping_interval) throws Exception { MBeanServer server=ManagementFactory.getPlatformMBeanServer(); cache=new ReplCache(props, cluster_name); cache.setCallTimeout(rpc_timeout); cache.setCachingTime(caching_time); cache.setMigrateData(migrate_data); JmxConfigurator.register(cache, server, BASENAME + ":name=cache"); JmxConfigurator.register(cache.getL2Cache(), server, BASENAME + ":name=l2-cache"); if(use_l1_cache) { Cache l1_cache=new Cache(); cache.setL1Cache(l1_cache); if(l1_reaping_interval > 0) l1_cache.enableReaping(l1_reaping_interval); if(l1_max_entries > 0) l1_cache.setMaxNumberOfEntries(l1_max_entries); JmxConfigurator.register(cache.getL1Cache(), server, BASENAME + ":name=l1-cache"); } if(l2_max_entries > 0 || l2_reaping_interval > 0) { Cache> l2_cache=cache.getL2Cache(); if(l2_max_entries > 0) l2_cache.setMaxNumberOfEntries(l2_max_entries); if(l2_reaping_interval > 0) l2_cache.enableReaping(l2_reaping_interval); } Runtime.getRuntime().addShutdownHook(new Thread() { public void run() { cache.stop(); } }); cache.start(); model=new MyTableModel(); model.setMap(cache.getL2Cache().getInternalMap()); cache.addChangeListener(model); frame=new JFrame("ReplCacheDemo"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); table=new MyTable(model); table.setPreferredScrollableViewportSize(new Dimension(500, 200)); // table.setFillsViewportHeight(true); // JDK 6 specific table.setShowGrid(false); table.setFont(table.getFont().deriveFont(Font.BOLD)); add(new JScrollPane(table)); JPanel key=new JPanel(new FlowLayout(FlowLayout.LEFT)); key.add(new JLabel("Key ")); key.add(key_field); add(key); JPanel value=new JPanel(new FlowLayout(FlowLayout.LEFT)); value.add(new JLabel("Value")); value.add(value_field); add(value); JPanel repl_count=new JPanel(new FlowLayout(FlowLayout.LEFT)); repl_count.add(new JLabel("Replication count")); repl_count.add(repl_count_field); add(repl_count); JPanel timeout=new JPanel(new FlowLayout(FlowLayout.LEFT)); timeout.add(new JLabel("Timeout")); timeout.add(timeout_field); add(timeout); JPanel buttons=new JPanel(); JButton put_button=createButton("Put"); buttons.add(createButton("Put")); buttons.add(createButton("Remove")); buttons.add(createButton("Clear")); buttons.add(createButton("Rebalance")); buttons.add(createButton("Exit")); buttons.add(num_elements); add(buttons); setOpaque(true); root_pane.addTab("Data", this); JPanel perf_panel=new JPanel(); perf_panel.setLayout(new BoxLayout(perf_panel, BoxLayout.Y_AXIS)); perf_panel.setOpaque(true); root_pane.addTab("Perf test", perf_panel); perf_panel.add(status); status.setForeground(Color.BLUE); JPanel prefix=new JPanel(new FlowLayout(FlowLayout.LEFT)); prefix.add(new JLabel("Key prefix")); prefix.add(perf_key_prefix); perf_panel.add(prefix); JPanel keys=new JPanel(new FlowLayout(FlowLayout.LEFT)); keys.add(new JLabel("Number of keys to insert")); keys.add(perf_num_keys); perf_panel.add(keys); JPanel size=new JPanel(new FlowLayout(FlowLayout.LEFT)); size.add(new JLabel("Size of each key (bytes)")); size.add(perf_size); size.add(new JLabel(" (ignored for now)")); perf_panel.add(size); JPanel perf_repl_count=new JPanel(new FlowLayout(FlowLayout.LEFT)); perf_repl_count.add(new JLabel("Replication count")); perf_repl_count.add(perf_repl_count_field); perf_panel.add(perf_repl_count); JPanel perf_timeout=new JPanel(new FlowLayout(FlowLayout.LEFT)); perf_timeout.add(new JLabel("Timeout")); perf_timeout.add(perf_timeout_field); perf_panel.add(perf_timeout); JPanel perf_buttons=new JPanel(new FlowLayout(FlowLayout.LEFT)); perf_buttons.add(createButton("Start")); perf_buttons.add(createButton("Stop")); perf_buttons.add(createButton("Reset")); perf_buttons.add(createButton("Exit")); perf_panel.add(perf_buttons); frame.setContentPane(root_pane); frame.pack(); frame.getRootPane().setDefaultButton(put_button); frame.setVisible(true); setTitle("ReplCacheDemo"); cache.addMembershipListener(new MembershipListenerAdapter() { public void viewAccepted(View new_view) { setTitle("ReplCacheDemo"); } }); } private JButton createButton(String text) { JButton retval=new JButton(text); retval.addActionListener(this); return retval; } private static JTextField createTextField(String name, int length) { JTextField retval=new JTextField(name, length); retval.addFocusListener(new MyFocusListener(retval)); return retval; } public static void main(String[] args) throws Exception { String props="udp.xml"; String cluster_name="replcache-cluster"; long rpc_timeout=1500L, caching_time=30000L; boolean migrate_data=true, use_l1_cache=true; int l1_max_entries=5000, l2_max_entries=-1; long l1_reaping_interval=-1, l2_reaping_interval=30000L; for(int i=0; i < args.length; i++) { if(args[i].equals("-props")) { props=args[++i]; continue; } if(args[i].equals("-cluster_name")) { cluster_name=args[++i]; continue; } if(args[i].equals("-rpc_timeout")) { rpc_timeout=Long.parseLong(args[++i]); continue; } if(args[i].equals("-caching_time")) { caching_time=Long.parseLong(args[++i]); continue; } if(args[i].equals("-migrate_data")) { migrate_data=Boolean.parseBoolean(args[++i]); continue; } if(args[i].equals("-use_l1_cache")) { use_l1_cache=Boolean.parseBoolean(args[++i]); continue; } if(args[i].equals("-l1_max_entries")) { l1_max_entries=Integer.parseInt(args[++i]); continue; } if(args[i].equals("-l1_reaping_interval")) { l1_reaping_interval=Long.parseLong(args[++i]); continue; } if(args[i].equals("-l2_max_entries")) { l2_max_entries=Integer.parseInt(args[++i]); continue; } if(args[i].equals("-l2_reaping_interval")) { l2_reaping_interval=Long.parseLong(args[++i]); continue; } help(); return; } ReplCacheDemo demo = new ReplCacheDemo(); demo.start(props, cluster_name, rpc_timeout, caching_time, migrate_data, use_l1_cache, l1_max_entries, l1_reaping_interval, l2_max_entries, l2_reaping_interval); } void setTitle(String title) { String local_addr=cache != null? cache.getLocalAddressAsString() : null; int num_nodes=cache != null? cache.getClusterSize() : 0; frame.setTitle(title + ": " + local_addr + " (" + num_nodes + ")"); } private static void help() { System.out.println("ReplCacheServer [-help] [-props ] [-cluster_name ]" + "[-rpc_timeout ] [-caching_time ] " + "[-migrate_data ] [-use_l1_cache ] " + "[-l1_max_entries ] [-l1_reaping_interval ] " + "[-l2_max_entries ] [-l2_reaping_interval ] "); } private static class MyFocusListener implements FocusListener { private final JTextField field; public MyFocusListener(JTextField field) { this.field=field; } public void focusGained(FocusEvent e) { String value=field.getText(); if(value != null && value.length() > 0) { field.selectAll(); } } public void focusLost(FocusEvent e) { } } private static class MyTable extends JTable { private MyTable(TableModel dm) { super(dm); } public boolean getScrollableTracksViewportHeight() { Container viewport=getParent(); return viewport instanceof JViewport && getPreferredSize().height < viewport.getHeight(); } } private class MyTableModel extends AbstractTableModel implements ReplCache.ChangeListener { private ConcurrentMap>> map; private final String[] columnNames = {"Key", "Value", "Replication Count", "Timeout"}; private static final long serialVersionUID=1314724464389654329L; public void setMap(ConcurrentMap>> map) { this.map=map; } public int getColumnCount() { return columnNames.length; } public int getRowCount() { return map.size(); } public String getColumnName(int col) { return columnNames[col]; } public Object getValueAt(int row, int col) { int count=0; for(Map.Entry>> entry: map.entrySet()) { if(count++ >= row) { K key=entry.getKey(); Cache.Value> val=entry.getValue(); ReplCache.Value tmp=val.getValue(); switch(col) { case 0: return key; case 1: V value=tmp.getVal(); return value instanceof byte[]? ((byte[])value).length + " bytes" : value; case 2: return tmp.getReplicationCount(); case 3: return val.getTimeout(); default: return "n/a"; } } } throw new IllegalArgumentException("row=" + row + ", col=" + col); } public void changed() { fireTableDataChanged(); num_elements.setText(cache.getL2Cache().getSize() + " elements"); } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/demos/ReplicatedHashMapDemo.java0000644000175000017500000002076111647260573027731 0ustar moellermoeller 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 */ 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; 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]"); } }libjgroups-java-2.12.2.Final.orig/src/org/jgroups/demos/ReplicatedTreeDemo.java0000644000175000017500000005363411647260573027314 0ustar moellermoeller 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]); } } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/demos/StompChat.java0000644000175000017500000003341511647260573025510 0ustar moellermoellerpackage org.jgroups.demos; import org.jgroups.client.StompConnection; import org.jgroups.util.Util; import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.util.*; import java.util.List; /** * Chat client using STOMP to talk to other clients */ public class StompChat implements StompConnection.Listener { JFrame mainFrame; TextArea txtArea; JTextField txtField; final JLabel csLabel=new JLabel("Send: "), status=new JLabel(""); JButton leaveButton; JButton sendButton; JButton clearButton; final JLabel cluster=new JLabel("Cluster: "), users_label=new JLabel("Users: "); private int num_servers=1; private int num_clients=0; protected String username=null; protected final Set users=new HashSet(); private final List servers=new ArrayList(); private final Set clients=new HashSet(); protected StompConnection stomp_client; static enum Destination { messages("/messages"), client_joined("/client-joined"); final String name; Destination(String name) { this.name=name; } } // ======================== reserved topic ========================== public static final String MESSAGES = "/messages"; // headers + body public static final String CLIENT_JOINED = "/client-joined"; // client: 1234-2532-2665 public static final String CLIENT_LEFT = "/client-left"; // client: 1432-7263-1002 public static final String CLIENTS = "/clients"; // clients: 355352,3343,2232 public static final String USER_JOINED = "/user-joined"; // user: Bela public static final String USER_LEFT = "/user-left"; // user: Bela public static final String GET_USERS = "/get-users"; // public static final String USERS = "/users"; // users: Bela, Michelle // reserved keywords in INFO messages public static final String ENDPOINTS = "endpoints"; public static final String VIEW = "view"; public static final String CLIENTS_KW = "clients"; public static final String DESTINATION = "destination"; public static final String USER = "user"; public static final String USERS_KW = "users"; public static final String CLIENT = "client"; public StompChat(String host, int port, String user) { stomp_client=new StompConnection(host + ":" + port); stomp_client.addListener(this); username=user; try { if(username == null) username=System.getProperty("user.name"); } catch(Throwable t) { } } public static void main(String[] args) throws Exception { String host="localhost"; int port=8787; String user=null; for(int i=0; i < args.length; i++) { if(args[i].equals("-host") || args[i].equals("-h")) { host=args[++i]; continue; } if(args[i].equals("-port") || args[i].equals("-p")) { port=Integer.parseInt(args[++i]); continue; } if(args[i].equals("-user") || args[i].equals("-name")) { user=args[++i]; continue; } help(); return; } StompChat instance=new StompChat(host, port, user); instance.start(); } void showMessage(String msg) { txtArea.append(msg + "\n"); } void userJoined(String name) { users.add(name); showStatus(name + " joined the chat"); users_label.setText("Users: " + users); } void userLeft(String name) { users.remove(name); showStatus(name + " left the chat"); users_label.setText("Users: " + users); } void newView(String view) { cluster.setText("Cluster: " + view); } void usersReceived(Collection users) { this.users.addAll(users); users_label.setText("Users: " + this.users); } static void help() { System.out.println("Chat [-help] [-host ] [-port ] [-user ]"); } public void start() throws Exception { mainFrame=new JFrame("Chat demo"); mainFrame.setPreferredSize(new Dimension(600,600)); mainFrame.setBackground(Color.white); mainFrame.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { stomp_client.send(USER_LEFT, USER, username); stomp_client.send(CLIENT_LEFT, CLIENT, username); stomp_client.disconnect(); System.exit(0); } }); connect(); Box main_box=Box.createVerticalBox(); main_box.setBackground(Color.white); Box input=Box.createHorizontalBox(); // input field Box buttons=Box.createHorizontalBox(); // for all the buttons mainFrame.add(main_box); main_box.add(Box.createVerticalStrut(10)); main_box.add(cluster); cluster.setAlignmentX(Component.LEFT_ALIGNMENT); main_box.add(Box.createVerticalStrut(10)); main_box.add(Box.createVerticalStrut(10)); main_box.add(users_label); main_box.add(Box.createVerticalStrut(10)); txtArea=new TextArea(); txtArea.setPreferredSize(new Dimension(550, 500)); txtArea.setEditable(false); txtArea.setBackground(Color.white); main_box.add(txtArea); main_box.add(Box.createVerticalStrut(10)); main_box.add(input); main_box.add(Box.createVerticalStrut(10)); main_box.add(buttons); csLabel.setPreferredSize(new Dimension(85, 30)); input.add(csLabel); txtField=new JTextField(); txtField.setPreferredSize(new Dimension(200, 30)); txtField.setBackground(Color.white); input.add(txtField); leaveButton=new JButton("Leave"); leaveButton.setPreferredSize(new Dimension(150, 30)); buttons.add(leaveButton); leaveButton.addMouseListener(new MouseAdapter() { public void mouseClicked(MouseEvent e) { stomp_client.send(USER_LEFT, USER, username); stomp_client.send(CLIENT_LEFT, CLIENT, username); stomp_client.disconnect(); System.exit(0); } }); sendButton=new JButton("Send"); sendButton.setPreferredSize(new Dimension(150, 30)); buttons.add(sendButton); sendButton.addMouseListener(new MouseAdapter() { public void mouseClicked(MouseEvent e) { send(txtField.getText()); txtField.selectAll(); } }); clearButton=new JButton("Clear"); clearButton.setPreferredSize(new Dimension(150, 30)); clearButton.addMouseListener(new MouseAdapter() { public void mouseClicked(MouseEvent e) { txtArea.setText(""); } }); buttons.add(clearButton); status.setForeground(Color.red); main_box.add(status); mainFrame.pack(); mainFrame.setLocation(15, 25); Dimension main_frame_size=mainFrame.getSize(); txtArea.setPreferredSize(new Dimension((int)(main_frame_size.width * 0.9), (int)(main_frame_size.height * 0.8))); mainFrame.setVisible(true); txtField.setFocusable(true); txtField.requestFocusInWindow(); txtField.setToolTipText("type and then press enter to send"); txtField.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { String cmd=e.getActionCommand(); if(cmd != null && cmd.length() > 0) { send(txtField.getText()); txtField.selectAll(); } } }); sendGetUsers(); } protected void connect() throws Exception { stomp_client.connect(); stomp_client.send(USER_JOINED, USER, username); stomp_client.subscribe(MESSAGES); stomp_client.subscribe(CLIENT_JOINED); stomp_client.subscribe(CLIENT_LEFT); stomp_client.subscribe(CLIENTS); stomp_client.subscribe(USER_JOINED); stomp_client.subscribe(USER_LEFT); stomp_client.subscribe(GET_USERS); stomp_client.subscribe(USERS); stomp_client.send(CLIENT_JOINED, CLIENT, username); stomp_client.send(USER_JOINED, USER, username); } protected void send(String msg) { try { String tmp=username + ": " + msg; byte[] buf=tmp.getBytes(); stomp_client.send(MESSAGES, buf, 0, buf.length); } catch(Exception e) { System.err.println("Failed sending message: " + e); } } public void sendGetUsers() { stomp_client.send(GET_USERS); } protected void showStatus(final String msg) { new Thread() { public void run() { synchronized(status) { status.setText(msg); Util.sleep(2000); status.setText(""); } } }.start(); } public void onInfo(Map information) { String view=information.get("view"); Collection list; if(view != null) { list=Util.parseCommaDelimitedStrings(view); if(list != null) { num_servers=list.size(); if(mainFrame != null) setTitle(); servers.clear(); servers.addAll(list); newView(view); } else { String targets=information.get("endpoints"); if(targets != null) { list=Util.parseCommaDelimitedStrings(targets); if(list != null) { num_servers=list.size(); if(mainFrame != null) setTitle(); servers.clear(); servers.addAll(list); } } } } } public void onMessage(Map headers, byte[] buf, int offset, int length) { String destination=headers.get("destination"); if(destination == null) return; if(destination.equals(MESSAGES)) { showMessage(new String(buf, offset, length)); return; } if(destination.equals(CLIENT_JOINED)) { String new_client=headers.get(CLIENT); if(new_client != null) { synchronized(clients) { if(clients.add(new_client)) { num_clients=clients.size(); setTitle(); } } stomp_client.send(CLIENTS, null, 0, 0, CLIENTS_KW, getAllClients()); } return; } if(destination.equals(CLIENT_LEFT)) { String left_client=headers.get(CLIENT); if(left_client != null) { synchronized(clients) { if(clients.remove(left_client)) { num_clients=clients.size(); setTitle(); } } } return; } if(destination.equals(CLIENTS)) { String all_clients=headers.get(CLIENTS_KW); if(all_clients != null) { List list=Util.parseCommaDelimitedStrings(all_clients); if(list != null) { synchronized(clients) { if(clients.addAll(list)) { num_clients=clients.size(); setTitle(); } } } } return; } if(destination.equals(USER_JOINED)) { String name=headers.get(USER); if(name != null) userJoined(name); return; } if(destination.equals(USER_LEFT)) { String name=headers.get(USER); if(name != null) userLeft(name); return; } if(destination.equals(GET_USERS)) { stomp_client.send(USERS, USERS_KW, usersToStr()); return; } if(destination.equals(USERS)) { String tmp=headers.get(USERS_KW); if(tmp != null) { List list=Util.parseCommaDelimitedStrings(tmp); if(list != null) usersReceived(list); } } } private String usersToStr() { StringBuilder sb=new StringBuilder(); boolean first=true; for(String user: users) { if(first) first=false; else sb.append(","); sb.append(user); } return sb.toString(); } void setTitle() { if(mainFrame != null) mainFrame.setTitle(num_servers + " server(s), " + num_clients + " client(s)"); } int getNumberOfClients() { synchronized(clients) { return clients.size(); } } String getAllClients() { StringBuilder sb=new StringBuilder(); boolean first=true; for(String client: clients) { if(first) first=false; else sb.append(","); sb.append(client); } return sb.toString(); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/demos/StompDraw.java0000644000175000017500000003737611647260573025540 0ustar moellermoeller package org.jgroups.demos; import org.jgroups.client.StompConnection; import org.jgroups.util.Util; import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.io.*; import java.util.*; import java.util.List; /** * Simple STOMP demo client. Use -h and -p to connect to *any* JGroups server (has to have STOMP in the config) *

* @author Bela Ban, Oct 17 2001 */ public class StompDraw implements StompConnection.Listener, ActionListener { private int num_servers=1; private int num_clients=0; 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; private final List servers=new ArrayList(); private final Set clients=new HashSet(); protected StompConnection stomp_client; protected static final String draw_dest="/topics/draw-demo"; protected static final String clients_dest="/topics/clients"; public StompDraw(String host, String port) throws Exception { stomp_client=new StompConnection(host + ":" + port); stomp_client.addListener(this); } public static void main(String[] args) { StompDraw draw=null; String host="localhost", port="8787"; for(int i=0; i < args.length; i++) { if("-help".equals(args[i])) { help(); return; } if("-h".equals(args[i])) { host=args[++i]; continue; } if("-p".equals(args[i])) { port=args[++i]; continue; } help(); return; } try { draw=new StompDraw(host, port); draw.go(); } catch(Throwable e) { System.err.println("fatal error: " + e.getLocalizedMessage() + ", cause: "); Throwable t=e.getCause(); if(t != null) t.printStackTrace(System.err); System.exit(0); } } static void help() { System.out.println("\nDraw [-help] [-no_channel] [-h host] [-port port]"); } 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); } private void sendToAll(byte[] buf) throws Exception { if(buf != null) stomp_client.send(draw_dest, buf, 0, buf.length); } public void go() throws Exception { stomp_client.connect(); stomp_client.subscribe(draw_dest); stomp_client.subscribe(clients_dest); mainFrame=new JFrame(); mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); panel=new DrawPanel(false); 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)); mainFrame.setVisible(true); setTitle(); String session_id=stomp_client.getSessionId(); if(session_id != null) stomp_client.send(clients_dest, null, 0, 0, "client-joined", session_id); Runtime.getRuntime().addShutdownHook(new Thread() { public void run() { StompDraw.this.stop(); } }); } void setTitle() { if(mainFrame != null) mainFrame.setTitle(num_servers + " server(s), " + num_clients + " client(s)"); } int getNumberOfClients() { synchronized(clients) { return clients.size(); } } String getAllClients() { StringBuilder sb=new StringBuilder(); boolean first=true; for(String client: clients) { if(first) first=false; else sb.append(","); sb.append(client); } return sb.toString(); } public void onInfo(Map information) { String view=information.get("view"); Collection list; if(view != null) { list=Util.parseCommaDelimitedStrings(view); if(list != null) { num_servers=list.size(); if(mainFrame != null) setTitle(); servers.clear(); servers.addAll(list); } else { String targets=information.get("endpoints"); if(targets != null) { list=Util.parseCommaDelimitedStrings(targets); if(list != null) { num_servers=list.size(); if(mainFrame != null) setTitle(); servers.clear(); servers.addAll(list); } } } } } public void onMessage(Map headers, byte[] buf, int offset, int length) { if(buf == null) return; String destination=headers.get("destination"); if(destination != null && destination.equals(clients_dest)) { String new_client=headers.get("client-joined"); if(new_client != null) { synchronized(clients) { if(clients.add(new_client)) { num_clients=clients.size(); setTitle(); } } stomp_client.send(clients_dest, null, 0, 0, "clients", getAllClients()); } String left_client=headers.get("client-left"); if(left_client != null) { synchronized(clients) { if(clients.remove(left_client)) { num_clients=clients.size(); setTitle(); } } } String all_clients=headers.get("clients"); if(all_clients != null) { List list=Util.parseCommaDelimitedStrings(all_clients); if(list != null) { synchronized(clients) { if(clients.addAll(list)) { num_clients=clients.size(); setTitle(); } } } } return; } try { DrawCommand comm=(DrawCommand)Util.streamableFromByteBuffer(DrawCommand.class, buf, offset, length); 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(); } } /* --------------- Callbacks --------------- */ public void clearPanel() { if(panel != null) panel.clear(); } public void sendClearPanelMsg() { DrawCommand comm=new DrawCommand(DrawCommand.CLEAR); try { byte[] buf=Util.streamableToByteBuffer(comm); sendToAll(buf); } catch(Exception ex) { System.err.println(ex); } } public void actionPerformed(ActionEvent e) { String command=e.getActionCommand(); if("Clear".equals(command)) { sendClearPanelMsg(); } else if("Leave".equals(command)) { stop(); mainFrame.setVisible(false); mainFrame.dispose(); } else System.out.println("Unknown action"); } public void stop() { if(!stomp_client.isConnected()) return; String session_id=stomp_client.getSessionId(); if(session_id != null) { stomp_client.send(clients_dest, null, 0, 0, "client-left", session_id); } stomp_client.disconnect(); } 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; } @SuppressWarnings("unchecked") 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()); try { byte[] buf=Util.streamableToByteBuffer(comm); sendToAll(buf); } 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); } } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/demos/Topology.java0000644000175000017500000001412211647260573025414 0ustar moellermoeller 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.getAddress(); 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); } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/demos/TotalOrder.java0000644000175000017500000005040611647260573025664 0ustar moellermoeller 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.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="udp.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); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/demos/TotalTokenDemo.java0000644000175000017500000003607311647260573026502 0ustar moellermoeller 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.13 $ * *@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.getAddress(); 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(String.valueOf(vw.getVid().getId())); control.numMessagesInLastView.setText(String.valueOf(counter)); counter = 0; v.clear(); continue; } 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.xml"; } 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(); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/demos/ViewDemo.java0000644000175000017500000000327011647260573025321 0ustar moellermoeller package org.jgroups.demos; import org.jgroups.*; import org.jgroups.util.Util; /** * 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) throws Exception { channel=new JChannel(props); channel.setReceiver(this); channel.connect("ViewDemo"); while(true) { Util.sleep(10000); } } public static void main(String args[]) { ViewDemo t=new ViewDemo(); 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("-bind_addr".equals(args[i])) { System.setProperty("jgroups.bind_addr", args[++i]); continue; } help(); return; } try { t.start(props); } catch(Exception e) { e.printStackTrace(); } } static void help() { System.out.println("ViewDemo [-props ] [-help] [-use_additional_data ] [-bind_addr

]"); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/demos/applets/0000755000175000017500000000000011647260573024405 5ustar moellermoellerlibjgroups-java-2.12.2.Final.orig/src/org/jgroups/demos/applets/DrawApplet.java0000644000175000017500000001652211647260573027321 0ustar moellermoeller 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.jgroups.logging.Log; import org.jgroups.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.toString()); } 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.toString()); 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.toString()); } 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.toString()); } } 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.toString()); } } 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.toString()); } } 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)); } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/demos/applets/draw.html0000644000175000017500000000101111647260573026221 0ustar moellermoeller A Simple Drawing Program Here is an example of a distributed Draw program:
libjgroups-java-2.12.2.Final.orig/src/org/jgroups/demos/applets/package.html0000644000175000017500000000013511647260573026665 0ustar moellermoeller Provides an applet that demonstrates JGroups functionality. libjgroups-java-2.12.2.Final.orig/src/org/jgroups/demos/package.html0000644000175000017500000000012311647260573025212 0ustar moellermoeller Provides demonstrations of JGroups functionality. libjgroups-java-2.12.2.Final.orig/src/org/jgroups/demos/wb/0000755000175000017500000000000011647260573023345 5ustar moellermoellerlibjgroups-java-2.12.2.Final.orig/src/org/jgroups/demos/wb/GraphPanel.java0000644000175000017500000001676511647260573026250 0ustar moellermoeller package org.jgroups.demos.wb; import org.jgroups.logging.Log; import org.jgroups.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; 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.toString()); } 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.toString()); } 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(); } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/demos/wb/MessageDialog.java0000644000175000017500000000165611647260573026724 0ustar moellermoeller 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(); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/demos/wb/Node.java0000644000175000017500000000100611647260573025072 0ustar moellermoeller 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(); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/demos/wb/SendDialog.java0000644000175000017500000000426011647260573026223 0ustar moellermoeller 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(); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/demos/wb/UserInfoDialog.java0000644000175000017500000000257311647260573027071 0ustar moellermoeller 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()); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/demos/wb/Whiteboard.java0000644000175000017500000001670711647260573026313 0ustar moellermoeller package org.jgroups.demos.wb; import org.jgroups.*; import org.jgroups.blocks.*; import org.jgroups.blocks.GroupRequest; import org.jgroups.blocks.RpcDispatcher; import org.jgroups.logging.Log; import org.jgroups.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.getAddress(); 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.toString()); } 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.toString()); } } 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) { } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/demos/wb/package.html0000644000175000017500000000013411647260573025624 0ustar moellermoeller A distributed whiteboard applet implemented using JGroups. libjgroups-java-2.12.2.Final.orig/src/org/jgroups/demos/wb/wb.html0000644000175000017500000000076411647260573024652 0ustar moellermoeller A Simple Whiteboard Program
/HTML> libjgroups-java-2.12.2.Final.orig/src/org/jgroups/jmx/0000755000175000017500000000000011647260573022424 5ustar moellermoellerlibjgroups-java-2.12.2.Final.orig/src/org/jgroups/jmx/JmxConfigurator.java0000644000175000017500000002324511647260573026416 0ustar moellermoellerpackage org.jgroups.jmx; import org.jgroups.JChannel; import org.jgroups.JChannelFactory; import org.jgroups.annotations.MBean; import org.jgroups.annotations.ManagedAttribute; import org.jgroups.annotations.ManagedOperation; import org.jgroups.logging.Log; import org.jgroups.logging.LogFactory; import org.jgroups.stack.Protocol; import org.jgroups.stack.ProtocolStack; import javax.management.*; import java.util.Iterator; import java.util.List; import java.util.Set; /** * @author Bela Ban, Vladimir Blagojevic */ public class JmxConfigurator { static final Log log = LogFactory.getLog(JmxConfigurator.class); /** * Registers an already created channel with the given MBeanServer. Wraps instance of JChannel * with DynamicMBean and delegates all calls to the actual JChannel wrapped. *

* Optionally, this method will also wrap each protocol in the given channel with DynamicMBean * 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 */ public static void registerChannel(JChannel channel, MBeanServer server, String domain, String cluster_name, boolean register_protocols) throws Exception { if(channel == null) throw new NullPointerException("channel cannot be null"); if (cluster_name == null) cluster_name=channel.getClusterName(); if (cluster_name == null) cluster_name = "null"; cluster_name=ObjectName.quote(cluster_name); if (register_protocols) { ProtocolStack stack = channel.getProtocolStack(); List protocols = stack.getProtocols(); for (Protocol p : protocols) { if (p.getClass().isAnnotationPresent(MBean.class)) { register(p, server, getProtocolRegistrationName(cluster_name, domain, p)); } } } register(channel, server, getChannelRegistrationName(channel, domain, cluster_name)); } /** * Registers an already created channel with the given MBeanServer. Wraps instance of JChannel * with DynamicMBean and delegates all calls to the actual JChannel wrapped. *

* This method will also wrap each protocol in the given channel with DynamicMBean 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 */ public static void registerChannel(JChannel channel, MBeanServer server, String name) throws Exception { registerChannel(channel, server, "jgroups", name, true); } 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 void unregisterChannel(JChannel c, MBeanServer server, String clusterName) throws Exception { unregisterChannel(c, server, "jgroups", clusterName); } public static void unregisterChannel(JChannel c, MBeanServer server, String domain, String clusterName) throws Exception { if(clusterName != null) clusterName=ObjectName.quote(clusterName); ProtocolStack stack = c.getProtocolStack(); List protocols = stack.getProtocols(); for (Protocol p : protocols) { if (p.getClass().isAnnotationPresent(MBean.class)) { try { unregister(p, server, getProtocolRegistrationName(clusterName, domain, p)); } catch (MBeanRegistrationException e) { if (log.isWarnEnabled()) { log.warn("MBean unregistration failed " + e); } } } } unregister(c, server, getChannelRegistrationName(c, domain, clusterName)); } public static void registerChannelFactory(JChannelFactory factory, MBeanServer server, String name) throws Exception { register(factory, server, name); } public static void unRegisterChannelFactory(JChannelFactory factory, MBeanServer server, String name) throws Exception { unregister(factory, server, name); } public static void register(Object obj, MBeanServer server, String name) throws MBeanRegistrationException, MalformedObjectNameException { internalRegister(obj, server, name); } public static void unregister(Object obj, MBeanServer server, String name) throws MBeanRegistrationException, MalformedObjectNameException { internalUnregister(obj, server, name); } @Deprecated public DynamicMBean asDynamicMBean(JChannel ch) { return new ResourceDMBean(ch); } @Deprecated public DynamicMBean asDynamicMBean(Protocol p) { return new ResourceDMBean(p); } /** * Wrap JChannel with DynamicMBean interface. All annotated attributes and methods will be * exposed through DynamicMBean API. * * @see ManagedAttribute * @see ManagedOperation * * @param ch channel to be wrapped * @return Channel ch wrapped as a DynamicBean */ public static DynamicMBean wrap(JChannel ch) { return new ResourceDMBean(ch); } /** * Wrap Protocol with DynamicMBean interface. All annotated attributes and methods will be * exposed through DynamicMBean API. * * @see ManagedAttribute * @see ManagedOperation * * @param p protocol to be wrapped * @return Protocol p as a DynamicMBean */ public static DynamicMBean wrap(Protocol p) { return new ResourceDMBean(p); } private static void internalRegister(Object obj, MBeanServer server, String name) throws MalformedObjectNameException, MBeanRegistrationException { if (obj == null) throw new IllegalArgumentException("Object being registered cannot be null"); if (server == null) throw new IllegalArgumentException("MBean server used for registeration cannot be null"); try { ObjectName objName = getObjectName(obj, name); ResourceDMBean res = new ResourceDMBean(obj); server.registerMBean(res, objName); } catch (InstanceAlreadyExistsException e) { if (log.isErrorEnabled()) { log.error("register MBean failed " + e.getMessage()); } throw new MBeanRegistrationException(e, "The @MBean objectName is not unique"); } catch (NotCompliantMBeanException e) { if (log.isErrorEnabled()) { log.error("register MBean failed " + e.getMessage()); } throw new MBeanRegistrationException(e); } } private static void internalUnregister(Object obj, MBeanServer server, String name) throws MBeanRegistrationException { try { if (name != null && name.length() > 0) { server.unregisterMBean(new ObjectName(name)); } else if (obj != null) { server.unregisterMBean(getObjectName(obj, null)); } else { throw new MBeanRegistrationException(null, "Cannot find MBean name from @MBean or passed in value"); } } catch (InstanceNotFoundException infe) { if (log.isErrorEnabled()) { log.error("unregister MBean failed " + infe.getMessage()); } throw new MBeanRegistrationException(infe); } catch (MalformedObjectNameException e) { if (log.isErrorEnabled()) { log.error("unregister MBean failed " + e.getMessage()); } throw new MBeanRegistrationException(e); } } private static ObjectName getObjectName(Object obj, String name) throws MalformedObjectNameException { MBean resource = obj.getClass().getAnnotation(MBean.class); if (name != null && name.length() > 0) { return new ObjectName(name); } else if (resource.objectName() != null && resource.objectName().length() > 0) { return new ObjectName(resource.objectName()); } else { throw new MalformedObjectNameException(obj + " of class " + obj.getClass() + " has an invalid object name"); } } /** * 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) { for (Iterator it = mbeans.iterator(); it.hasNext();) { server.unregisterMBean(it.next()); } } } private static String getChannelRegistrationName(JChannel c, String domain, String clusterName) { return domain + ":type=channel,cluster=" + clusterName; } private static String getProtocolRegistrationName(String clusterName, String domain, Protocol p) { return domain + ":type=protocol,cluster=" + clusterName + ",protocol=" + p.getName(); } private static String getChannelRegistrationName(String clusterName) { return "jgroups:type=channel,cluster=" + clusterName; } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/jmx/ResourceDMBean.java0000755000175000017500000005340211647260573026074 0ustar moellermoellerpackage org.jgroups.jmx; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import javax.management.Attribute; import javax.management.AttributeList; import javax.management.DynamicMBean; import javax.management.MBeanAttributeInfo; import javax.management.MBeanException; import javax.management.MBeanInfo; import javax.management.MBeanOperationInfo; import javax.management.ReflectionException; import org.jgroups.logging.Log; import org.jgroups.logging.LogFactory; import org.jgroups.annotations.MBean; import org.jgroups.annotations.ManagedAttribute; import org.jgroups.annotations.ManagedOperation; import org.jgroups.annotations.Property; import org.jgroups.util.Util; /** * * A DynamicMBean wrapping an annotated object instance. * * @author Chris Mills * @author Vladimir Blagojevic * @see ManagedAttribute * @see ManagedOperation * @see MBean * */ public class ResourceDMBean implements DynamicMBean { private static final Class[] primitives= { int.class, byte.class, short.class, long.class, float.class, double.class, boolean.class, char.class }; private static final String MBEAN_DESCRITION="Dynamic MBean Description"; private final Log log=LogFactory.getLog(ResourceDMBean.class); private final Object obj; private String description=""; private final MBeanAttributeInfo[] attrInfo; private final MBeanOperationInfo[] opInfo; private final HashMap atts=new HashMap(); private final List ops=new ArrayList(); public ResourceDMBean(Object instance) { if(instance == null) throw new NullPointerException("Cannot make an MBean wrapper for null instance"); this.obj=instance; findDescription(); findFields(); findMethods(); attrInfo=new MBeanAttributeInfo[atts.size()]; int i=0; MBeanAttributeInfo info=null; for(AttributeEntry entry:atts.values()) { info=entry.getInfo(); attrInfo[i++]=info; } opInfo=new MBeanOperationInfo[ops.size()]; ops.toArray(opInfo); } Object getObject() { return obj; } private void findDescription() { MBean mbean=getObject().getClass().getAnnotation(MBean.class); if(mbean != null && mbean.description() != null && mbean.description().trim().length() > 0) { description=mbean.description(); MBeanAttributeInfo info=new MBeanAttributeInfo(ResourceDMBean.MBEAN_DESCRITION, "java.lang.String", "MBean description", true, false, false); try { atts.put(ResourceDMBean.MBEAN_DESCRITION, new FieldAttributeEntry(info,getClass().getDeclaredField("description"))); } catch(NoSuchFieldException e) { //this should not happen unless somebody removes description field log.warn("Could not reflect field description of this class. Was it removed?"); } } } public synchronized MBeanInfo getMBeanInfo() { return new MBeanInfo(getObject().getClass().getCanonicalName(), description, attrInfo, null, opInfo, null); } public synchronized Object getAttribute(String name) { if(name == null || name.length() == 0) throw new NullPointerException("Invalid attribute requested " + name); Attribute attr=getNamedAttribute(name); return attr.getValue(); } public synchronized void setAttribute(Attribute attribute) { if(attribute == null || attribute.getName() == null) throw new NullPointerException("Invalid attribute requested " + attribute); setNamedAttribute(attribute); } public synchronized AttributeList getAttributes(String[] names) { AttributeList al=new AttributeList(); for(String name:names) { Attribute attr=getNamedAttribute(name); if(attr != null) { al.add(attr); } else { log.warn("Did not find attribute " + name); } } return al; } public synchronized AttributeList setAttributes(AttributeList list) { AttributeList results=new AttributeList(); for(int i=0;i < list.size();i++) { Attribute attr=(Attribute)list.get(i); if(setNamedAttribute(attr)) { results.add(attr); } else { if(log.isWarnEnabled()) { log.warn("Failed to update attribute name " + attr.getName() + " with value " + attr.getValue()); } } } return results; } public Object invoke(String name, Object[] args, String[] sig) throws MBeanException, ReflectionException { try { Class[] classes=new Class[sig.length]; for(int i=0;i < classes.length;i++) { classes[i]=getClassForName(sig[i]); } Method method=getObject().getClass().getMethod(name, classes); return method.invoke(getObject(), args); } catch(Exception e) { throw new MBeanException(e); } } public static Class getClassForName(String name) throws ClassNotFoundException { try { return Class.forName(name); } catch(ClassNotFoundException cnfe) { //Could be a primitive - let's check for(int i=0;i < primitives.length;i++) { if(name.equals(primitives[i].getName())) { return primitives[i]; } } } throw new ClassNotFoundException("Class " + name + " cannot be found"); } private void findMethods() { //find all methods but don't include methods from Object class List methods = new ArrayList(Arrays.asList(getObject().getClass().getMethods())); List objectMethods = new ArrayList(Arrays.asList(Object.class.getMethods())); methods.removeAll(objectMethods); for(Method method:methods) { //does method have @ManagedAttribute annotation? if(method.isAnnotationPresent(ManagedAttribute.class) || method.isAnnotationPresent(Property.class)) { exposeManagedAttribute(method); } //or @ManagedOperation else if (method.isAnnotationPresent(ManagedOperation.class) || isMBeanAnnotationPresentWithExposeAll()){ exposeManagedOperation(method); } } } /** find all methods but don't include methods from Object class */ /*private void findMethods() { for(Class clazz=getObject().getClass();clazz != null; clazz=clazz.getSuperclass()) { if(clazz.equals(Object.class)) break; Method[] methods=clazz.getDeclaredMethods(); for(Method method: methods) { //does method have @ManagedAttribute annotation? if(method.isAnnotationPresent(ManagedAttribute.class) || method.isAnnotationPresent(Property.class)) { exposeManagedAttribute(method); } //or @ManagedOperation else if (method.isAnnotationPresent(ManagedOperation.class) || isMBeanAnnotationPresentWithExposeAll()){ exposeManagedOperation(method); } } } }*/ private void exposeManagedOperation(Method method) { ManagedOperation op=method.getAnnotation(ManagedOperation.class); String attName=method.getName(); if(isSetMethod(method) || isGetMethod(method)) { attName=attName.substring(3); } else if(isIsMethod(method)) { attName=attName.substring(2); } //expose unless we already exposed matching attribute field boolean isAlreadyExposed=atts.containsKey(attName); if(!isAlreadyExposed) { ops.add(new MBeanOperationInfo(op != null? op.description() : "", method)); } } private void exposeManagedAttribute(Method method){ String methodName=method.getName(); if(!methodName.startsWith("get") && !methodName.startsWith("set") && !methodName.startsWith("is")) { if(log.isWarnEnabled()) log.warn("method name " + methodName + " doesn't start with \"get\", \"set\", or \"is\"" + ", but is annotated with @ManagedAttribute: will be ignored"); return; } ManagedAttribute attr = method.getAnnotation(ManagedAttribute.class); Property prop=method.getAnnotation(Property.class); boolean expose_prop=prop != null && prop.exposeAsManagedAttribute(); boolean expose=attr != null || expose_prop; if(!expose) return; // Is name field of @ManagedAttributed used? String attributeName=attr != null? attr.name() : null; if(attributeName != null && attributeName.trim().length() > 0) attributeName=attributeName.trim(); else attributeName=null; String descr=attr != null ? attr.description() : prop != null? prop.description() : null; boolean writeAttribute=false; MBeanAttributeInfo info=null; if(isSetMethod(method)) { // setter attributeName=(attributeName==null)?methodName.substring(3):attributeName; info=new MBeanAttributeInfo(attributeName, method.getParameterTypes()[0].getCanonicalName(), descr, true, true, false); writeAttribute=true; } else { // getter if(method.getParameterTypes().length == 0 && method.getReturnType() != java.lang.Void.TYPE) { boolean hasSetter=atts.containsKey(attributeName); //we found is method if(methodName.startsWith("is")) { attributeName=(attributeName==null)?methodName.substring(2):attributeName; info=new MBeanAttributeInfo(attributeName, method.getReturnType().getCanonicalName(), descr, true, hasSetter, true); } else { // this has to be get attributeName=(attributeName==null)?methodName.substring(3):attributeName; info=new MBeanAttributeInfo(attributeName, method.getReturnType().getCanonicalName(), descr, true, hasSetter, false); } } else { if(log.isWarnEnabled()) { log.warn("Method " + method.getName() + " must have a valid return type and zero parameters"); } //silently skip this method return; } } AttributeEntry ae=atts.get(attributeName); //is it a read method? if(!writeAttribute) { //we already have annotated field as read if(ae instanceof FieldAttributeEntry && ae.getInfo().isReadable()) { log.warn("not adding annotated method " + method + " since we already have read attribute"); } //we already have annotated set method else if(ae instanceof MethodAttributeEntry) { MethodAttributeEntry mae=(MethodAttributeEntry)ae; if(mae.hasSetMethod()) { atts.put(attributeName, new MethodAttributeEntry(mae.getInfo(), mae.getSetMethod(), method)); } } //we don't have such entry else { atts.put(attributeName, new MethodAttributeEntry(info, findSetter(obj.getClass(), attributeName), method)); } } //is it a set method? else { if(ae instanceof FieldAttributeEntry) { //we already have annotated field as write if(ae.getInfo().isWritable()) { log.warn("Not adding annotated method " + methodName + " since we already have writable attribute"); } else { //we already have annotated field as read //lets make the field writable Field f = ((FieldAttributeEntry)ae).getField(); MBeanAttributeInfo i=new MBeanAttributeInfo(ae.getInfo().getName(), f.getType().getCanonicalName(), descr, true, !Modifier.isFinal(f.getModifiers()), false); atts.put(attributeName,new FieldAttributeEntry(i,f)); } } //we already have annotated getOrIs method else if(ae instanceof MethodAttributeEntry) { MethodAttributeEntry mae=(MethodAttributeEntry)ae; if(mae.hasIsOrGetMethod()) { atts.put(attributeName, new MethodAttributeEntry(info, method, mae.getIsOrGetMethod())); } } // we don't have such entry else { atts.put(attributeName, new MethodAttributeEntry(info, method, findGetter(obj.getClass(), attributeName))); } } } private static boolean isSetMethod(Method method) { return(method.getName().startsWith("set") && method.getParameterTypes().length == 1 && method.getReturnType() == java.lang.Void.TYPE); } private static boolean isGetMethod(Method method) { return(method.getParameterTypes().length == 0 && method.getReturnType() != java.lang.Void.TYPE && method.getName().startsWith("get")); } private static boolean isIsMethod(Method method) { return(method.getParameterTypes().length == 0 && (method.getReturnType() == boolean.class || method.getReturnType() == Boolean.class) && method.getName().startsWith("is")); } public static Method findGetter(Class clazz, String name) { try { return clazz.getMethod("get" + name); } catch(NoSuchMethodException e) { } try { return clazz.getMethod("is" + name); } catch(NoSuchMethodException ex) { } return null; } public static Method findSetter(Class clazz, String name) { try { return clazz.getMethod("set" + name); } catch(NoSuchMethodException e) { } return null; } private void findFields() { //traverse class hierarchy and find all annotated fields for(Class clazz=getObject().getClass();clazz != null; clazz=clazz.getSuperclass()) { Field[] fields=clazz.getDeclaredFields(); for(Field field:fields) { ManagedAttribute attr=field.getAnnotation(ManagedAttribute.class); Property prop=field.getAnnotation(Property.class); boolean expose_prop=prop != null && prop.exposeAsManagedAttribute(); boolean expose=attr != null || expose_prop; if(expose) { String fieldName = Util.attributeNameToMethodName(field.getName()); String descr=attr != null? attr.description() : prop.description(); boolean writable=attr != null? attr.writable() : prop.writable(); MBeanAttributeInfo info=new MBeanAttributeInfo(fieldName, field.getType().getCanonicalName(), descr, true, !Modifier.isFinal(field.getModifiers()) && writable, false); atts.put(fieldName, new FieldAttributeEntry(info, field)); } } } } private Attribute getNamedAttribute(String name) { Attribute result=null; if(name.equals(ResourceDMBean.MBEAN_DESCRITION)) { result=new Attribute(ResourceDMBean.MBEAN_DESCRITION, this.description); } else { AttributeEntry entry=atts.get(name); if(entry != null) { try { result=new Attribute(name, entry.invoke(null)); } catch(Exception e) { log.warn("Exception while reading value of attribute " + name, e); } } else { log.warn("Did not find queried attribute with name " + name); } } return result; } private boolean setNamedAttribute(Attribute attribute) { boolean result=false; AttributeEntry entry=atts.get(attribute.getName()); if(entry != null) { try { entry.invoke(attribute); result=true; } catch(Exception e) { log.warn("Exception while writing value for attribute " + attribute.getName(), e); } } else { log.warn("Could not invoke set on attribute " + attribute.getName() + " with value " + attribute.getValue()); } return result; } private boolean isMBeanAnnotationPresentWithExposeAll(){ Class c=getObject().getClass(); return c.isAnnotationPresent(MBean.class) && c.getAnnotation(MBean.class).exposeAll(); } private class MethodAttributeEntry implements AttributeEntry { final MBeanAttributeInfo info; final Method isOrGetmethod; final Method setMethod; public MethodAttributeEntry(final MBeanAttributeInfo info, final Method setMethod, final Method isOrGetMethod) { super(); this.info=info; this.setMethod=setMethod; this.isOrGetmethod=isOrGetMethod; } public Object invoke(Attribute a) throws Exception { if(a == null && isOrGetmethod != null) return isOrGetmethod.invoke(getObject()); else if(a != null && setMethod != null) return setMethod.invoke(getObject(), a.getValue()); else return null; } public MBeanAttributeInfo getInfo() { return info; } public boolean hasIsOrGetMethod() { return isOrGetmethod != null; } public boolean hasSetMethod() { return setMethod != null; } public Method getIsOrGetMethod() { return isOrGetmethod; } public Method getSetMethod() { return setMethod; } } private class FieldAttributeEntry implements AttributeEntry { private final MBeanAttributeInfo info; private final Field field; public FieldAttributeEntry(final MBeanAttributeInfo info,final Field field) { super(); this.info=info; this.field=field; if(!field.isAccessible()) { field.setAccessible(true); } } public Field getField(){ return field; } public Object invoke(Attribute a) throws Exception { if(a == null) { return field.get(getObject()); } else { field.set(getObject(), a.getValue()); return null; } } public MBeanAttributeInfo getInfo() { return info; } } private interface AttributeEntry { public Object invoke(Attribute a) throws Exception; public MBeanAttributeInfo getInfo(); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/jmx/package.html0000644000175000017500000000014611647260573024706 0ustar moellermoeller libjgroups-java-2.12.2.Final.orig/src/org/jgroups/logging/0000755000175000017500000000000011647260573023254 5ustar moellermoellerlibjgroups-java-2.12.2.Final.orig/src/org/jgroups/logging/CustomLogFactory.java0000644000175000017500000000057011647260573027365 0ustar moellermoellerpackage org.jgroups.logging; /** * An extension interface allowing to plug in a custom log provider. Set the * jgroups.logging.log_factory_class system property with the fully * qualified class name of your implementation in order for JGroups to use it */ public interface CustomLogFactory { Log getLog(Class clazz); Log getLog(String category); } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/logging/JDKLogImpl.java0000644000175000017500000000620211647260573026013 0ustar moellermoellerpackage org.jgroups.logging; import java.util.logging.Level; import java.util.logging.Logger; /** * Logger that delivers messages to a JDK logger * @author Manik Surtani * @author Bela Ban * @since 2.8 */ public class JDKLogImpl implements Log { private final Logger logger; public JDKLogImpl(String category) { logger=Logger.getLogger(category); } public JDKLogImpl(Class category) { logger=Logger.getLogger(category.getName()); // fix for https://jira.jboss.org/browse/JGRP-1224 } public boolean isTraceEnabled() { return logger.isLoggable(Level.FINER); } public boolean isDebugEnabled() { return logger.isLoggable(Level.FINE); } public boolean isInfoEnabled() { return logger.isLoggable(Level.INFO); } public boolean isWarnEnabled() { return logger.isLoggable(Level.WARNING); } public boolean isErrorEnabled() { return logger.isLoggable(Level.SEVERE); } public boolean isFatalEnabled() { return logger.isLoggable(Level.SEVERE); } public void trace(String msg) { logger.log(Level.FINER, msg); } public void trace(Object msg) { logger.log(Level.FINER, msg.toString()); } public void debug(String msg) { logger.log(Level.FINE, msg); } public void info(String msg) { logger.log(Level.INFO, msg); } public void warn(String msg) { logger.log(Level.WARNING, msg); } public void error(String msg) { logger.log(Level.SEVERE, msg); } public void fatal(String msg) { logger.log(Level.SEVERE, msg); } public void trace(Object msg, Throwable t) { logger.log(Level.FINER, msg.toString(), t); } public void trace(String msg, Throwable t) { logger.log(Level.FINER, msg, t); } public void debug(String msg, Throwable t) { logger.log(Level.FINE, msg, t); } public void info(String msg, Throwable t) { logger.log(Level.INFO, msg, t); } public void warn(String msg, Throwable t) { logger.log(Level.WARNING, msg, t); } public void error(String msg, Throwable t) { logger.log(Level.SEVERE, msg, t); } public void fatal(String msg, Throwable t) { logger.log(Level.SEVERE, msg, t); } public String getLevel() { Level level=logger.getLevel(); return level != null? level.toString() : "off"; } public void setLevel(String level) { Level new_level=strToLevel(level); if(new_level != null) logger.setLevel(new_level); } private static Level strToLevel(String level) { if(level == null) return null; level=level.toLowerCase().trim(); if(level.equals("fatal")) return Level.SEVERE; if(level.equals("error")) return Level.SEVERE; if(level.equals("warn")) return Level.WARNING; if(level.equals("warning")) return Level.WARNING; if(level.equals("info")) return Level.INFO; if(level.equals("debug")) return Level.FINE; if(level.equals("trace")) return Level.FINER; return null; } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/logging/Log.java0000644000175000017500000000250311647260573024640 0ustar moellermoellerpackage org.jgroups.logging; /** * Simple logging wrapper for log4j or JDK logging. Code originally copied from Infinispan * * @author Manik Surtani * @author Bela Ban * @since 2.8 */ public interface Log { boolean isFatalEnabled(); boolean isErrorEnabled(); boolean isWarnEnabled(); boolean isInfoEnabled(); boolean isDebugEnabled(); boolean isTraceEnabled(); void debug(String msg); void debug(String msg, Throwable throwable); void error(String msg); void error(String msg, Throwable throwable); void fatal(String msg); void fatal(String msg, Throwable throwable); void info(String msg); void info(String msg, Throwable throwable); void trace(Object msg); void trace(Object msg, Throwable throwable); void trace(String msg); void trace(String msg, Throwable throwable); void warn(String msg); void warn(String msg, Throwable throwable); // Advanced methods /** * Sets the level of a logger. This method is used to dynamically change the logging level of a running system, * e.g. via JMX. The appender of a level needs to exist. * @param level The new level. Valid values are "fatal", "error", "warn", "info", "debug", "trace" * (capitalization not relevant) */ void setLevel(String level); String getLevel(); } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/logging/Log4JLogImpl.java0000644000175000017500000000666411647260573026336 0ustar moellermoellerpackage org.jgroups.logging; import org.apache.log4j.Level; import org.apache.log4j.Logger; /** * Logger that delivers messages to a Log4J logger * * @author Manik Surtani * @author Bela Ban * @since 2.8 */ public class Log4JLogImpl implements Log { private static final String FQCN = Log4JLogImpl.class.getName(); private final Logger logger; public Log4JLogImpl(String category) { logger = Logger.getLogger(category); } public Log4JLogImpl(Class category) { logger = Logger.getLogger(category); } public boolean isFatalEnabled() { return logger.isEnabledFor(Level.FATAL); } public boolean isErrorEnabled() { return logger.isEnabledFor(Level.ERROR); } public boolean isWarnEnabled() { return logger.isEnabledFor(Level.WARN); } public boolean isInfoEnabled() { return logger.isInfoEnabled(); } public boolean isDebugEnabled() { return logger.isDebugEnabled(); } public boolean isTraceEnabled() { return logger.isTraceEnabled(); } public void debug(String msg) { logger.log(FQCN, Level.DEBUG, msg, null); } public void debug(String msg, Throwable throwable) { logger.log(FQCN, Level.DEBUG, msg, throwable); } public void error(String msg) { logger.log(FQCN, Level.ERROR, msg, null); } public void error(String msg, Throwable throwable) { logger.log(FQCN, Level.ERROR, msg, throwable); } public void fatal(String msg) { logger.log(FQCN, Level.FATAL, msg, null); } public void fatal(String msg, Throwable throwable) { logger.log(FQCN, Level.FATAL, msg, throwable); } public void info(String msg) { logger.log(FQCN, Level.INFO, msg, null); } public void info(String msg, Throwable throwable) { logger.log(FQCN, Level.INFO, msg, throwable); } public void trace(Object msg) { logger.log(FQCN, Level.TRACE, msg, null); } public void trace(Object msg, Throwable throwable) { logger.log(FQCN, Level.TRACE, msg, throwable); } public void trace(String msg) { logger.log(FQCN, Level.TRACE, msg, null); } public void trace(String msg, Throwable throwable) { logger.log(FQCN, Level.TRACE, msg, throwable); } public void warn(String msg) { logger.log(FQCN, Level.WARN, msg, null); } public void warn(String msg, Throwable throwable) { logger.log(FQCN, Level.WARN, msg, throwable); } public String getLevel() { Level level = logger.getLevel(); return level != null ? level.toString() : "off"; } public void setLevel(String level) { Level new_level = strToLevel(level); if (new_level != null) logger.setLevel(new_level); } private static Level strToLevel(String level) { if (level == null) return null; level = level.toLowerCase().trim(); if (level.equals("fatal")) return Level.FATAL; if (level.equals("error")) return Level.ERROR; if (level.equals("warn")) return Level.WARN; if (level.equals("warning")) return Level.WARN; if (level.equals("info")) return Level.INFO; if (level.equals("debug")) return Level.DEBUG; if (level.equals("trace")) return Level.TRACE; return null; } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/logging/LogFactory.java0000644000175000017500000000417511647260573026177 0ustar moellermoellerpackage org.jgroups.logging; import org.jgroups.Global; /** * Factory that creates {@link Log} instances. * * @author Manik Surtani * @author Bela Ban * @since 4.0 */ public class LogFactory { public static final boolean IS_LOG4J_AVAILABLE; private static final CustomLogFactory custom_log_factory; static { String customLogFactoryClass=System.getProperty(Global.CUSTOM_LOG_FACTORY); CustomLogFactory customLogFactoryX=null; if(customLogFactoryClass != null) { try { // customLogFactoryX=(CustomLogFactory)Util.loadClass(customLogFactoryClass, null).newInstance(); customLogFactoryX=(CustomLogFactory)Class.forName(customLogFactoryClass).newInstance(); } catch(Exception e) { // failed to create the custom log factory, ignore? } } custom_log_factory=customLogFactoryX; boolean available; try { Class.forName("org.apache.log4j.Logger"); available=true; } catch(ClassNotFoundException cnfe) { available=false; } IS_LOG4J_AVAILABLE=available; } public static Log getLog(Class clazz) { if(custom_log_factory != null) return custom_log_factory.getLog(clazz); // this call is not executed frequently, so we don't need to move the check for USE_JDK_LOGGER to class init time final boolean use_jdk_logger=Boolean.parseBoolean(System.getProperty(Global.USE_JDK_LOGGER)); if(IS_LOG4J_AVAILABLE && !use_jdk_logger) { return new Log4JLogImpl(clazz); } else { return new JDKLogImpl(clazz); } } public static Log getLog(String category) { if(custom_log_factory != null) return custom_log_factory.getLog(category); final boolean use_jdk_logger=Boolean.parseBoolean(System.getProperty(Global.USE_JDK_LOGGER)); if(IS_LOG4J_AVAILABLE && !use_jdk_logger) { return new Log4JLogImpl(category); } else { return new JDKLogImpl(category); } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/mux/0000755000175000017500000000000011647260573022437 5ustar moellermoellerlibjgroups-java-2.12.2.Final.orig/src/org/jgroups/mux/Multiplexer.java0000644000175000017500000012614211647260573025622 0ustar moellermoellerpackage org.jgroups.mux; import org.jgroups.*; import org.jgroups.TimeoutException; import org.jgroups.annotations.Experimental; import org.jgroups.conf.ClassConfigurator; import org.jgroups.logging.Log; import org.jgroups.logging.LogFactory; 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 */ @Experimental(comment="because of impedance mismatches between a MuxChannel and JChannel, this might get deprecated " + "in the future. The replacement would be a shared transport (see the documentation for details)") @Deprecated 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 short ID=ClassConfigurator.getProtocolId(Multiplexer.class); /** * Map. Maintains the mapping between service IDs and * their associated MuxChannels */ private final ConcurrentMap services=Util.createConcurrentMap(); 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.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(ID); 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.BLOCK: passToAllMuxChannels(evt, true, true); return null; case Event.UNBLOCK: // process queued-up MergeViews passToAllMuxChannels(evt); 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"); } try { Util.shutdown(channel); } catch(Exception e) { e.printStackTrace(); } services.clear(); shutdownThreadPool(); } return all_closed; } Address getLocalAddress() { return channel.getAddress(); } boolean flushSupported() { return channel.flushSupported(); } boolean startFlush(boolean automatic_resume) { boolean result = Util.startFlush(channel); if(automatic_resume) channel.stopFlush(); return result; } 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.getAddress() + " is not connected, cannot send ServiceInfo." + ServiceInfo.typeToString(type) + " message"); } return; } Message service_msg=new Message(); service_msg.putHeader(ID, new MuxHeader(new ServiceInfo(type, service, host, payload))); if(oob) service_msg.setFlag(Message.OOB); if(channel.flushSupported()) service_msg.putHeader(ClassConfigurator.getProtocolId(FLUSH.class), 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.getAddress() + " 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.getAddress() + " 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(ID, 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 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(); } } } }libjgroups-java-2.12.2.Final.orig/src/org/jgroups/mux/MuxChannel.java0000644000175000017500000003234711647260573025355 0ustar moellermoellerpackage org.jgroups.mux; import org.jgroups.*; import org.jgroups.conf.ClassConfigurator; import org.jgroups.annotations.Experimental; 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 */ @Experimental(comment="because of impedance mismatches between a MuxChannel and JChannel, this might get deprecated " + "in the future. The replacement would be a shared transport (see the documentation for details)") @Deprecated public class MuxChannel extends JChannel { /* * Header identifier */ private static final short ID=ClassConfigurator.getProtocolId(MuxChannel.class); /* * 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 getAddress() { 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(ID, 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(ID, hdr); } mux.getChannel().down(evt); } public Object downcall(Event evt) { if(evt.getType() == Event.MSG) { Message msg=(Message)evt.getArg(); msg.putHeader(ID, 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=getAddress(); 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; return fetchState && mux.getState(target, my_id, timeout); } } 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); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/mux/MuxHeader.java0000644000175000017500000000320411647260573025163 0ustar moellermoellerpackage org.jgroups.mux; import org.jgroups.Global; import org.jgroups.Header; 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 */ public class MuxHeader extends Header { String id=null; /** Used for service state communication between Multiplexers */ ServiceInfo info; public MuxHeader() { } public MuxHeader(String id) { this.id=id; } public MuxHeader(ServiceInfo info) { this.info=info; } public String getId() { return id; } 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 ""; } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/mux/ServiceInfo.java0000644000175000017500000000762411647260573025527 0ustar moellermoellerpackage 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 */ 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"; } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/overview.html0000644000175000017500000000135311647260573024364 0ustar moellermoeller 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. libjgroups-java-2.12.2.Final.orig/src/org/jgroups/package.html0000644000175000017500000000015211647260573024105 0ustar moellermoeller Provides top-level public JGroups classes such as Channel, Message, etc. libjgroups-java-2.12.2.Final.orig/src/org/jgroups/persistence/0000755000175000017500000000000011647260573024152 5ustar moellermoellerlibjgroups-java-2.12.2.Final.orig/src/org/jgroups/persistence/CannotConnectException.java0000644000175000017500000000214111647260573031426 0ustar moellermoellerpackage 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; } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/persistence/CannotCreateSchemaException.java0000644000175000017500000000176411647260573032373 0ustar moellermoellerpackage 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; } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/persistence/CannotPersistException.java0000644000175000017500000000167111647260573031475 0ustar moellermoellerpackage 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; } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/persistence/CannotRemoveException.java0000644000175000017500000000166511647260573031304 0ustar moellermoellerpackage 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; } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/persistence/CannotRetrieveException.java0000644000175000017500000000170411647260573031626 0ustar moellermoellerpackage 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; } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/persistence/DBPersistenceManager.java0000644000175000017500000005277511647260573031022 0ustar moellermoellerpackage 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.jgroups.logging.Log; import org.jgroups.logging.LogFactory; import org.jgroups.annotations.Unsupported; import java.io.*; import java.sql.*; import java.util.*; /** * Class will be utilized */ @Unsupported 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 initialize complete DB access. This method will use * existing database to create schema (if it doesn't 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 default 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)"; } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/persistence/FilePersistenceManager.java0000644000175000017500000001135611647260573031402 0ustar moellermoellerpackage 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 org.jgroups.annotations.Unsupported; import java.io.*; import java.util.*; @Unsupported 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 libjgroups-java-2.12.2.Final.orig/src/org/jgroups/persistence/PersistenceFactory.java0000644000175000017500000001344611647260573030641 0ustar moellermoellerpackage 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.jgroups.logging.Log; import org.jgroups.logging.LogFactory; import org.jgroups.util.Util; import org.jgroups.annotations.Unsupported; import java.io.FileInputStream; import java.io.IOException; import java.lang.reflect.Constructor; import java.util.Properties; @Unsupported @Deprecated 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"; } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/persistence/PersistenceManager.java0000644000175000017500000000321011647260573030570 0ustar moellermoellerpackage 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 org.jgroups.annotations.Unsupported; import java.io.Serializable; import java.util.Map; @Deprecated @Unsupported 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(); } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/persistence/package.html0000644000175000017500000000027111647260573026433 0ustar moellermoeller Provides features for storing information to a database or file. Note that this package is experimental and has never been tested extensively libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/0000755000175000017500000000000011647260573023652 5ustar moellermoellerlibjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/AUTH.java0000644000175000017500000001620211647260573025257 0ustar moellermoellerpackage org.jgroups.protocols; import org.jgroups.Address; import org.jgroups.Event; import org.jgroups.Message; import org.jgroups.conf.ClassConfigurator; import org.jgroups.annotations.Property; import org.jgroups.auth.AuthToken; import org.jgroups.auth.X509Token; import org.jgroups.protocols.pbcast.GMS; import org.jgroups.protocols.pbcast.JoinRsp; import org.jgroups.stack.Protocol; import java.util.LinkedList; import java.util.List; /** * The AUTH protocol adds a layer of authentication to JGroups * @author Chris Mills * @autho Bela Ban */ public class AUTH extends Protocol { /** * used on the coordinator to authentication joining member requests against */ private AuthToken auth_plugin=null; private static final short gms_id=ClassConfigurator.getProtocolId(GMS.class); public AUTH() { name="AUTH"; } @Property(name="auth_class") public void setAuthClass(String class_name) throws Exception { Object obj=Class.forName(class_name).newInstance(); auth_plugin=(AuthToken)obj; auth_plugin.setAuth(this); } public String getAuthClass() {return auth_plugin != null? auth_plugin.getClass().getName() : null;} protected List getConfigurableObjects() { List retval=new LinkedList(); if(auth_plugin != null) retval.add(auth_plugin); return retval; } public void init() throws Exception { super.init(); if(auth_plugin instanceof X509Token) { X509Token tmp=(X509Token)auth_plugin; tmp.setCertificate(); } auth_plugin.init(); } /** * 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=getGMSHeader(evt); if(hdr == null) return up_prot.up(evt); if(hdr.getType() == GMS.GmsHeader.JOIN_REQ || hdr.getType() == GMS.GmsHeader.JOIN_REQ_WITH_STATE_TRANSFER || hdr.getType() == GMS.GmsHeader.MERGE_REQ) { // we found a join or merge message - now try and get the AUTH Header Message msg=(Message)evt.getArg(); if((msg.getHeader(this.id) != null) && (msg.getHeader(this.id) instanceof AuthHeader)) { AuthHeader authHeader=(AuthHeader)msg.getHeader(this.id); if(authHeader != null) { //Now we have the AUTH Header we need to validate it if(this.auth_plugin.authenticate(authHeader.getToken(), msg)) { return up_prot.up(evt); } else { if(log.isWarnEnabled()) log.warn("failed to validate AuthHeader token"); sendRejectionMessage(hdr.getType(), msg.getSrc(), "Authentication failed"); return null; } } else { //Invalid AUTH Header - need to send failure message if(log.isWarnEnabled()) log.warn("AUTH failed to get valid AuthHeader from Message"); sendRejectionMessage(hdr.getType(), msg.getSrc(), "Failed to find valid AuthHeader in Message"); return null; } } else { sendRejectionMessage(hdr.getType(), msg.getSrc(), "Failed to find an AuthHeader in Message"); return null; } } return up_prot.up(evt); } protected void sendRejectionMessage(byte type, Address dest, String error_msg) { switch(type) { case GMS.GmsHeader.JOIN_REQ: case GMS.GmsHeader.JOIN_REQ_WITH_STATE_TRANSFER: sendJoinRejectionMessage(dest, error_msg); break; case GMS.GmsHeader.MERGE_REQ: sendMergeRejectionMessage(dest, error_msg); break; default: log.error("type " + type + " unknown"); break; } } protected void sendJoinRejectionMessage(Address dest, String error_msg) { if(dest == null) return; Message msg = new Message(dest, null, null); JoinRsp joinRes=new JoinRsp(error_msg); // specify the error message on the JoinRsp GMS.GmsHeader gmsHeader=new GMS.GmsHeader(GMS.GmsHeader.JOIN_RSP, joinRes); msg.putHeader(gms_id, gmsHeader); down_prot.down(new Event(Event.MSG, msg)); } protected void sendMergeRejectionMessage(Address dest, String error_msg) { Message msg=new Message(dest, null, null); msg.setFlag(Message.OOB); GMS.GmsHeader hdr=new GMS.GmsHeader(GMS.GmsHeader.MERGE_RSP); hdr.setMergeRejected(true); msg.putHeader(gms_id, hdr); if(log.isDebugEnabled()) log.debug("merge response=" + hdr); down_prot.down(new Event(Event.MSG, msg)); } /** * 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 = getGMSHeader(evt); if((hdr != null) && (hdr.getType() == GMS.GmsHeader.JOIN_REQ || hdr.getType() == GMS.GmsHeader.JOIN_REQ_WITH_STATE_TRANSFER || hdr.getType() == GMS.GmsHeader.MERGE_REQ)) { //we found a join request message - now add an AUTH Header Message msg = (Message)evt.getArg(); AuthHeader authHeader = new AuthHeader(); authHeader.setToken(this.auth_plugin); msg.putHeader(this.id, authHeader); } return down_prot.down(evt); } /** * Get the header from 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 getGMSHeader(Event evt){ Message msg; switch(evt.getType()){ case Event.MSG: msg = (Message)evt.getArg(); Object obj = msg.getHeader(gms_id); if(obj == null || !(obj instanceof GMS.GmsHeader)){ return null; } return (GMS.GmsHeader)obj; } return null; } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/AuthHeader.java0000644000175000017500000000217711647260573026536 0ustar moellermoellerpackage org.jgroups.protocols; import org.jgroups.Header; import org.jgroups.auth.AuthToken; 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 { private AuthToken token=null; 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 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); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/BARRIER.java0000644000175000017500000001710211647260573025604 0ustar moellermoellerpackage org.jgroups.protocols; import org.jgroups.Event; import org.jgroups.Message; import org.jgroups.annotations.MBean; import org.jgroups.annotations.ManagedAttribute; import org.jgroups.annotations.Property; import org.jgroups.stack.Protocol; import org.jgroups.util.TimeScheduler; import org.jgroups.util.Util; import java.util.HashSet; import java.util.Iterator; import java.util.Set; 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 */ @MBean(description="Blocks all multicast threads when closed") public class BARRIER extends Protocol { @Property(description="Max time barrier can be closed. Default is 60000 ms") 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=Util.createConcurrentMap(); Future barrier_opener_future=null; TimeScheduler timer; private static final Object NULL=new Object(); @ManagedAttribute public boolean isClosed() { return barrier_closed.get(); } public int getNumberOfInFlightThreads() { return in_flight_threads.size(); } @ManagedAttribute public int getInFlightThreadsCount() { return getNumberOfInFlightThreads(); } @ManagedAttribute public boolean isOpenerScheduled() { return barrier_opener_future != null && !barrier_opener_future.isDone() && !barrier_opener_future.isCancelled(); } 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: Message msg=(Message)evt.getArg(); if(msg.getDest() != null) // https://issues.jboss.org/browse/JGRP-1341: let unicast messages pass return up_prot.up(evt); 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 { if(in_flight_threads.remove(current_thread) == NULL && barrier_closed.get() && in_flight_threads.isEmpty()) { lock.lock(); try { 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; } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/BPING.java0000644000175000017500000001252111647260573025355 0ustar moellermoellerpackage org.jgroups.protocols; import org.jgroups.Event; import org.jgroups.Global; import org.jgroups.Message; import org.jgroups.annotations.Experimental; import org.jgroups.annotations.Property; import org.jgroups.util.Buffer; import org.jgroups.util.ExposedByteArrayInputStream; import org.jgroups.util.ExposedByteArrayOutputStream; import org.jgroups.util.Util; import java.io.ByteArrayInputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.net.SocketException; /** * Broadcast PING. Uses UDP broadcasts to discover initial membership. This protocol is useless in IPv6 environments, as * IPv6 has no notion of broadcast addresses. Use IP multicasts instead (e.g. PING or MPING) when running in IPv6. * @author Bela Ban * @since 2.12 */ @Experimental public class BPING extends PING implements Runnable { /* ----------------------------------------- Properties -------------------------------------------------- */ @Property(description="Target address for broadcasts. This should be restricted to the local subnet, e.g. 192.168.1.255") protected String dest="255.255.255.255"; @Property(description="Port for discovery packets", systemProperty=Global.BPING_BIND_PORT) protected int bind_port=8555; @Property(description="Sends discovery packets to ports 8555 to (8555+port_range)") protected int port_range=5; /* --------------------------------------------- Fields ------------------------------------------------------ */ protected DatagramSocket sock=null; protected volatile Thread receiver=null; protected InetAddress dest_addr; public BPING() { } public int getBindPort() { return bind_port; } public void setBindPort(int bind_port) { this.bind_port=bind_port; } public void init() throws Exception { super.init(); dest_addr=InetAddress.getByName(dest); if(log.isDebugEnabled()) log.debug("listening on " + bind_port); } public void start() throws Exception { for(int i=bind_port; i < bind_port+port_range; i++) { try { sock=getSocketFactory().createDatagramSocket(Global.BPING_SOCK, i); break; } catch(Throwable t) { if(i >= bind_port+port_range-1) throw new RuntimeException("failed to open a port in range [" + bind_port + " - " + (bind_port+port_range) + "]", t); } } sock.setBroadcast(true); startReceiver(); super.start(); } 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(sock); sock=null; receiver=null; super.stop(); } void sendMcastDiscoveryRequest(Message msg) { DataOutputStream out=null; try { if(msg.getSrc() == null) msg.setSrc(local_addr); ExposedByteArrayOutputStream out_stream=new ExposedByteArrayOutputStream(128); out=new DataOutputStream(out_stream); msg.writeTo(out); out.flush(); // flushes contents to out_stream Buffer buf=new Buffer(out_stream.getRawBuffer(), 0, out_stream.size()); discovery_reception.reset(); for(int i=bind_port; i < bind_port+port_range; i++) { DatagramPacket packet=new DatagramPacket(buf.getBuf(), buf.getOffset(), buf.getLength(), dest_addr, i); sock.send(packet); } waitForDiscoveryRequestReception(); } catch(IOException ex) { log.error("failed sending discovery request", ex); } finally { Util.close(out); } } public void run() { final byte[] receive_buf=new byte[65535]; DatagramPacket packet=new DatagramPacket(receive_buf, receive_buf.length); byte[] data; ByteArrayInputStream inp_stream; DataInputStream inp=null; Message msg; while(sock != null && receiver != null && Thread.currentThread().equals(receiver)) { packet.setData(receive_buf, 0, receive_buf.length); try { sock.receive(packet); data=packet.getData(); inp_stream=new ExposedByteArrayInputStream(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 { Util.close(inp); } } if(log.isTraceEnabled()) log.trace("receiver thread terminated"); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/BSH.java0000644000175000017500000001031311647260573025127 0ustar moellermoeller package org.jgroups.protocols; import bsh.EvalError; import bsh.Interpreter; import org.jgroups.annotations.Experimental; import org.jgroups.annotations.Property; import org.jgroups.annotations.Unsupported; import org.jgroups.stack.Protocol; import org.jgroups.util.Util; import org.jgroups.Global; import java.io.*; import java.net.ServerSocket; import java.net.Socket; import java.util.ArrayList; import java.util.List; /** * Beanshell (www.beanshell.org) interpreter class. * The eval() method receives Java code, executes it and returns the * result of the evaluation (or an exception).

* This protocol is experimental * User: Bela * Date: Mar 8, 2003 * Time: 1:57:07 PM * @author Bela Ban */ @Experimental @Unsupported public class BSH extends Protocol implements Runnable { protected Interpreter interpreter=null; protected ServerSocket srv_sock; protected Thread acceptor; protected final List sockets=new ArrayList(); @Property(description="Port on which the interpreter should listen for requests. 0 is an ephemeral port") int bind_port=0; public BSH() { } public void start() throws Exception { srv_sock=Util.createServerSocket(getSocketFactory(), Global.BSH_SRV_SOCK, null, bind_port); log.info("Server socket listening at " + srv_sock.getLocalSocketAddress()); acceptor=new Thread(this); acceptor.start(); } public void stop() { Util.close(srv_sock); if(acceptor != null && acceptor.isAlive()) acceptor.interrupt(); Util.sleep(500); if(!sockets.isEmpty()) { for(Socket sock: sockets) Util.close(sock); } } public void run() { while(srv_sock != null && !srv_sock.isClosed()) { try { final Socket sock=srv_sock.accept(); sockets.add(sock); createInterpreter(); new Thread() { public void run() { try { InputStream input=sock.getInputStream(); OutputStream out=sock.getOutputStream(); BufferedReader reader=new BufferedReader(new InputStreamReader(input)); while(!sock.isClosed()) { String line=reader.readLine(); if(line == null || line.length() == 0) continue; try { Object retval=interpreter.eval(line); if(retval != null) { String rsp=retval.toString(); byte[] buf=rsp.getBytes(); out.write(buf, 0, buf.length); out.flush(); } if(log.isTraceEnabled()) { log.trace(line); if(retval != null) log.trace(retval); } } catch(EvalError evalError) { evalError.printStackTrace(); } } } catch(IOException e) { e.printStackTrace(); } finally { Util.close(sock); sockets.remove(sock); } } }.start(); } catch(IOException e) { } } } synchronized void createInterpreter() { // create interpreter just-in-time if(interpreter == null) { interpreter=new Interpreter(); try { interpreter.set("bsh_prot", this); } catch(EvalError evalError) { } } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/BasicTCP.java0000644000175000017500000001200611647260573026104 0ustar moellermoellerpackage org.jgroups.protocols; import org.jgroups.Address; import org.jgroups.Event; import org.jgroups.PhysicalAddress; import org.jgroups.annotations.DeprecatedProperty; import org.jgroups.annotations.Property; import org.jgroups.stack.Protocol; import java.net.InetAddress; import java.util.Collection; import java.util.HashSet; import java.util.Set; /** * Shared base class for TCP protocols * @author Scott Marlow */ @DeprecatedProperty(names={"suspect_on_send_failure", "skip_suspected_members"}) public abstract class BasicTCP extends TP { /* ----------------------------------------- Properties -------------------------------------------------- */ @Property(description="Reaper interval in msec. Default is 0 (no reaping)") protected long reaper_interval=0; // time in msecs between connection reaps @Property(description="Max time connection can be idle before being reaped (in ms)") protected long conn_expire_time=0; // max time a conn can be idle before being reaped @Property(description="Should separate send queues be used for each connection") boolean use_send_queues=true; @Property(description="Max number of messages in a send queue") int send_queue_size=10000; @Property(description="Receiver buffer size in bytes") int recv_buf_size=150000; @Property(description="Send buffer size in bytes") int send_buf_size=150000; @Property(description="Max time allowed for a socket creation in connection table") int sock_conn_timeout=2000; // max time in millis for a socket creation in connection table @Property(description="Max time to block on reading of peer address") int peer_addr_read_timeout=1000; // max time to block on reading of peer address @Property(description="Should TCP no delay flag be turned on") boolean tcp_nodelay=true; @Property(description="SO_LINGER in msec. Default of -1 disables it") int linger=-1; // SO_LINGER (number of ms, -1 disables it) @Property(description="Use \"external_addr\" if you have hosts on different networks, behind " + "firewalls. On each firewall, set up a port forwarding rule (sometimes called \"virtual server\") to " + "the local IP (e.g. 192.168.1.100) of the host then on each host, set \"external_addr\" TCP transport " + "parameter to the external (public IP) address of the firewall.") InetAddress external_addr = null ; /* --------------------------------------------- Fields ------------------------------------------------------ */ protected BasicTCP() { super(); } public boolean supportsMulticasting() { return false; } 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 void init() throws Exception { super.init(); if(!isSingleton() && bind_port <= 0) { Discovery discovery_prot=(Discovery)stack.findProtocol(Discovery.class); if(discovery_prot != null && !discovery_prot.isDynamic()) throw new IllegalArgumentException("start_port cannot be set to " + bind_port + ", as no dynamic discovery protocol (e.g. MPING or TCPGOSSIP) has been detected."); } } public void sendMulticast(byte[] data, int offset, int length) throws Exception { sendToAllPhysicalAddresses(data, offset, length); } public void sendUnicast(PhysicalAddress dest, byte[] data, int offset, int length) throws Exception { if(log.isTraceEnabled()) log.trace("dest=" + dest + " (" + length + " bytes)"); send(dest, data, offset, length); } public String getInfo() { StringBuilder sb=new StringBuilder(); sb.append("connections: ").append(printConnections()).append("\n"); return sb.toString(); } public abstract String printConnections(); public abstract void send(Address dest, byte[] data, int offset, int length) throws Exception; public abstract void retainAll(Collection

members); /** ConnectionMap.Receiver interface */ public void receive(Address sender, byte[] data, int offset, int length) { super.receive(sender, data, offset, length); } protected Object handleDownEvent(Event evt) { Object ret=super.handleDownEvent(evt); if(evt.getType() == Event.VIEW_CHANGE) { Set
physical_mbrs=new HashSet
(); for(Address addr: members) { PhysicalAddress physical_addr=getPhysicalAddressFromCache(addr); if(physical_addr != null) physical_mbrs.add(physical_addr); } retainAll(physical_mbrs); // remove all connections which are not members } return ret; } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/CENTRAL_EXECUTOR.java0000644000175000017500000001725711647260573027137 0ustar moellermoellerpackage org.jgroups.protocols; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import org.jgroups.Address; import org.jgroups.View; import org.jgroups.annotations.Experimental; import org.jgroups.annotations.ManagedAttribute; import org.jgroups.annotations.Property; import org.jgroups.util.Util; /** * This is a central executor service where each request is sent to the coordinator * for either a task or a current waiting thread. * * @author wburns * @since 2.12.0 */ @Experimental public class CENTRAL_EXECUTOR extends Executing { @Property(description="Number of backups to the coordinator. Queue State gets replicated to these nodes as well") protected int num_backups=1; protected Address coord; @ManagedAttribute protected boolean is_coord; protected final List
backups=new ArrayList
(); public CENTRAL_EXECUTOR() { super(); } public Address getCoord() { return coord; } public boolean isCoord() { return is_coord; } @ManagedAttribute public String getCoordinator() { return coord != null? coord.toString() : "n/a"; } public int getNumberOfBackups() { return num_backups; } public void setNumberOfBackups(int num_backups) { this.num_backups=num_backups; } @ManagedAttribute public String getBackups() { return backups != null? backups.toString() : null; } public void handleView(View view) { Address oldCoord = coord; if(view.size() > 0) { coord=view.getMembers().firstElement(); is_coord=coord.equals(local_addr); if(log.isDebugEnabled()) log.debug("local_addr=" + local_addr + ", coord=" + coord + ", is_coord=" + is_coord); } // If we got a new coordinator we have to send all the requests for // tasks and consumers again just incase they were missed when // coordinator went down // We are okay with duplicates since we don't add multiple times. // We also have a problem that if a task/consumer was picked up as the // consumer is changing we may have duplicates. But this is technically // okay in that an extra consumer will reject and an extra task will just // be ran and return nowhere, but at least we won't lose data. if (oldCoord != coord) { for (Long requests : _requestId.values()) { sendToCoordinator(Type.RUN_REQUEST, requests, local_addr); } for (Long requests : _consumerId.keySet()) { sendToCoordinator(Type.CONSUMER_READY, requests, local_addr); } } if(num_backups > 0) { if (is_coord) { List
new_backups=Util.pickNext(view.getMembers(), local_addr, num_backups); List
new_members=null; synchronized(backups) { if(!backups.equals(new_backups)) { new_members=new ArrayList
(new_backups); new_members.removeAll(backups); backups.clear(); backups.addAll(new_backups); } } if(new_members != null && !new_members.isEmpty()) copyQueueTo(new_members); } // We keep what backups we have ourselves, so that when we become // the coordinator we don't update them again. Technically we can // send multiple requests but don't if to prevent more message being // sent. else { List
possiblebackups = Util.pickNext(view.getMembers(), coord, num_backups); boolean foundMyself = false; List
myBackups = new ArrayList
(); for (Address backup : possiblebackups) { if (foundMyself) { myBackups.add(backup); } else if (backup.equals(local_addr)) { foundMyself = true; } } synchronized (backups) { backups.clear(); backups.addAll(myBackups); } } } // Need to run this last so the backups are updated super.handleView(view); } protected void updateBackups(Type type, Owner obj) { synchronized(backups) { for(Address backup: backups) sendRequest(backup, type, obj.getRequestId(), obj.getAddress()); } } protected void copyQueueTo(List
new_joiners) { Set copyRequests; Set copyConsumers; _consumerLock.lock(); try { copyRequests = new HashSet(_runRequests); copyConsumers = new HashSet(_consumersAvailable); } finally { _consumerLock.unlock(); } if(log.isTraceEnabled()) log.trace("copying queue to " + new_joiners); for(Address joiner: new_joiners) { for(Owner address: copyRequests) { sendRequest(joiner, Type.CREATE_RUN_REQUEST, address.getRequestId(), address.getAddress()); } for(Owner address: copyConsumers) { sendRequest(joiner, Type.CREATE_CONSUMER_READY, address.getRequestId(), address.getAddress()); } } } // @see org.jgroups.protocols.Executing#sendToCoordinator(org.jgroups.protocols.Executing.Type, long, org.jgroups.Address) @Override protected void sendToCoordinator(Type type, final long requestId, final Address value) { if (is_coord) { if(log.isTraceEnabled()) log.trace("[redirect] <--> [" + local_addr + "] " + type.name() + " [" + value + (requestId != -1 ? " request id: " + requestId : "") + "]"); switch(type) { case RUN_REQUEST: handleTaskRequest(requestId, value); break; case CONSUMER_READY: handleConsumerReadyRequest(requestId, value); break; case CONSUMER_UNREADY: handleConsumerUnreadyRequest(requestId, value); break; }; } else sendRequest(coord, type, requestId, value); } // @see org.jgroups.protocols.Executing#sendNewRunRequest(org.jgroups.protocols.Executing.Owner) @Override protected void sendNewRunRequest(Owner sender) { if(is_coord) updateBackups(Type.CREATE_RUN_REQUEST, sender); } // @see org.jgroups.protocols.Executing#sendRemoveRunRequest(org.jgroups.protocols.Executing.Owner) @Override protected void sendRemoveRunRequest(Owner sender) { if(is_coord) updateBackups(Type.DELETE_RUN_REQUEST, sender); } // @see org.jgroups.protocols.Executing#sendNewConsumerRequest(org.jgroups.protocols.Executing.Owner) @Override protected void sendNewConsumerRequest(Owner sender) { if(is_coord) updateBackups(Type.CREATE_CONSUMER_READY, sender); } // @see org.jgroups.protocols.Executing#sendRemoveConsumerRequest(org.jgroups.protocols.Executing.Owner) @Override protected void sendRemoveConsumerRequest(Owner sender) { if(is_coord) updateBackups(Type.DELETE_CONSUMER_READY, sender); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/CENTRAL_LOCK.java0000644000175000017500000001672611647260573026431 0ustar moellermoellerpackage org.jgroups.protocols; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Queue; import org.jgroups.Address; import org.jgroups.View; import org.jgroups.annotations.Experimental; import org.jgroups.annotations.ManagedAttribute; import org.jgroups.annotations.Property; import org.jgroups.blocks.locking.LockNotification; import org.jgroups.blocks.locking.Owner; import org.jgroups.util.Util; /** * Implementation of a locking protocol which acquires locks by contacting the coordinator.

Because the * coordinator maintains all locks, no total order configuration is required.

* CENTRAL_LOCK has all members send lock and unlock requests to a central coordinator. The coordinator has a queue for * incoming requests, and grants locks based on order of arrival. To prevent all acquired locks from being forgotten * when the coordinator crashes, setting num_backups lets the coordinator backup lock information to a number of * backup nodes. Valid values for num_backups are 0 (no backup) to N-1, e.g. in a cluster of 4, we can have only 3 backup * nodes.

* Say we have a cluster of {A,B,C,D,E} and num_backups=1. A is the coordinator, and A updates all locks (and released * locks) in B as well. When A crashes, everybody falls over to B for sending lock and unlock requests. * B in turn copies all existing locks over to C and - when locks are acquired or released - forwards this * information to C as well. *

* An alternative is also the {@link org.jgroups.protocols.PEER_LOCK} protocol. * @author Bela Ban * @since 2.12 * @see Locking * @see PEER_LOCK */ @Experimental public class CENTRAL_LOCK extends Locking implements LockNotification { @Property(description="Number of backups to the coordinator. Server locks get replicated to these nodes as well") protected int num_backups=1; protected Address coord; @ManagedAttribute protected boolean is_coord; protected final List

backups=new ArrayList
(); public CENTRAL_LOCK() { super(); addLockListener(this); } public Address getCoord() { return coord; } public boolean isCoord() { return is_coord; } @ManagedAttribute public String getCoordinator() { return coord != null? coord.toString() : "n/a"; } public int getNumberOfBackups() { return num_backups; } public void setNumberOfBackups(int num_backups) { this.num_backups=num_backups; } @ManagedAttribute public String getBackups() { return backups != null? backups.toString() : null; } protected void sendGrantLockRequest(String lock_name, Owner owner, long timeout, boolean is_trylock) { if(coord != null) sendRequest(coord, Type.GRANT_LOCK, lock_name, owner, timeout, is_trylock); } protected void sendReleaseLockRequest(String lock_name, Owner owner) { if(coord != null) sendRequest(coord, Type.RELEASE_LOCK, lock_name, owner, 0, false); } protected void sendCreateLockRequest(Address dest, String lock_name, Owner owner) { sendRequest(dest, Type.CREATE_LOCK, lock_name, owner, 0, false); } protected void sendDeleteLockRequest(Address dest, String lock_name) { sendRequest(dest, Type.DELETE_LOCK, lock_name, null, 0, false); } @Override protected void sendAwaitConditionRequest(String lock_name, Owner owner) { sendRequest(coord, Type.LOCK_AWAIT, lock_name, owner, 0, false); } @Override protected void sendSignalConditionRequest(String lock_name, boolean all) { sendRequest(coord, all ? Type.COND_SIG_ALL : Type.COND_SIG, lock_name, null, 0, false); } @Override protected void sendDeleteAwaitConditionRequest(String lock_name, Owner owner) { sendRequest(coord, Type.DELETE_LOCK_AWAIT, lock_name, owner, 0, false); } public void handleView(View view) { super.handleView(view); Address old_coord=coord; if(view.size() > 0) { coord=view.getMembers().firstElement(); is_coord=coord.equals(local_addr); if(log.isDebugEnabled()) log.debug("local_addr=" + local_addr + ", coord=" + coord + ", is_coord=" + is_coord); } if(is_coord && num_backups > 0) { List
new_backups=Util.pickNext(view.getMembers(), local_addr, num_backups); List
copy_locks_list=null; synchronized(backups) { if(!backups.equals(new_backups)) { copy_locks_list=new ArrayList
(new_backups); copy_locks_list.removeAll(backups); backups.clear(); backups.addAll(new_backups); } } if(copy_locks_list != null && !copy_locks_list.isEmpty()) copyLocksTo(copy_locks_list); } // For all non-acquired client locks, send the GRANT_LOCK request to the new coordinator (if changed) if(old_coord != null && !old_coord.equals(coord)) { Map> copy; synchronized(client_locks) { copy=new HashMap>(client_locks); } if(!copy.isEmpty()) { for(Map map: copy.values()) { for(ClientLock lock: map.values()) { if(!lock.acquired && !lock.denied) sendGrantLockRequest(lock.name, lock.owner, lock.timeout, lock.is_trylock); } } } } } public void lockCreated(String name) { } public void lockDeleted(String name) { } public void locked(String lock_name, Owner owner) { if(is_coord) updateBackups(Type.CREATE_LOCK, lock_name, owner); } public void unlocked(String lock_name, Owner owner) { if(is_coord) updateBackups(Type.DELETE_LOCK, lock_name, owner); } public void awaiting(String lock_name, Owner owner) { if(is_coord) updateBackups(Type.CREATE_AWAITER, lock_name, owner); } public void awaited(String lock_name, Owner owner) { if(is_coord) updateBackups(Type.DELETE_AWAITER, lock_name, owner); } protected void updateBackups(Type type, String lock_name, Owner owner) { synchronized(backups) { for(Address backup: backups) sendRequest(backup, type, lock_name, owner, 0, false); } } protected void copyLocksTo(List
new_joiners) { Map copy; synchronized(server_locks) { copy=new HashMap(server_locks); } if(log.isTraceEnabled()) log.trace("copying locks to " + new_joiners); for(Map.Entry entry: copy.entrySet()) { for(Address joiner: new_joiners) { ServerLock lock = entry.getValue(); if (lock.current_owner != null) { sendCreateLockRequest(joiner, entry.getKey(), entry.getValue().current_owner); } synchronized (lock.condition) { Queue queue = lock.condition.queue; for (Owner owner : queue) { sendAwaitConditionRequest(lock.lock_name, owner); } } } } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/COMPRESS.java0000755000175000017500000001654211647260573025763 0ustar moellermoellerpackage org.jgroups.protocols; import org.jgroups.Event; import org.jgroups.Global; import org.jgroups.Header; import org.jgroups.Message; import org.jgroups.annotations.MBean; import org.jgroups.annotations.Property; import org.jgroups.stack.Protocol; import java.io.*; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; 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 */ @MBean(description="Compresses messages to send and uncompresses received messages") public class COMPRESS extends Protocol { /* ----------------------------------------- Properties -------------------------------------------------- */ @Property(description="Compression level 0-9 (0=no compression, 9=best compression). Default is 9") private int compression_level=Deflater.BEST_COMPRESSION; // this is 9 @Property(description="Minimal payload size of a message (in bytes) for compression to kick in. Default is 500 bytes") private long min_size=500; @Property(description="Number of inflaters/deflaters for concurrent processing. Default is 2 ") private int pool_size=2; /* --------------------------------------------- Fields ------------------------------------------------------ */ BlockingQueue deflater_pool=null; BlockingQueue inflater_pool=null; public COMPRESS() { } 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(); } /** * 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(this.id, 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(this.id); 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 { int original_size=0; public CompressHeader() { super(); } public CompressHeader(int s) { original_size=s; } public int size() { return Global.INT_SIZE; } public void writeTo(DataOutputStream out) throws IOException { out.writeInt(original_size); } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { original_size=in.readInt(); } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/DAISYCHAIN.java0000644000175000017500000002055411647260573026137 0ustar moellermoellerpackage org.jgroups.protocols; import org.jgroups.*; import org.jgroups.annotations.*; import org.jgroups.stack.Protocol; import org.jgroups.util.ConcurrentLinkedBlockingQueue; import org.jgroups.util.Util; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.util.concurrent.BlockingQueue; import java.util.concurrent.Executor; /** * Implementation of daisy chaining. Multicast messages are sent to our neighbor, which sends them to its neighbor etc. * A TTL restricts the number of times a message is forwarded. The advantage of daisy chaining is that - for * point-to-point transports such as TCP - we can avoid the N-1 issue: when A sends a multicast message to 10 * members, it needs to send it 9 times. With daisy chaining, it sends it 1 time, and in the next round, can already * send another message. This leads to much better throughput, see the ref in the JIRA.

* Should be inserted just above MERGE2, in TCP based configurations. * JIRA: https://jira.jboss.org/browse/JGRP-1021 * @author Bela Ban * @since 2.11 */ @Experimental @MBean(description="Protocol just above the transport which disseminates multicasts via daisy chaining") public class DAISYCHAIN extends Protocol { /* ----------------------------------------- Properties -------------------------------------------------- */ @Property(description="Loop back multicast messages") boolean loopback=true; @Property(description="The number of messages in the forward queue. This queue is used to host messages that " + "need to be forwarded by us on behalf of our neighbor") int forward_queue_size=10000; @Property(description="The number of messages in the send queue. This queue is used to host messages that need " + "to be sent") int send_queue_size=10000; /* --------------------------------------------- Fields ------------------------------------------------------ */ protected Address local_addr, next; protected int view_size=0; protected Executor default_pool=null; protected Executor oob_pool=null; protected BlockingQueue send_queue; protected BlockingQueue forward_queue; protected volatile boolean forward=false; // flipped between true and false, to ensure fairness protected volatile boolean running=true; @ManagedAttribute public int msgs_forwarded=0; @ManagedAttribute public int msgs_sent=0; @ManagedAttribute public int getElementsInForwardQueue() {return forward_queue.size();} @ManagedAttribute public int getElementsInSendQueue() {return send_queue.size();} public void init() throws Exception { default_pool=getTransport().getDefaultThreadPool(); oob_pool=getTransport().getOOBThreadPool(); send_queue=new ConcurrentLinkedBlockingQueue(send_queue_size); forward_queue=new ConcurrentLinkedBlockingQueue(forward_queue_size); } public void start() throws Exception { super.start(); running=true; } public void stop() { super.stop(); running=false; } public Object down(final Event evt) { switch(evt.getType()) { case Event.MSG: final Message msg=(Message)evt.getArg(); Address dest=msg.getDest(); if(dest != null && !dest.isMulticastAddress()) break; // only process multicast messages if(next == null) // view hasn't been received yet, use the normal transport break; // we need to copy the message, as we cannot do a msg.setSrc(next): the next retransmission // would use 'next' as destination ! Message copy=msg.copy(true); short hdr_ttl=(short)(loopback? view_size -1 : view_size); DaisyHeader hdr=new DaisyHeader(hdr_ttl); copy.setDest(next); copy.putHeader(getId(), hdr); try { msgs_sent++; send_queue.put(copy); } catch(InterruptedException e) { Thread.currentThread().interrupt(); return null; } if(loopback) { if(log.isTraceEnabled()) log.trace(new StringBuilder("looping back message ").append(msg)); if(msg.getSrc() == null) msg.setSrc(local_addr); Executor pool=msg.isFlagSet(Message.OOB)? oob_pool : default_pool; pool.execute(new Runnable() { public void run() { up_prot.up(evt); } }); } return processQueues(); case Event.VIEW_CHANGE: handleView((View)evt.getArg()); break; case Event.TMP_VIEW: view_size=((View)evt.getArg()).size(); break; case Event.SET_LOCAL_ADDRESS: local_addr=(Address)evt.getArg(); break; } return down_prot.down(evt); } public Object up(Event evt) { switch(evt.getType()) { case Event.MSG: Message msg=(Message)evt.getArg(); DaisyHeader hdr=(DaisyHeader)msg.getHeader(getId()); if(hdr == null) break; // 1. forward the message to the next in line if ttl > 0 short ttl=hdr.getTTL(); if(log.isTraceEnabled()) log.trace(local_addr + ": received message from " + msg.getSrc() + " with ttl=" + ttl); if(--ttl > 0) { Message copy=msg.copy(true); copy.setDest(next); copy.putHeader(getId(), new DaisyHeader(ttl)); msgs_forwarded++; if(forward_queue.offer(copy)) // we don't want incoming threads to block processQueues(); } // 2. Pass up msg.setDest(null); break; } return up_prot.up(evt); } protected Object processQueues() { int cnt=0; while(running && cnt++ < 10000) { // cnt is a second line of defense against loops and should never be used ! try { Message msg=forward? forward_queue.poll() : send_queue.poll(); if(msg == null) { msg=forward? send_queue.poll() : forward_queue.poll(); if(msg == null) continue; } if(log.isTraceEnabled()) { DaisyHeader hdr=(DaisyHeader)msg.getHeader(getId()); log.trace(local_addr + ": " + (forward? " forwarding" : " sending") + " message with ttl=" + hdr.getTTL() + " to " + next); } return down_prot.down(new Event(Event.MSG, msg)); } catch(Throwable t) { log.error("failed sending message down", t); return null; } finally { forward=!forward; } } return null; } protected void handleView(View view) { view_size=view.size(); Address tmp=Util.pickNext(view.getMembers(), local_addr); if(tmp != null && !tmp.equals(local_addr)) { next=tmp; if(log.isDebugEnabled()) log.debug("next=" + next); } } public static class DaisyHeader extends Header { private short ttl; public DaisyHeader() { } public DaisyHeader(short ttl) { this.ttl=ttl; } public short getTTL() {return ttl;} public void setTTL(short ttl) { this.ttl=ttl; } public int size() { return Global.SHORT_SIZE; } public void writeTo(DataOutputStream out) throws IOException { out.writeShort(ttl); } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { ttl=in.readShort(); } public String toString() { return "ttl=" + ttl; } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/DELAY.java0000644000175000017500000000403611647260573025356 0ustar moellermoeller package org.jgroups.protocols; import org.jgroups.Event; import org.jgroups.annotations.Property; import org.jgroups.annotations.Unsupported; import org.jgroups.stack.Protocol; import org.jgroups.util.Util; /** * 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). */ @Unsupported public class DELAY extends Protocol { @Property int in_delay=0; @Property int out_delay=0; public int getInDelay() { return in_delay ; } public void setInDelay(int in_delay) { this.in_delay=in_delay ; } public int getOutDelay() { return out_delay ; } public void setOutDelay(int out_delay) { this.out_delay=out_delay ; } 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); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/DELAY_JOIN_REQ.java0000644000175000017500000000356311647260573026710 0ustar moellermoellerpackage org.jgroups.protocols; import org.jgroups.Event; import org.jgroups.Message; import org.jgroups.conf.ClassConfigurator; import org.jgroups.annotations.Property; import org.jgroups.annotations.Unsupported; import org.jgroups.protocols.pbcast.GMS; import org.jgroups.stack.Protocol; import org.jgroups.util.Util; import java.util.Date; /** * Discards 2 JOIN-REQs then accepts 1, then discards 2 more and so on * @author Bela Ban */ @Unsupported public class DELAY_JOIN_REQ extends Protocol { @Property private long delay=4000; private static final short gms_id=ClassConfigurator.getProtocolId(GMS.class); 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_id); 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); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/DISCARD.java0000644000175000017500000003034211647260573025570 0ustar moellermoeller package org.jgroups.protocols; import org.jgroups.*; import org.jgroups.Event; import org.jgroups.annotations.*; import org.jgroups.stack.Protocol; 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). */ @Unsupported @MBean(description="Discards messages") public class DISCARD extends Protocol { @Property double up=0.0; // probability of dropping up msgs @Property double down=0.0; // probability of dropping down msgs @Property boolean excludeItself=true; // if true don't discard messages sent/received in this stack Address localAddress; @ManagedAttribute(description="Number of dropped down messages",name="droppedDownMessages") int num_down=0; @ManagedAttribute(description="Number of dropped up messages",name="droppedUpMessages") int num_up=0; final Set

ignoredMembers = new HashSet
(); final Collection
members=new ArrayList
(); @ManagedAttribute(description="drop all messages (up or down)", writable=true) boolean discard_all=false; @ManagedAttribute(description="Number of subsequent unicasts to drop in the down direction",writable=true) int drop_down_unicasts=0; @ManagedAttribute(description="Number of subsequent multicasts to drop in the down direction",writable=true) int drop_down_multicasts=0; private DiscardDialog discard_dialog=null; @Property(name="gui", description="use a GUI or not") protected boolean use_gui=false; public boolean isDiscardAll() { return discard_all; } public void setDiscardAll(boolean discard_all) { this.discard_all=discard_all; } 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();} @ManagedOperation public void startGui() { if(discard_dialog == null) { discard_dialog=new DiscardDialog(); discard_dialog.init(); discard_dialog.setTitle(localAddress != null? localAddress.toString() : "n/a"); discard_dialog.handleView(members); } } @ManagedOperation public void stopGui() { if(discard_dialog != null) discard_dialog.dispose(); discard_dialog=null; } 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("Discard dialog (" + localAddress + ")"); } 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(this.id); 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(localAddress + ": 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(localAddress + ": 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(); members.clear(); members.addAll(mbrs); ignoredMembers.retainAll(mbrs); // remove all non members if(discard_dialog != null) discard_dialog.handleView(mbrs); break; case Event.SET_LOCAL_ADDRESS: localAddress=(Address)evt.getArg(); if(discard_dialog != null) discard_dialog.setTitle("Discard dialog (" + localAddress + ")"); 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 static class DiscardHeader extends Header { private final Set
dropMessages; 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 int size() { return (int)Util.size(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.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 String toString() { return super.toString() + " [mbr=" + mbr + "]"; } } }libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/DISCARD_PAYLOAD.java0000644000175000017500000000326511647260573026745 0ustar moellermoellerpackage org.jgroups.protocols; import org.jgroups.Event; import org.jgroups.Message; import org.jgroups.annotations.Property; import org.jgroups.annotations.Unsupported; import org.jgroups.stack.Protocol; /** * Discards a message whose sequence number (in the payload, as a Long) matches seqno 2 times, * before passing it down. Used for unit testing * of OOB messages * @author Bela Ban */ @Unsupported public class DISCARD_PAYLOAD extends Protocol { @Property long seqno=3; // drop 3 @Property long duplicate=4; // duplicate 4 (one time) int num_discards=0; public DISCARD_PAYLOAD() { } public Object down(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.down(evt); // pass it down, will passed down a second time by the default down_prot.down(evt) } } } catch(Throwable t) { ; } } } return down_prot.down(evt); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/DUPL.java0000644000175000017500000000700311647260573025261 0ustar moellermoellerpackage org.jgroups.protocols; import org.jgroups.stack.Protocol; import org.jgroups.annotations.Property; import org.jgroups.annotations.Unsupported; import org.jgroups.Event; import org.jgroups.Message; import org.jgroups.Address; /** Duplicates outgoing or incoming messages by copying them * @author Bela Ban */ @Unsupported public class DUPL extends Protocol { private static enum Direction {UP,DOWN}; @Property(description="Number of copies of each incoming message (0=no copies)") protected int incoming_copies=1; @Property(description="Number of copies of each outgoing message (0=no copies)") protected int outgoing_copies=1; @Property(description="Whether or not to copy unicast messages") protected boolean copy_unicast_msgs=true; @Property(description="Whether or not to copy multicast messages") 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 int getIncomingCopies() { return incoming_copies; } public void setIncomingCopies(int incoming_copies) { this.incoming_copies=incoming_copies; } public int getOutgoingCopies() { return outgoing_copies; } public void setOutgoingCopies(int outgoing_copies) { this.outgoing_copies=outgoing_copies; } public boolean isCopyUnicastMsgs() { return copy_unicast_msgs; } public void setCopyUnicastMsgs(boolean copy_unicast_msgs) { this.copy_unicast_msgs=copy_unicast_msgs; } public boolean isCopyMulticastMsgs() { return copy_multicast_msgs; } public void setCopyMulticastMsgs(boolean copy_multicast_msgs) { this.copy_multicast_msgs=copy_multicast_msgs; } 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; } } } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/Discovery.java0000644000175000017500000007144611647260573026500 0ustar moellermoellerpackage org.jgroups.protocols; import org.jgroups.*; import org.jgroups.annotations.MBean; import org.jgroups.annotations.ManagedAttribute; import org.jgroups.annotations.Property; import org.jgroups.annotations.ManagedOperation; import org.jgroups.protocols.pbcast.JoinRsp; import org.jgroups.stack.Protocol; import org.jgroups.util.*; import org.jgroups.util.UUID; 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 */ @MBean public abstract class Discovery extends Protocol { /* ----------------------------------------- Properties -------------------------------------------------- */ @Property(description="Timeout to wait for the initial members. Default is 3000 msec") protected long timeout=3000; @Property(description="Minimum number of initial members to get a response from. Default is 2") protected int num_initial_members=2; @Property(description="Minimum number of server responses (PingData.isServer()=true). If this value is " + "greater than 0, we'll ignore num_initial_members") protected int num_initial_srv_members=0; @Property(description="Return from the discovery phase as soon as we have 1 coordinator response") protected boolean break_on_coord_rsp=true; @Property(description="Number of discovery requests to be sent distributed over timeout. Default is 2") protected int num_ping_requests=2; @Property(description="Whether or not to return the entire logical-physical address cache mappings on a " + "discovery request, or not. Default is false, except for TCPPING") protected boolean return_entire_cache=false; @Property(description="Only members with a rank <= max_rank will send a discovery response. 1 means only the " + "coordinator will reply. 0 disables this; everyone replies. JIRA: https://jira.jboss.org/browse/JGRP-1181") protected int max_rank=0; @Property(description="If greater than 0, we'll wait a random number of milliseconds in range [0..stagger_timeout] " + "before sending a discovery response. This prevents traffic spikes in large clusters when everyone sends their " + "discovery response at the same time") protected long stagger_timeout=0; /* --------------------------------------------- JMX ------------------------------------------------------ */ @ManagedAttribute(description="Total number of discovery requests sent ") protected int num_discovery_requests=0; /** The largest cluster size found so far (gets reset on stop()) */ @ManagedAttribute protected volatile int max_found_members=0; @ManagedAttribute protected int rank=0; /* --------------------------------------------- Fields ------------------------------------------------------ */ protected volatile boolean is_server=false; protected TimeScheduler timer=null; protected View view; protected final Vector
members=new Vector
(11); protected Address local_addr=null; protected String group_addr=null; protected final Set ping_responses=new HashSet(); protected 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"); if(max_rank > 0) return_entire_cache=true; if(stagger_timeout < 0) throw new IllegalArgumentException("stagger_timeout cannot be negative"); if(stagger_timeout > timeout / num_ping_requests) { log.debug("stagger_timeout (" + stagger_timeout + ") was greater than timeout (" + timeout/num_ping_requests + "); setting it to " + timeout/num_ping_requests + " ms"); stagger_timeout=timeout / num_ping_requests; } } public abstract void sendGetMembersRequest(String cluster_name, Promise promise, ViewId view_id) throws Exception; public abstract boolean isDynamic(); public void handleDisconnect() { } public void handleConnect() { } public void discoveryRequestReceived(Address sender, String logical_name, Collection physical_addrs) { } 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; } @ManagedAttribute public String getView() {return view != null? view.getViewId().toString() : "null";} public ViewId getViewId() { return view != null? view.getViewId() : null; } public Vector providedUpServices() { Vector ret=new Vector(3); ret.addElement(Event.FIND_INITIAL_MBRS); ret.addElement(Event.FIND_ALL_VIEWS); ret.addElement(Event.GET_PHYSICAL_ADDRESS); return ret; } public void resetStats() { super.resetStats(); num_discovery_requests=0; } public void start() throws Exception { super.start(); } public void stop() { is_server=false; max_found_members=0; } /** * Finds initial members * @param promise * @return */ public List findInitialMembers(Promise promise) { return findMembers(promise, num_initial_members, break_on_coord_rsp, null); } public List findAllViews(Promise promise) { int num_expected_mbrs=Math.max(max_found_members, Math.max(num_initial_members, view != null? view.size() : num_initial_members)); max_found_members=Math.max(max_found_members, num_expected_mbrs); return findMembers(promise, num_expected_mbrs, false, getViewId()); } protected List findMembers(Promise promise, int num_expected_rsps, boolean break_on_coord, ViewId view_id) { num_discovery_requests++; final Responses rsps=new Responses(num_expected_rsps, num_initial_srv_members, break_on_coord, promise); synchronized(ping_responses) { ping_responses.add(rsps); } sender.start(group_addr, promise, view_id); try { return rsps.get(timeout); } catch(Exception e) { return new LinkedList(); } finally { sender.stop(); synchronized(ping_responses) { ping_responses.remove(rsps); } } } @ManagedOperation(description="Runs the discovery protocol to find initial members") public String findInitialMembersAsString() { List results=findInitialMembers(null); if(results == null || results.isEmpty()) return ""; StringBuilder sb=new StringBuilder(); for(PingData rsp: results) { sb.append(rsp).append("\n"); } return sb.toString(); } @ManagedOperation(description="Runs the discovery protocol to find all views") public String findAllViewsAsString() { List rsps=findAllViews(null); if(rsps == null || rsps.isEmpty()) return ""; StringBuilder sb=new StringBuilder(); for(PingData data: rsps) { View v=data.getView(); if(v != null) sb.append(v).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 */ @SuppressWarnings("unchecked") public Object up(Event evt) { switch(evt.getType()) { case Event.MSG: Message msg=(Message)evt.getArg(); PingHeader hdr=(PingHeader)msg.getHeader(this.id); if(hdr == null) return up_prot.up(evt); PingData data=hdr.data; Address logical_addr=data != null? data.getAddress() : null; switch(hdr.type) { case PingHeader.GET_MBRS_REQ: // return Rsp(local_addr, coord) if(group_addr == null || hdr.cluster_name == null) { if(log.isWarnEnabled()) log.warn("group_addr (" + group_addr + ") or cluster_name of header (" + hdr.cluster_name + ") is null; passing up discovery request from " + msg.getSrc() + ", but this should not" + " be the case"); } else { if(!group_addr.equals(hdr.cluster_name)) { if(log.isWarnEnabled()) log.warn("discarding discovery request for cluster '" + hdr.cluster_name + "' from " + msg.getSrc() + "; our cluster name is '" + group_addr + "'. " + "Please separate your clusters cleanly."); return null; } } // add physical address and logical name of the discovery sender (if available) to the cache if(data != null) { if(logical_addr == null) logical_addr=msg.getSrc(); Collection physical_addrs=data.getPhysicalAddrs(); PhysicalAddress physical_addr=physical_addrs != null && !physical_addrs.isEmpty()? physical_addrs.iterator().next() : null; if(logical_addr != null && physical_addr != null) down(new Event(Event.SET_PHYSICAL_ADDRESS, new Tuple(logical_addr, physical_addr))); if(logical_addr != null && data.getLogicalName() != null) UUID.add(logical_addr, data.getLogicalName()); discoveryRequestReceived(msg.getSrc(), data.getLogicalName(), physical_addrs); } if(max_rank > 0 && rank > 0 && rank > max_rank) // https://jira.jboss.org/browse/JGRP-1181 return null; if(return_entire_cache && hdr.view_id == null && rank != 0) { Map cache=(Map)down(new Event(Event.GET_LOGICAL_PHYSICAL_MAPPINGS)); if(cache != null) { for(Map.Entry entry: cache.entrySet()) { Address addr=entry.getKey(); PhysicalAddress physical_addr=entry.getValue(); sendDiscoveryResponse(addr, Arrays.asList(physical_addr), is_server, hdr.view_id != null, UUID.get(addr), msg.getSrc()); } } } else { if(hdr.view_id != null) { // If the discovery request is merge-triggered, and we the ViewId shipped with it // is the same as ours, we don't respond (JGRP-1315). ViewId my_view_id=view != null? view.getViewId() : null; if(my_view_id != null && Util.sameViewId(my_view_id, hdr.view_id)) return null; } List physical_addrs=hdr.view_id != null? null : Arrays.asList((PhysicalAddress)down(new Event(Event.GET_PHYSICAL_ADDRESS, local_addr))); sendDiscoveryResponse(local_addr, physical_addrs, is_server, hdr.view_id != null, UUID.get(local_addr), msg.getSrc()); } return null; case PingHeader.GET_MBRS_RSP: // add response to vector and notify waiting thread // add physical address (if available) to transport's cache if(data != null) { Address response_sender=msg.getSrc(); if(logical_addr == null) logical_addr=msg.getSrc(); Collection physical_addrs=data.getPhysicalAddrs(); PhysicalAddress physical_addr=physical_addrs != null && !physical_addrs.isEmpty()? physical_addrs.iterator().next() : null; if(logical_addr != null && physical_addr != null) down(new Event(Event.SET_PHYSICAL_ADDRESS, new Tuple(logical_addr, physical_addr))); if(logical_addr != null && data.getLogicalName() != null) UUID.add(logical_addr, data.getLogicalName()); if(log.isTraceEnabled()) log.trace("received GET_MBRS_RSP from " + response_sender + ": " + data); boolean overwrite=logical_addr != null && logical_addr.equals(response_sender); synchronized(ping_responses) { for(Responses response: ping_responses) { response.addResponse(data, overwrite); } } } return null; default: if(log.isWarnEnabled()) log.warn("got PING header with unknown type (" + hdr.type + ')'); return null; } case Event.GET_PHYSICAL_ADDRESS: try { sendGetMembersRequest(group_addr, null, null); } catch(InterruptedIOException ie) { if(log.isWarnEnabled()){ log.warn("Discovery request for cluster " + group_addr + " interrupted"); } Thread.currentThread().interrupt(); } catch(Exception ex) { if(log.isErrorEnabled()) log.error("failed sending discovery request", ex); } return null; case Event.FIND_INITIAL_MBRS: // sent by transport return findInitialMembers(null); } 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 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. */ @SuppressWarnings("unchecked") public Object down(Event evt) { switch(evt.getType()) { case Event.FIND_INITIAL_MBRS: // sent by GMS layer case Event.FIND_ALL_VIEWS: // sends the GET_MBRS_REQ to all members, waits 'timeout' ms or until 'num_initial_members' have been retrieved long start=System.currentTimeMillis(); boolean find_all_views=evt.getType() == Event.FIND_ALL_VIEWS; Promise promise=(Promise)evt.getArg(); List rsps=find_all_views? findAllViews(promise) : findInitialMembers(promise); long diff=System.currentTimeMillis() - start; if(log.isTraceEnabled()) log.trace("discovery took "+ diff + " ms: responses: " + Util.printPingData(rsps)); return rsps; case Event.TMP_VIEW: case Event.VIEW_CHANGE: Vector

tmp; view=(View)evt.getArg(); if((tmp=view.getMembers()) != null) { synchronized(members) { members.clear(); members.addAll(tmp); } } rank=Util.getRank(view, local_addr); if(ergonomics) { int size=view.size(); if(size <= Global.SMALL_CLUSTER_SIZE) { max_rank=0; return_entire_cache=false; } else if(size <= Global.NORMAL_CLUSTER_SIZE) { max_rank=size / 5; return_entire_cache=true; } else { max_rank=Math.min(size / 5, 10); return_entire_cache=true; } } 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.SET_LOCAL_ADDRESS: local_addr=(Address)evt.getArg(); return down_prot.down(evt); case Event.CONNECT: case Event.CONNECT_WITH_STATE_TRANSFER: case Event.CONNECT_USE_FLUSH: case Event.CONNECT_WITH_STATE_TRANSFER_USE_FLUSH: 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) { return new View(new ViewId(local_addr), mbrs); } /** * Creates a byte[] representation of the PingData, but DISCARDING the view it contains. * @param data the PingData instance to serialize. * @return */ protected byte[] serializeWithoutView(PingData data) { final PingData clone = new PingData(data.getAddress(), null, data.isServer(), data.getLogicalName(), data.getPhysicalAddrs()); try { return Util.streamableToByteBuffer(clone); } catch(Exception e) { log.error("Error", e); return null; } } protected PingData deserialize(final byte[] data) { try { return (PingData)Util.streamableFromByteBuffer(PingData.class, data); } catch(Exception e) { log.error("Error", e); return null; } } protected void sendDiscoveryResponse(Address logical_addr, List physical_addrs, boolean is_server, boolean return_view_only, String logical_name, final Address sender) { PingData data; if(return_view_only) { // data=new PingData(logical_addr, view, is_server, logical_name, physical_addrs); data=new PingData(logical_addr, view, is_server, null, null); } else { ViewId view_id=view != null? view.getViewId() : null; data=new PingData(logical_addr, null, view_id, is_server, logical_name, physical_addrs); } final Message rsp_msg=new Message(sender, null, null); rsp_msg.setFlag(Message.OOB); final PingHeader rsp_hdr=new PingHeader(PingHeader.GET_MBRS_RSP, data); rsp_msg.putHeader(this.id, rsp_hdr); if(stagger_timeout > 0) { int view_size=view != null? view.size() : 10; long sleep_time=rank == 0? Util.random(stagger_timeout) : stagger_timeout * rank / view_size - (stagger_timeout / view_size); timer.schedule(new Runnable() { public void run() { if(log.isTraceEnabled()) log.trace("received GET_MBRS_REQ from " + sender + ", sending staggered response " + rsp_hdr); down_prot.down(new Event(Event.MSG, rsp_msg)); } }, sleep_time, TimeUnit.MILLISECONDS); return; } if(log.isTraceEnabled()) log.trace("received GET_MBRS_REQ from " + sender + ", sending response " + rsp_hdr); down_prot.down(new Event(Event.MSG, rsp_msg)); } protected class PingSenderTask { protected Future senderFuture; public PingSenderTask() {} public synchronized void start(final String cluster_name, final Promise promise, final ViewId view_id) { long delay = (long)(timeout / (double)num_ping_requests); if(senderFuture == null || senderFuture.isDone()) { senderFuture=timer.scheduleWithFixedDelay(new Runnable() { public void run() { try { sendGetMembersRequest(cluster_name, promise, view_id); } 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; } } } protected static class Responses { final Promise promise; final List ping_rsps=new ArrayList(); 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(PingData rsp) { addResponse(rsp, false); } public void addResponse(PingData rsp, boolean overwrite) { if(rsp == null) return; promise.getLock().lock(); try { if(overwrite) ping_rsps.remove(rsp); // 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()) { PingData 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(); } } protected static int getNumServerResponses(Collection rsps) { int cnt=0; for(PingData rsp: rsps) { if(rsp.isServer()) cnt++; } return cnt; } protected static boolean containsCoordinatorResponse(Collection rsps) { if(rsps == null || rsps.isEmpty()) return false; for(PingData rsp: rsps) { if(rsp.isCoord()) return true; } return false; } } }libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/ENCRYPT.java0000644000175000017500000012327411647260573025652 0ustar moellermoeller package org.jgroups.protocols; import org.jgroups.*; import org.jgroups.annotations.GuardedBy; import org.jgroups.annotations.Property; import org.jgroups.stack.Protocol; import org.jgroups.util.QueueClosedException; 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.Vector; import java.util.WeakHashMap; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * 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); } private static final String DEFAULT_SYM_ALGO="AES"; // address info Address local_addr=null; // keyserver address Address keyServerAddr=null; //used to see whether we are the key server boolean keyServer=false; /* ----------------------------------------- Properties -------------------------------------------------- */ // encryption properties in no supplied key mode @Property(name="asym_provider", description="Cryptographic Service Provider. Default is Bouncy Castle Provider") String asymProvider=null; @Property(name="sym_provider", description="Cryptographic Service Provider. Default is Bouncy Castle Provider") String symProvider=null; @Property(name="asym_algorithm", description="Cipher engine transformation for asymmetric algorithm. Default is RSA") String asymAlgorithm="RSA"; @Property(name="sym_algorithm", description="Cipher engine transformation for symmetric algorithm. Default is AES") String symAlgorithm=DEFAULT_SYM_ALGO; @Property(name="asym_init", description="Initial public/private key length. Default is 512") int asymInit=512; @Property(name="sym_init", description="Initial key length for matching symmetric algorithm. Default is 128") int symInit=128; // properties for functioning in supplied key mode private boolean suppliedKey=false; @Property(name="key_store_name", description="File on classpath that contains keystore repository") String keyStoreName; @Property(name="store_password", description="Password used to check the integrity/unlock the keystore. Change the default") private String storePassword="changeit"; //JDK default @Property(name="key_password", description="Password for recovering the key. Change the default") private String keyPassword="changeit"; //JDK default @Property(name="alias", description="Alias used for recovering the key. Change the 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; @GuardedBy("decrypt_lock") Cipher symDecodingCipher; /** To synchronize access to symDecodingCipher */ protected final Lock decrypt_lock=new ReentrantLock(); // 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 */ @Property private boolean encrypt_entire_message=false; public 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 void init() throws Exception { if(keyPassword == null && storePassword != null) { keyPassword=storePassword; if(log.isInfoEnabled()) log.info("key_password used is same as store_password"); } 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"); if(symProvider != null && symProvider.trim().length() > 0) { symEncodingCipher=Cipher.getInstance(algorithm, symProvider); symDecodingCipher=Cipher.getInstance(algorithm, symProvider); } else { 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)"); /* 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 if(asymProvider != null && asymProvider.trim().length() > 0) asymCipher=Cipher.getInstance(asymAlgorithm, asymProvider); else 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()) { 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 Vector

members = view.getMembers(); if (members == null || members.isEmpty() || members.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 && !encrypt_entire_message) { passItUp(evt); return; } EncryptHeader hdr=(EncryptHeader)msg.getHeader(this.id); // 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=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(this.id); if(!hdr.getVersion().equals(getSymVersion())) { log.warn("attempting to use stored cipher as message does not uses current encryption version "); 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 Message _decrypt(Cipher cipher, Message msg, boolean decrypt_entire_msg) throws Exception { byte[] decrypted_msg; decrypt_lock.lock(); try { decrypted_msg=cipher.doFinal(msg.getRawBuffer(), msg.getOffset(), msg.getLength()); } finally { decrypt_lock.unlock(); } if(!decrypt_entire_msg) { msg.setBuffer(decrypted_msg); return msg; } 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, NoSuchProviderException { Message newMsg; if(log.isDebugEnabled()) log.debug("encoding shared key "); // create a cipher with peer's public key Cipher tmp; if (asymProvider != null && asymProvider.trim().length() > 0) tmp=Cipher.getInstance(asymAlgorithm, asymProvider); else 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(this.id, new EncryptHeader(EncryptHeader.SECRETKEY, getSymVersion())); if(log.isDebugEnabled()) log.debug(" Sending version " + getSymVersion() + " encoded key to client"); passItDown(new Event(Event.MSG, newMsg)); } /** * @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(this.id, 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.SET_LOCAL_ADDRESS: local_addr=(Address)evt.getArg(); if(log.isDebugEnabled()) log.debug("set local address to " + local_addr); 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=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 && !encrypt_entire_message) { 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); if(tmp.getSrc() == null) tmp.setSrc(local_addr); tmp.putHeader(this.id, hdr); passItDown(new Event(Event.MSG, tmp)); return; } // put our encrypt header on the message msg.putHeader(this.id, 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 synchronized 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; synchronized(this) { keyBytes=asymCipher.doFinal(encodedKey); } SecretKeySpec keySpec=null; try { keySpec=new SecretKeySpec(keyBytes, getAlgorithm(symAlgorithm)); // test reconstituted key to see if valid Cipher temp; if (symProvider != null && symProvider.trim().length() > 0) temp=Cipher.getInstance(symAlgorithm, symProvider); else temp=Cipher.getInstance(symAlgorithm); temp.init(Cipher.SECRET_KEY, keySpec); } catch(Exception e) { log.fatal(e.toString()); 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 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 { 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; 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 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 != null? version.length() + " bytes" : "n/a") + "\"]"; } public int size() { int retval=Global.SHORT_SIZE + Global.BYTE_SIZE + Global.BYTE_SIZE; if(version != null) retval+=version.length() + 2; return retval; } /** * @return Returns the type. */ protected short getType() { return type; } /** * @return Returns the version. */ protected String getVersion() { return version; } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/EXAMPLE.java0000644000175000017500000000426011647260573025612 0ustar moellermoeller package org.jgroups.protocols; import org.jgroups.Address; import org.jgroups.Event; import org.jgroups.Message; import org.jgroups.View; import org.jgroups.annotations.MBean; import org.jgroups.annotations.Unsupported; 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. */ @Unsupported @MBean(description="Sample protocol") public class EXAMPLE extends Protocol { final Vector
members=new Vector
(); /** * Just remove if you don't need to reset any state */ public static void reset() { } public Object up(Event evt) { switch(evt.getType()) { case Event.MSG: Message 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 } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/Executing.java0000644000175000017500000011453711647260573026463 0ustar moellermoellerpackage org.jgroups.protocols; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.Externalizable; import java.io.IOException; import java.io.NotSerializableException; import java.io.Serializable; import java.nio.ByteBuffer; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Queue; import java.util.Set; import java.util.concurrent.BlockingQueue; import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.FutureTask; import java.util.concurrent.RunnableFuture; import java.util.concurrent.SynchronousQueue; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import org.jgroups.Address; import org.jgroups.Event; import org.jgroups.Header; import org.jgroups.Message; import org.jgroups.View; import org.jgroups.annotations.MBean; import org.jgroups.annotations.ManagedAttribute; import org.jgroups.annotations.Property; import org.jgroups.blocks.executor.ExecutionService.DistributedFuture; import org.jgroups.blocks.executor.ExecutorEvent; import org.jgroups.blocks.executor.ExecutorNotification; import org.jgroups.stack.Protocol; import org.jgroups.util.Streamable; import org.jgroups.util.Util; /** * This is the base protocol used for executions. * @author wburns * @see org.jgroups.protocols.CENTRAL_EXECUTOR */ @MBean(description="Based class for executor service functionality") abstract public class Executing extends Protocol { @Property(description="bypasses message bundling if set") protected boolean bypass_bundling=true; protected Address local_addr; protected View view; /** * This is a queue on the client side that holds all of the tasks that * are awaiting a consumer to pick them up */ protected final Queue _awaitingConsumer = new ConcurrentLinkedQueue(); /** * This is a map on the client side showing for all of the current pending * requests */ protected final ConcurrentMap _requestId = new ConcurrentHashMap(); /** * This is essentially a set on the consumer side of id's of all the threads * currently running as consumers. This is basically a set, but since * there is no ConcurrentHashSet we use a phoney value */ protected final ConcurrentMap _consumerId = new ConcurrentHashMap(); protected final ConcurrentMap, ExecutorNotification> notifiers = new ConcurrentHashMap, ExecutorNotification>(); /** * This is a map on the server side that shows which owner is currently * tied to the runnable so we can return to them the results */ protected final Map _running; /** * This is a map on the client side that shows for which * owner(consumer, request) the runnable they are currently using. This * also allows us to set the values on a future when finished. */ protected final Map _awaitingReturn; /** * This is a server side queue of all the tasks to pass off. Currently * there will never be tasks waiting to put in. If a task is put in and doesn't have a * respective take at the same time that task is rejected. */ protected BlockingQueue _tasks = new SynchronousQueue(); /** * This is a server side map to show which threads are running for a * given runnable. This is used to interrupt those threads if needed. */ protected final ConcurrentMap _runnableThreads = new ConcurrentHashMap(); /** * This lock is to protect the incoming run requests and the incoming * consumer queues */ protected Lock _consumerLock = new ReentrantLock(); /** * This is stored on the coordinator side. This queue holds all of the * addresses that currently want to run something. If this queue has * elements the consumer queue must be empty. */ protected Queue _runRequests = new ArrayDeque(); /** * This is stored on the coordinator side. This queue holds all of the * addresses that currently are able to run something. If this queue has * elements the run request queue must be empty. */ protected Queue _consumersAvailable = new ArrayDeque(); protected static enum Type { RUN_REQUEST, // request to coordinator from client to tell of a new task request CONSUMER_READY, // request to coordinator from server to tell of a new consumer ready CONSUMER_UNREADY, // request to coordinator from server to tell of a consumer stopping CONSUMER_FOUND, // response to client from coordinator of the consumer to send the task to RUN_SUBMITTED, // request to consumer from client the task to run RUN_REJECTED, // response to client from the consumer due to the consumer being gone (usually because the runner was stopped) RESULT_EXCEPTION, // response to client from the consumer when an exception was encountered RESULT_SUCCESS, // response to client from the consumer when a value is returned INTERRUPT_RUN, // request to consumer from client to interrupt the task CREATE_RUN_REQUEST, // request to backups from coordinator to create a new task request. Used by CENTRAL_LOCKING CREATE_CONSUMER_READY, // request to backups from coordinator to create a new consumer ready. Used by CENTRAL_LOCKING DELETE_RUN_REQUEST, // request to backups from coordinator to delete a task request. Used by CENTRAL_LOCKING DELETE_CONSUMER_READY // request to backups from coordinator to delete a consumer ready. Used by CENTRAL_LOCKING } public Executing() { _awaitingReturn = Collections.synchronizedMap(new HashMap()); _running = Collections.synchronizedMap(new HashMap()); } public boolean getBypassBundling() { return bypass_bundling; } public void setBypassBundling(boolean bypass_bundling) { this.bypass_bundling=bypass_bundling; } public void addExecutorListener(Future future, ExecutorNotification listener) { if(listener != null) notifiers.put(future, listener); } @ManagedAttribute public String getAddress() { return local_addr != null? local_addr.toString() : null; } @ManagedAttribute public String getView() { return view != null? view.toString() : null; } public Object down(Event evt) { switch(evt.getType()) { case ExecutorEvent.TASK_SUBMIT: Runnable runnable = (Runnable)evt.getArg(); _awaitingConsumer.add(runnable); // We are limited to a number of concurrent request id's // equal to 2^63-1. This is quite large and if it // overflows it will still be positive long requestId = Math.abs(counter.getAndIncrement()); _requestId.put(runnable, requestId); sendToCoordinator(Type.RUN_REQUEST, requestId, local_addr); break; case ExecutorEvent.CONSUMER_READY: Thread currentThread = Thread.currentThread(); long id = currentThread.getId(); _consumerId.put(id, new Object()); sendToCoordinator(Type.CONSUMER_READY, id, local_addr); try { // Unfortunately we can't start taking before we send // a message, therefore we have to do a timed poll on // _tasks below to make sure that we have time to call take runnable = _tasks.take(); _runnableThreads.put(runnable, currentThread); return runnable; } catch (InterruptedException e) { sendToCoordinator(Type.CONSUMER_UNREADY, id, local_addr); Thread.currentThread().interrupt(); } finally { _consumerId.remove(id); } break; case ExecutorEvent.TASK_COMPLETE: Object arg = evt.getArg(); Throwable throwable = null; if (arg instanceof Object[]) { Object[] array = (Object[])arg; runnable = (Runnable)array[0]; throwable = (Throwable)array[1]; } else { runnable = (Runnable)arg; } Owner owner = _running.remove(runnable); // This won't remove anything if owner doesn't come back _runnableThreads.remove(runnable); Object value = null; boolean exception = false; if (throwable != null) { // InterruptedException is special telling us that // we interrupted the thread while waiting but still got // a task therefore we have to reject it. if (throwable instanceof InterruptedException) { sendRequest(owner.address, Type.RUN_REJECTED, owner.requestId, null); break; } value = throwable; exception = true; } else if (runnable instanceof RunnableFuture) { RunnableFuture future = (RunnableFuture)runnable; boolean interrupted = false; boolean gotValue = false; // We have the value, before we interrupt at least get it! while (!gotValue) { try { value = future.get(); gotValue = true; } catch (InterruptedException e) { interrupted = true; } catch (ExecutionException e) { value = e.getCause(); exception = true; gotValue = true; } } if (interrupted) { Thread.currentThread().interrupt(); } } if (owner != null) { final Type type; final Object valueToSend; if (value == null) { type = Type.RESULT_SUCCESS; valueToSend = value; } // Both serializable values and exceptions would go in here else if (value instanceof Serializable || value instanceof Externalizable || value instanceof Streamable) { type = exception ? Type.RESULT_EXCEPTION : Type.RESULT_SUCCESS; valueToSend = value; } // This would happen if the value wasn't serializable, // so we have to send back to the client that the class // wasn't serializable else { type = Type.RESULT_EXCEPTION; valueToSend = new NotSerializableException( value.getClass().getName()); } if (local_addr.equals(owner.getAddress())) { if(log.isTraceEnabled()) log.trace("[redirect] <--> [" + local_addr + "] " + type.name() + " [" + value + (owner.requestId != -1 ? " request id: " + owner.requestId : "") + "]"); final Owner finalOwner = owner; if (type == Type.RESULT_SUCCESS) { handleValueResponse(local_addr, finalOwner.requestId, valueToSend); } else if (type == Type.RESULT_EXCEPTION){ handleExceptionResponse(local_addr, finalOwner.requestId, (Throwable)valueToSend); } } else { sendRequest(owner.getAddress(), type, owner.requestId, valueToSend); } } else { if (log.isTraceEnabled()) { log.trace("Could not return result - most likely because it was interrupted"); } } break; case ExecutorEvent.TASK_CANCEL: Object[] array = (Object[])evt.getArg(); runnable = (Runnable)array[0]; if (_awaitingConsumer.remove(runnable)) { _requestId.remove(runnable); if (log.isTraceEnabled()) log.trace("Cancelled task " + runnable + " before it was picked up"); return Boolean.TRUE; } // This is guaranteed to not be null so don't take cost of auto unboxing else if (array[1] == Boolean.TRUE) { owner = removeKeyForValue(_awaitingReturn, runnable); if (owner != null) { Long requestIdValue = _requestId.remove(runnable); // We only cancel if the requestId is still available // this means the result hasn't been returned yet and // we still have a chance to interrupt if (requestIdValue != null) { if (requestIdValue != owner.getRequestId()) { log.warn("Cancelling requestId didn't match waiting"); } sendRequest(owner.getAddress(), Type.INTERRUPT_RUN, owner.getRequestId(), null); } } else { if (log.isTraceEnabled()) log.warn("Couldn't interrupt server task: " + runnable); } ExecutorNotification notification = notifiers.remove(runnable); if (notification != null) { notification.interrupted(runnable); } return Boolean.TRUE; } else { return Boolean.FALSE; } case ExecutorEvent.ALL_TASK_CANCEL: array = (Object[])evt.getArg(); // This is a RunnableFuture so this cast is okay @SuppressWarnings("unchecked") Set runnables = (Set)array[0]; Boolean booleanValue = (Boolean)array[1]; List notRan = new ArrayList(); for (Runnable cancelRunnable : runnables) { // Removed from the consumer if (!_awaitingConsumer.remove(cancelRunnable) && booleanValue == Boolean.TRUE) { synchronized (_awaitingReturn) { owner = removeKeyForValue(_awaitingReturn, cancelRunnable); if (owner != null) { Long requestIdValue = _requestId.remove(cancelRunnable); if (requestIdValue != owner.getRequestId()) { log.warn("Cancelling requestId didn't match waiting"); } sendRequest(owner.getAddress(), Type.INTERRUPT_RUN, owner.getRequestId(), null); } ExecutorNotification notification = notifiers.remove(cancelRunnable); if (notification != null) { log.trace("Notifying listener"); notification.interrupted(cancelRunnable); } } } else { _requestId.remove(cancelRunnable); notRan.add(cancelRunnable); } } return notRan; case Event.SET_LOCAL_ADDRESS: local_addr=(Address)evt.getArg(); break; case Event.VIEW_CHANGE: handleView((View)evt.getArg()); break; } return down_prot.down(evt); } protected static V removeKeyForValue(Map map, K value) { synchronized (map) { Iterator> iter = map.entrySet().iterator(); while (iter.hasNext()) { Entry entry = iter.next(); if (entry.getValue().equals(value)) { iter.remove(); return entry.getKey(); } } } return null; } public Object up(Event evt) { switch(evt.getType()) { case Event.MSG: Message msg=(Message)evt.getArg(); ExecutorHeader hdr=(ExecutorHeader)msg.getHeader(id); if(hdr == null) break; Request req=(Request)msg.getObject(); if(log.isTraceEnabled()) log.trace("[" + local_addr + "] <-- [" + msg.getSrc() + "] " + req); switch(req.type) { case RUN_REQUEST: handleTaskRequest(req.request, (Address)req.object); break; case CONSUMER_READY: handleConsumerReadyRequest(req.request, (Address)req.object); break; case CONSUMER_UNREADY: handleConsumerUnreadyRequest(req.request, (Address)req.object); break; case CONSUMER_FOUND: handleConsumerFoundResponse(req.request, (Address)req.object); break; case RUN_SUBMITTED: Object objectToRun = req.object; Runnable runnable; if (objectToRun instanceof Runnable) { runnable = (Runnable)objectToRun; } else if (objectToRun instanceof Callable) { @SuppressWarnings("unchecked") Callable callable = (Callable)objectToRun; runnable = new FutureTask(callable); } else { log.error("Request of type " + req.type + " sent an object of " + objectToRun + " which is invalid"); break; } handleTaskSubmittedRequest(runnable, msg.getSrc(), req.request); break; case RUN_REJECTED: // We could make requests local for this, but is it really worth it handleTaskRejectedResponse(msg.getSrc(), req.request); break; case RESULT_SUCCESS: handleValueResponse(msg.getSrc(), req.request, req.object); break; case RESULT_EXCEPTION: handleExceptionResponse(msg.getSrc(), req.request, (Throwable)req.object); break; case INTERRUPT_RUN: // We could make requests local for this, but is it really worth it handleInterruptRequest(msg.getSrc(), req.request); break; case CREATE_CONSUMER_READY: Owner owner = new Owner((Address)req.object, req.request); handleNewConsumer(owner); break; case CREATE_RUN_REQUEST: owner = new Owner((Address)req.object, req.request); handleNewRunRequest(owner); break; case DELETE_CONSUMER_READY: owner = new Owner((Address)req.object, req.request); handleRemoveConsumer(owner); break; case DELETE_RUN_REQUEST: owner = new Owner((Address)req.object, req.request); handleRemoveRunRequest(owner); break; default: log.error("Request of type " + req.type + " not known"); break; } return null; case Event.VIEW_CHANGE: handleView((View)evt.getArg()); break; } return up_prot.up(evt); } protected void handleView(View view) { this.view=view; if(log.isDebugEnabled()) log.debug("view=" + view); List
members=view.getMembers(); _consumerLock.lock(); try { // This removes the consumers that were registered that are now gone Iterator iterator = _consumersAvailable.iterator(); while (iterator.hasNext()) { Owner owner = iterator.next(); if (!members.contains(owner.getAddress())) { iterator.remove(); sendRemoveConsumerRequest(owner); } } // This removes the tasks that those requestors are gone iterator = _runRequests.iterator(); while (iterator.hasNext()) { Owner owner = iterator.next(); if (!members.contains(owner.getAddress())) { iterator.remove(); sendRemoveRunRequest(owner); } } for (Entry entry :_awaitingReturn.entrySet()) { // The person currently servicing our request has gone down // without completing so we have to keep our request alive by // sending ours back to the coordinator Owner owner = entry.getKey(); if (!members.contains(owner.getAddress())) { sendToCoordinator(Type.RUN_REQUEST, owner.getRequestId(), owner.getAddress()); Runnable runnable = entry.getValue(); _requestId.put(runnable, owner.getRequestId()); _awaitingConsumer.add(runnable); } } } finally { _consumerLock.unlock(); } } abstract protected void sendToCoordinator(Type type, long requestId, Address address); abstract protected void sendNewRunRequest(Owner source); abstract protected void sendRemoveRunRequest(Owner source); abstract protected void sendNewConsumerRequest(Owner source); abstract protected void sendRemoveConsumerRequest(Owner source); protected void handleTaskRequest(long requestId, Address address) { final Owner consumer; Owner source = new Owner(address, requestId); _consumerLock.lock(); try { consumer = _consumersAvailable.poll(); // We don't add duplicate run requests - this allows for resubmission // if it is thought the message may have been dropped if (consumer == null && !_runRequests.contains(source)) { _runRequests.add(source); } } finally { _consumerLock.unlock(); } if (consumer != null) { sendRequest(source.getAddress(), Type.CONSUMER_FOUND, consumer.getRequestId(), consumer.getAddress()); sendRemoveConsumerRequest(consumer); } else { sendNewRunRequest(source); } } protected void handleConsumerReadyRequest(long requestId, Address address) { Owner requestor; final Owner source = new Owner(address, requestId); _consumerLock.lock(); try { requestor = _runRequests.poll(); // We don't add duplicate consumers - this allows for resubmission // if it is thought the message may have been dropped if (requestor == null && !_consumersAvailable.contains(source)) { _consumersAvailable.add(source); } } finally { _consumerLock.unlock(); } if (requestor != null) { sendRequest(requestor.getAddress(), Type.CONSUMER_FOUND, source.getRequestId(), source.getAddress()); sendRemoveRunRequest(requestor); } else { sendNewConsumerRequest(source); } } protected void handleConsumerUnreadyRequest(long requestId, Address address) { Owner consumer = new Owner(address, requestId); _consumersAvailable.remove(consumer); sendRemoveConsumerRequest(consumer); } protected void handleConsumerFoundResponse(long request, Address address) { final Runnable runnable = _awaitingConsumer.poll(); // This is a representation of the server side owner running our task. Owner owner = new Owner(address, request); if (runnable == null) { // For some reason we don't have a runnable anymore // so we have to send back to the coordinator that // the consumer is still available. The runnable // would be removed on a cancel sendToCoordinator(Type.CONSUMER_READY, owner.getRequestId(), owner.getAddress()); } else { final Long requestId = _requestId.get(runnable); owner = new Owner(address, requestId); _awaitingReturn.put(owner, runnable); // If local we pass along without serializing if (local_addr.equals(owner.getAddress())) { handleTaskSubmittedRequest(runnable, local_addr, requestId); } else { if (runnable instanceof DistributedFuture) { Callable callable = ((DistributedFuture)runnable).getCallable(); sendRequest(owner.getAddress(), Type.RUN_SUBMITTED, requestId, callable); } else { sendRequest(owner.getAddress(), Type.RUN_SUBMITTED, requestId, runnable); } } } } protected void handleTaskSubmittedRequest(Runnable runnable, Address source, long requestId) { // We store in our map so that when that task is // finished so that we can send back to the owner // with the results _running.put(runnable, new Owner(source, requestId)); // We give the task to the thread that is now waiting for it to be returned // If we can't offer then we have to respond back to // caller that we can't handle it. They must have // gotten our address when we had a consumer, but // they went away between then and now. boolean received = false; try { /** * We offer it a while before rejecting it. This is required * in case if the _tasks.take() call isn't registered quick * enough after sending the Type.CONSUMER_READY message */ received = _tasks.offer(runnable, 1000, TimeUnit.SECONDS); } catch (InterruptedException e) { Thread.currentThread().interrupt(); System.out.println("Interrupted while handing off"); } if (!received) { // If we couldn't hand off the task we have to tell the client // and also reupdate the coordinator that our consumer is ready sendRequest(source, Type.RUN_REJECTED, requestId, null); _running.remove(runnable); } } protected void handleTaskRejectedResponse(Address source, long requestId) { Runnable runnable = _awaitingReturn.remove(new Owner( source, requestId)); if (runnable != null) { _awaitingConsumer.add(runnable); Long taskRequestId = _requestId.get(runnable); if (taskRequestId != requestId) { log.warn("Task Request Id doesn't match in rejection"); } sendToCoordinator(Type.RUN_REQUEST, taskRequestId, local_addr); } else { log.error("error resubmitting task for request-id: " + requestId); } } protected void handleValueResponse(Address source, long requestId, Object value) { Runnable runnable = _awaitingReturn.remove( new Owner(source, requestId)); if (runnable != null) { _requestId.remove(runnable); } // We can only notify of success if it was a future if (runnable instanceof RunnableFuture) { RunnableFuture future = (RunnableFuture)runnable; ExecutorNotification notifier = notifiers.remove(future); if (notifier != null) { notifier.resultReturned(value); } } else { log.warn("Runnable was not found in awaiting"); } } protected void handleExceptionResponse(Address source, long requestId, Throwable throwable) { Runnable runnable = _awaitingReturn.remove( new Owner(source, requestId)); if (runnable != null) { _requestId.remove(runnable); } // We can only notify of exception if it was a future if (runnable instanceof RunnableFuture) { RunnableFuture future = (RunnableFuture)runnable; ExecutorNotification notifier = notifiers.remove(future); if (notifier != null) { notifier.throwableEncountered(throwable); } } else { // All we can do is log the error since their is no // way to return this to the user since they don't // have a future object. log.error("Runtime Error encountered from " + "Cluster execute(Runnable) method", throwable); } } protected void handleInterruptRequest(Address source, long requestId) { Owner owner = new Owner(source, requestId); Runnable runnable = removeKeyForValue(_running, owner); if (runnable != null) { Thread thread = _runnableThreads.remove(runnable); thread.interrupt(); } else { if (log.isTraceEnabled()) log.trace("Message could not be interrupted due to it already returned"); } } protected void handleNewRunRequest(Owner sender) { _consumerLock.lock(); try { if (!_runRequests.contains(sender)) { _runRequests.add(sender); } } finally { _consumerLock.unlock(); } } protected void handleRemoveRunRequest(Owner sender) { _consumerLock.lock(); try { _runRequests.remove(sender); } finally { _consumerLock.unlock(); } } protected void handleNewConsumer(Owner sender) { _consumerLock.lock(); try { if (!_consumersAvailable.contains(sender)) { _consumersAvailable.add(sender); } } finally { _consumerLock.unlock(); } } protected void handleRemoveConsumer(Owner sender) { _consumerLock.lock(); try { _consumersAvailable.remove(sender); } finally { _consumerLock.unlock(); } } protected void sendRequest(Address dest, Type type, long requestId, Object object) { Request req=new Request(type, object, requestId); Message msg=new Message(dest, null, req); msg.putHeader(id, new ExecutorHeader()); if(bypass_bundling) msg.setFlag(Message.DONT_BUNDLE); if(log.isTraceEnabled()) log.trace("[" + local_addr + "] --> [" + (dest == null? "ALL" : dest) + "] " + req); try { down_prot.down(new Event(Event.MSG, msg)); } catch(Exception ex) { log.error("failed sending " + type + " request: " + ex); } } /** * This keeps track of all the requests we send. This is used so that * the response doesn't have to send back the future but instead the counter * We just let this roll over */ protected static final AtomicLong counter = new AtomicLong(); protected static class Request implements Streamable { protected Type type; protected Object object; protected long request; public Request() { } public Request(Type type, Object object, long request) { this.type=type; this.object=object; this.request=request; } public void writeTo(DataOutputStream out) throws IOException { out.writeByte(type.ordinal()); // We can't use Util.writeObject since it's size is limited to 2^15-1 try { if (object instanceof Streamable) { out.writeShort(-1); Util.writeGenericStreamable((Streamable)object, out); } else { byte[] bytes = Util.objectToByteBuffer(object); out.writeInt(bytes.length); out.write(bytes); } } catch (IOException e) { throw e; } catch (Exception e) { throw new IOException("Exception encountered while serializing execution request", e); } out.writeLong(request); } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { type=Type.values()[in.readByte()]; // We can't use Util.readObject since it's size is limited to 2^15-1 try { short first = in.readShort(); if (first == -1) { object = Util.readGenericStreamable(in); } else { ByteBuffer bb = ByteBuffer.allocate(4); bb.putShort(first); bb.putShort(in.readShort()); int size = bb.getInt(0); byte[] bytes = new byte[size]; in.readFully(bytes, 0, size); object = Util.objectFromByteBuffer(bytes); } } catch (IOException e) { throw e; } catch (Exception e) { throw new IOException("Exception encountered while serializing execution request", e); } request=in.readLong(); } public String toString() { return type.name() + " [" + object + (request != -1 ? " request id: " + request : "") + "]"; } } public static class ExecutorHeader extends Header { public ExecutorHeader() { } public int size() { return 0; } public void writeTo(DataOutputStream out) throws IOException { } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { } } public static class Owner { protected final Address address; protected final long requestId; public Owner(Address address, long requestId) { this.address=address; this.requestId=requestId; } public Address getAddress() { return address; } public long getRequestId() { return requestId; } // @see java.lang.Object#hashCode() @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((address == null) ? 0 : address.hashCode()); result = prime * result + (int) (requestId ^ (requestId >>> 32)); return result; } // @see java.lang.Object#equals(java.lang.Object) @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Owner other = (Owner) obj; if (address == null) { if (other.address != null) return false; } else if (!address.equals(other.address)) return false; if (requestId != other.requestId) return false; return true; } public String toString() { return address + "::" + requestId; } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/FC.java0000644000175000017500000010101211647260573025000 0ustar moellermoellerpackage org.jgroups.protocols; import org.jgroups.Address; import org.jgroups.Event; import org.jgroups.Message; import org.jgroups.View; import org.jgroups.annotations.*; import org.jgroups.stack.Protocol; import org.jgroups.util.BoundedList; import org.jgroups.util.Util; import java.util.*; import java.util.concurrent.ConcurrentMap; 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 */ @MBean(description="Simple flow control protocol based on a credit system") public class FC extends Protocol { private final static FcHeader REPLENISH_HDR=new FcHeader(FcHeader.REPLENISH); private final static FcHeader CREDIT_REQUEST_HDR=new FcHeader(FcHeader.CREDIT_REQUEST); /* ----------------------------------------- Properties -------------------------------------------------- */ /** * Max number of bytes to send per receiver until an ack must be received before continuing sending */ @Property(description="Max number of bytes to send per receiver until an ack must be received to proceed. Default is 500000 bytes") 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. */ @Property(description="Max time (in milliseconds) to block. Default is 5000 msec") 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 we've received (min_threshold * max_credits) bytes from P, we send more credits to P. Example: if * max_credits is 1'000'000, and min_threshold 0.25, then we send ca. 250'000 credits to P once we've * received 250'000 bytes from P. */ @Property(description="The threshold (as a percentage of max_credits) at which a receiver sends more credits to " + "a sender. Example: if max_credits is 1'000'000, and min_threshold 0.25, then we send ca. 250'000 credits " + "to P once we've received 250'000 bytes from P") private double min_threshold=0.60; /** * Computed as max_credits times min_theshold. If explicitly set, this will * override the above computation */ @Property(description="Computed as max_credits x min_theshold unless explicitly set") private long min_credits=0; /** * 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 */ @Property(description="Does not block a down message if it is a result of handling an up message in the" + "same thread. Fixes JGRP-928") private boolean ignore_synchronous_response=true; /* --------------------------------------------- JMX ------------------------------------------------------ */ 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); /* --------------------------------------------- Fields ------------------------------------------------------ */ /** * 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("lock") private final ConcurrentMap sent=Util.createConcurrentMap(); /** * Keeps track of credits / member at the receiver's side. 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 fall below the threshold, we refill and send a REPLENISH message to the sender. * The sender blocks until REPLENISH message is received. */ private final ConcurrentMap received=Util.createConcurrentMap(); /** * List of members from whom we expect credits */ @GuardedBy("lock") private final Set
      creditors=new HashSet
      (11); /** * Whether FC is still running, this is set to false when the protocol terminates (on stop()) */ private volatile boolean running=true; private boolean frag_size_received=false; /** * the lowest credits of any destination (sent_msgs) */ @GuardedBy("lock") @ManagedAttribute(writable=false) private long lowest_credit=max_credits; /** Lock protecting sent credits table and some other vars (creditors for example) */ private final Lock lock=new ReentrantLock(); /** Mutex to block on down() */ private final Condition credits_available=lock.newCondition(); /** * 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; } }; /** Last time a credit request was sent. Used to prevent credit request storms */ @GuardedBy("lock") private long last_credit_request=0; 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; } @ManagedAttribute(description="Number of times flow control blocks sender") public int getNumberOfBlockings() { return num_blockings; } public long getMaxBlockTime() { return max_block_time; } public void setMaxBlockTime(long t) { max_block_time=t; } @Property(description="Max times to block for the listed messages sizes (Message.getLength()). Example: \"1000:10,5000:30,10000:500\"") public void setMaxBlockTimes(String str) { if(str == null) return; Long prev_key=null, prev_val=null; List vals=Util.parseCommaDelimitedStrings(str); if(max_block_times == null) 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); } public String getMaxBlockTimes() { if(max_block_times == null) return "n/a"; StringBuilder sb=new StringBuilder(); boolean first=true; for(Map.Entry entry: max_block_times.entrySet()) { if(!first) { sb.append(", "); } else { first=false; } sb.append(entry.getKey()).append(":").append(entry.getValue()); } return sb.toString(); } @ManagedAttribute(description="Total time (ms) spent in flow control block") public long getTotalTimeBlocked() { return total_time_blocking; } @ManagedAttribute(description="Average time spent in a flow control block") public double getAverageTimeBlocked() { return num_blockings == 0? 0.0 : total_time_blocking / (double)num_blockings; } @ManagedAttribute(description="Number of credit requests received") public int getNumberOfCreditRequestsReceived() { return num_credit_requests_received; } @ManagedAttribute(description="Number of credit requests sent") public int getNumberOfCreditRequestsSent() { return num_credit_requests_sent; } @ManagedAttribute(description="Number of credit responses received") public int getNumberOfCreditResponsesReceived() { return num_credit_responses_received; } @ManagedAttribute(description="Number of credit responses sent") public int getNumberOfCreditResponsesSent() { return num_credit_responses_sent; } @ManagedOperation(description="Print sender credits") public String printSenderCredits() { return printMap(sent); } @ManagedOperation(description="Print receiver credits") public String printReceiverCredits() { return printMap(received); } @ManagedOperation(description="Print credits") public String printCredits() { StringBuilder sb=new StringBuilder(); sb.append("senders:\n").append(printMap(sent)).append("\n\nreceivers:\n").append(printMap(received)); return sb.toString(); } @ManagedOperation(description="Prints the creditors") public String printCreditors() { return creditors.toString(); } public Map dumpStats() { Map retval=super.dumpStats(); retval.put("senders", printMap(sent)); retval.put("receivers", printMap(received)); return retval; } @ManagedOperation(description="Print last blocking times") 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 */ @ManagedOperation(description="Unblock a sender") public void unblock() { 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.getValue().set(max_credits); lowest_credit=computeLowestCredit(sent); creditors.clear(); credits_available.signalAll(); } finally { lock.unlock(); } } public void init() throws Exception { boolean min_credits_set = min_credits != 0; if(!min_credits_set) min_credits=(long)(max_credits * min_threshold); lowest_credit=max_credits; } 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"); } lock.lock(); try { running=true; lowest_credit=max_credits; } finally { lock.unlock(); } } public void stop() { super.stop(); lock.lock(); try { running=false; ignore_thread.set(false); credits_available.signalAll(); // notify all threads waiting on the mutex that we are done } finally { lock.unlock(); } } @SuppressWarnings("unchecked") public Object down(Event evt) { switch(evt.getType()) { case Event.MSG: Message msg=(Message)evt.getArg(); if(msg.isFlagSet(Message.NO_FC)) break; int length=msg.getLength(); if(length == 0) break; return handleDownMessage(evt, msg, length); case Event.CONFIG: handleConfigEvent((Map)evt.getArg()); break; 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 } @SuppressWarnings("unchecked") 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(); if(msg.isFlagSet(Message.NO_FC)) break; FcHeader hdr=(FcHeader)msg.getHeader(this.id); 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(); if(sent_credits != null) handleCreditRequest(received, sender, sent_credits.longValue()); 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, 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.CONFIG: Map map=(Map)evt.getArg(); handleConfigEvent(map); break; } return up_prot.up(evt); } private void handleConfigEvent(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(final Event evt, final Message msg, int length) { Address dest=msg.getDest(); if(max_block_times != null) { long tmp=getMaxBlockTime(length); if(tmp > 0) end_time.set(System.currentTimeMillis() + tmp); } 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("Blocking (lowest_credit=" + lowest_credit + "; 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) { // A negative block_time 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(length <= lowest_credit || rc || !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 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); lock.unlock(); try { for(Map.Entry entry: sent_copy.entrySet()) sendCreditRequest(entry.getKey(), entry.getValue().get()); } finally { lock.lock(); } } } catch(InterruptedException e) { // bela June 15 2007: don't interrupt the thread again, as this will trigger an infinite loop !! // (http://jira.jboss.com/jira/browse/JGRP-536) // Thread.currentThread().interrupt(); } } 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 { 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 lock held * @param dest * @param length */ private void determineCreditors(Address dest, int length) { boolean multicast=dest == null || dest.isMulticastAddress(); if(multicast) { for(Map.Entry entry: sent.entrySet()) { if(entry.getValue().get() <= length) creditors.add(entry.getKey()); } } else { Credit cred=sent.get(dest); if(cred != null && cred.get() <= 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 map, Address dest, long credits) { boolean multicast=dest == null || dest.isMulticastAddress(); long lowest=max_credits; if(multicast) { if(map.isEmpty()) return -1; for(Credit cred: map.values()) lowest=Math.min(cred.decrement(credits), lowest); return lowest; } else { Credit cred=map.get(dest); if(cred != null) return lowest=cred.decrement(credits); } return -1; } private void handleCredit(Address sender, Number increase) { if(sender == null) return; StringBuilder sb=null; lock.lock(); try { Credit cred=sent.get(sender); if(cred == null) return; long new_credit=Math.min(max_credits, cred.get() + increase.longValue()); if(log.isTraceEnabled()) { sb=new StringBuilder(); sb.append("received credit from ").append(sender).append(", old credit was ").append(cred) .append(", new credits are ").append(new_credit).append(".\nCreditors before are: ").append(creditors); } cred.increment(increase.longValue()); lowest_credit=computeLowestCredit(sent); if(!creditors.isEmpty() && creditors.remove(sender) && creditors.isEmpty()) credits_available.signalAll(); } finally { lock.unlock(); } } private static long computeLowestCredit(Map m) { Collection credits=m.values(); return Collections.min(credits).get(); } /** * Check whether sender has enough credits left. If not, send it some more * @param map The hashmap to use * @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, Address sender, int length) { if(sender == null || length == 0) return 0; Credit cred=map.get(sender); if(cred == null) return 0; if(log.isTraceEnabled()) log.trace("sender " + sender + " minus " + length + " credits, " + (cred.get() - length) + " remaining"); return cred.decrementAndGet(length); } /** * @param map The map to modify * @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, Address sender, long left_credits) { if(sender == null) return; Credit cred=map.get(sender); if(cred == null) return; long credit_response=Math.min(max_credits - left_credits, max_credits); if(log.isTraceEnabled()) log.trace("received credit request from " + sender + ": sending " + credit_response + " credits"); cred.set(max_credits); sendCredit(sender, credit_response); } private void sendCredit(Address dest, long credit) { if(log.isTraceEnabled()) log.trace("replenishing " + 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(this.id, 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(this.id, 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); 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, new Credit(max_credits)); if(!sent.containsKey(addr)) sent.put(addr, new Credit(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 } // 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 { 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(); } private class Credit implements Comparable { private long credits_left; private Credit(long credits) { this.credits_left=credits; } private synchronized long decrementAndGet(long credits) { credits_left=Math.max(0, credits_left - credits); long credit_response=max_credits - credits_left; if(credit_response >= min_credits) { credits_left=max_credits; return credit_response; } return 0; } private synchronized long decrement(long credits) { return credits_left=Math.max(0, credits_left - credits); } private synchronized long get() {return credits_left;} private synchronized void set(long new_credits) {credits_left=Math.min(max_credits, new_credits);} private synchronized long increment(long credits) { return credits_left=Math.min(max_credits, credits_left + credits); } public String toString() { return String.valueOf(credits_left); } public int compareTo(Object o) { Credit other=(Credit)o; return credits_left < other.credits_left ? -1 : credits_left > other.credits_left ? 1 : 0; } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/FD.java0000644000175000017500000005263011647260573025014 0ustar moellermoeller package org.jgroups.protocols; import org.jgroups.*; import org.jgroups.annotations.*; import org.jgroups.stack.Protocol; import org.jgroups.util.BoundedList; import org.jgroups.util.TimeScheduler; import org.jgroups.util.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. * * @author Bela Ban */ @MBean(description="Failure detection based on simple heartbeat protocol") @DeprecatedProperty(names={"shun"}) public class FD extends Protocol { /* ----------------------------------------- Properties -------------------------------------------------- */ @Property(description="Timeout to suspect a node P if neither a heartbeat nor data were received from P. Default is 3000 msec") long timeout=3000; @Property(description="Number of times to send an are-you-alive message") int max_tries=2; /* --------------------------------------------- JMX ------------------------------------------------------ */ protected int num_heartbeats=0; protected int num_suspect_events=0; protected final BoundedList

      suspect_history=new BoundedList
      (20); /* --------------------------------------------- Fields ------------------------------------------------------ */ protected Address local_addr=null; private long last_ack=System.currentTimeMillis(); protected int num_tries=0; protected final Lock lock=new ReentrantLock(); @GuardedBy("lock") protected Address ping_dest=null; @GuardedBy("lock") protected final List
      members=new ArrayList
      (); /** Members from which we select ping_dest. may be subset of {@link #members} */ @GuardedBy("lock") protected final List
      pingable_mbrs=new ArrayList
      (); private TimeScheduler timer=null; @GuardedBy("lock") private Future monitor_future=null; // task that performs the actual monitoring for failure detection /** Transmits SUSPECT message until view change or UNSUSPECT is received */ protected final Broadcaster bcast_task=new Broadcaster(); @ManagedAttribute(description="Member address") public String getLocalAddress() {return local_addr != null? local_addr.toString() : "null";} @ManagedAttribute(description="List of cluster members") public String getMembers() {return members != null? members.toString() : "null";} @ManagedAttribute(description="List of pingable members of a cluster") public String getPingableMembers() {return pingable_mbrs != null? pingable_mbrs.toString() : "null";} @ManagedAttribute(description="Ping destination") public String getPingDest() {return ping_dest != null? ping_dest.toString() : "null";} @ManagedAttribute(description="Number of heartbeats sent") public int getNumberOfHeartbeatsSent() {return num_heartbeats;} @ManagedAttribute(description="Number of suspect events received") 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;} @Deprecated public static boolean isShun() {return false;} @Deprecated public void setShun(boolean flag) {} @ManagedOperation(description="Print suspect history") 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 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"); } 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; } protected Monitor createMonitor() { return new Monitor(); } @ManagedOperation(description="Stops checking for crashed members") public void stopFailureDetection() { stopMonitor(); } @ManagedOperation(description="Resumes checking for crashed members") public void startFailureDetection() { startMonitor(); } /** 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(createMonitor(), 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.MSG: Message msg=(Message)evt.getArg(); FdHeader hdr=(FdHeader)msg.getHeader(this.id); 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); 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(Address mbr: hdr.mbrs) { if(local_addr != null && mbr.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(mbr); restartMonitor(); } finally { lock.unlock(); } } up_prot.up(new Event(Event.SUSPECT, mbr)); down_prot.down(new Event(Event.SUSPECT, mbr)); } } 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); case Event.SET_LOCAL_ADDRESS: local_addr=(Address)evt.getArg(); break; } 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(this.id, 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(); } } } public static class FdHeader extends Header { public static final byte HEARTBEAT=0; public static final byte HEARTBEAT_ACK=1; public static final byte SUSPECT=2; byte type=HEARTBEAT; Collection
      mbrs=null; Address from=null; // member who detected that suspected_mbr has failed public FdHeader() { } public FdHeader(byte type) { this.type=type; } public FdHeader(byte type, Collection
      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 + ")"; default: return "unknown type (" + type + ")"; } } 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=(Collection
      )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(id, 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(id, 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); } } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/FD_ALL.java0000644000175000017500000003070411647260573025502 0ustar moellermoellerpackage org.jgroups.protocols; import org.jgroups.*; import org.jgroups.annotations.*; import org.jgroups.stack.Protocol; import org.jgroups.util.BoundedList; import org.jgroups.util.TimeScheduler; import org.jgroups.util.Util; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.util.*; import java.util.Map.Entry; 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. Every member periodically multicasts a heartbeat. * Every member also maintains a table of all members (minus itself). When data or a heartbeat from P is received, * we reset the timestamp for P to the current time. Periodically, we check for expired members, and suspect those.

      * Reduced number of messages exchanged on suspect event: https://jira.jboss.org/browse/JGRP-1241 * * @author Bela Ban */ @MBean(description="Failure detection based on simple heartbeat protocol") @DeprecatedProperty(names={"shun"}) public class FD_ALL extends Protocol { /* ----------------------------------------- Properties -------------------------------------------------- */ @Property(description="Interval in which a HEARTBEAT is sent to the cluster") long interval=3000; @Property(description="Timeout after which a node P is suspected if neither a heartbeat nor data were received from P") long timeout=10000; @Property(description="Treat messages received from members as heartbeats. Note that this means we're updating " + "a value in a hashmap every time a message is passing up the stack through FD_ALL, which is costly. Default is false") boolean msg_counts_as_heartbeat=false; /* --------------------------------------------- JMX ------------------------------------------------------ */ @ManagedAttribute(description="Number of heartbeats sent") protected int num_heartbeats_sent; @ManagedAttribute(description="Number of heartbeats received") protected int num_heartbeats_received=0; @ManagedAttribute(description="Number of suspected events received") protected int num_suspect_events=0; /* --------------------------------------------- Fields ------------------------------------------------------ */ // Map of addresses and timestamps of last updates private final Map timestamps=Util.createConcurrentMap(); private Address local_addr=null; private final List
      members=new ArrayList
      (); protected final Set
      suspected_mbrs=new HashSet
      (); private TimeScheduler timer=null; // task which multicasts HEARTBEAT message after 'interval' ms @GuardedBy("lock") private Future heartbeat_sender_future=null; // task which checks for members exceeding timeout and suspects them @GuardedBy("lock") private Future timeout_checker_future=null; private final BoundedList
      suspect_history=new BoundedList
      (20); private final Lock lock=new ReentrantLock(); public FD_ALL() {} @ManagedAttribute(description="Member address") public String getLocalAddress() {return local_addr != null? local_addr.toString() : "null";} @ManagedAttribute(description="Lists members of a cluster") public String getMembers() {return members.toString();} @ManagedAttribute(description="Currently suspected members") public String getSuspectedMembers() {return suspected_mbrs.toString();} 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;} @Deprecated public static boolean isShun() {return false;} @Deprecated public void setShun(boolean flag) {} @ManagedAttribute(description="Are heartbeat tasks running") public boolean isRunning() { lock.lock(); try{ return isTimeoutCheckerRunning() && isHeartbeatSenderRunning(); } finally{ lock.unlock(); } } @ManagedOperation(description="Prints suspect history") public String printSuspectHistory() { StringBuilder sb=new StringBuilder(); for(Address tmp: suspect_history) { sb.append(new Date()).append(": ").append(tmp).append("\n"); } return sb.toString(); } @ManagedOperation(description="Prints timestamps") public String printTimestamps() { return _printTimestamps(); } @ManagedOperation(description="Stops checking for crashed members") public void stopFailureDetection() { stopTimeoutChecker(); } @ManagedOperation(description="Resumes checking for crashed members") public void startFailureDetection() { startTimeoutChecker(); } 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 not set"); } public void stop() { stopHeartbeatSender(); stopTimeoutChecker(); suspected_mbrs.clear(); } public Object up(Event evt) { switch(evt.getType()) { case Event.MSG: Message msg=(Message)evt.getArg(); Address sender=msg.getSrc(); Header hdr=msg.getHeader(this.id); if(hdr != null) { update(sender); // updates the heartbeat entry for 'sender' num_heartbeats_received++; return null; // consume heartbeat message, do not pass to the layer above } else if(msg_counts_as_heartbeat) { // message did not originate from FD_ALL layer, but still count as heartbeat update(sender); // update when data is received too ? maybe a bit costly } break; // pass message to the layer above } 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; case Event.SET_LOCAL_ADDRESS: local_addr=(Address)evt.getArg(); break; } return down_prot.down(evt); } private void startTimeoutChecker() { lock.lock(); try { if(!isTimeoutCheckerRunning()) { 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(!isHeartbeatSenderRunning()) { 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 boolean isTimeoutCheckerRunning() { return timeout_checker_future != null && !timeout_checker_future.isDone(); } private boolean isHeartbeatSenderRunning() { return heartbeat_sender_future != null && !heartbeat_sender_future.isDone(); } private void update(Address sender) { if(sender != null && !sender.equals(local_addr)) timestamps.put(sender, System.currentTimeMillis()); } private void handleViewChange(View v) { List
      mbrs=v.getMembers(); synchronized(this) { members.clear(); members.addAll(mbrs); suspected_mbrs.retainAll(mbrs); timestamps.keySet().retainAll(mbrs); } for(Address member: mbrs) update(member); if(mbrs.size() > 1) { startHeartbeatSender(); startTimeoutChecker(); } else { stopHeartbeatSender(); stopTimeoutChecker(); } } private String _printTimestamps() { StringBuilder sb=new StringBuilder(); long current_time=System.currentTimeMillis(); for(Iterator> it=timestamps.entrySet().iterator(); it.hasNext();) { 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(List
      suspects) { if(suspects == null) return; num_suspect_events+=suspects.size(); final List
      eligible_mbrs=new ArrayList
      (); synchronized(this) { for(Address suspect: suspects) { suspect_history.add(suspect); suspected_mbrs.add(suspect); } eligible_mbrs.addAll(members); eligible_mbrs.removeAll(suspected_mbrs); } // Check if we're coord, then send up the stack if(local_addr != null && !eligible_mbrs.isEmpty()) { Address first=eligible_mbrs.get(0); if(local_addr.equals(first)) { if(log.isDebugEnabled()) log.debug("suspecting " + suspected_mbrs); for(Address suspect: suspects) { up_prot.up(new Event(Event.SUSPECT, suspect)); down_prot.down(new Event(Event.SUSPECT, suspect)); } } } } public static class HeartbeatHeader extends Header { public HeartbeatHeader() {} public String toString() {return "heartbeat";} public int size() {return 0;} public void writeTo(DataOutputStream out) throws IOException {} public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException {} } /** * 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); heartbeat.putHeader(id, new HeartbeatHeader()); down_prot.down(new Event(Event.MSG, heartbeat)); num_heartbeats_sent++; } } class TimeoutChecker implements Runnable { public void run() { List
      suspects=new LinkedList
      (); long current_time=System.currentTimeMillis(), diff; for(Iterator> it=timestamps.entrySet().iterator(); it.hasNext();) { 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.isDebugEnabled()) log.debug("haven't received a heartbeat from " + key + " for " + diff + " ms, adding it to suspect list"); suspects.add(key); } } if(!suspects.isEmpty()) suspect(suspects); } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/FD_ICMP.java0000644000175000017500000001415311647260573025622 0ustar moellermoellerpackage org.jgroups.protocols; import org.jgroups.Event; import org.jgroups.Global; import org.jgroups.annotations.Experimental; import org.jgroups.annotations.LocalAddress; import org.jgroups.annotations.Property; import org.jgroups.conf.PropertyConverters; 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.util.Map; /** * 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 */ @Experimental public class FD_ICMP extends FD { /** network interface to be used to send the ICMP packets */ private NetworkInterface intf=null; @LocalAddress @Property(name="bind_addr", description="The NIC on which the ServerSocket should listen on. " + "The following special values are also recognized: GLOBAL, SITE_LOCAL, LINK_LOCAL and NON_LOOPBACK", systemProperty={Global.BIND_ADDR, Global.BIND_ADDR_OLD}, defaultValueIPv4=Global.NON_LOOPBACK_ADDRESS, defaultValueIPv6=Global.NON_LOOPBACK_ADDRESS) private InetAddress bind_addr=null ; @Property(name="bind_interface", converter=PropertyConverters.BindInterface.class, description="The interface (NIC) which should be used by this transport", dependsUpon="bind_addr") protected String bind_interface_str=null; private Method is_reacheable; /** Time-to-live for InetAddress.isReachable() */ @Property private int ttl=32; public void init() throws Exception { super.init(); if(bind_addr != null) intf=NetworkInterface.getByInetAddress(bind_addr); try { Class is_reacheable_class=Util.loadClass("java.net.InetAddress", this.getClass()); is_reacheable=is_reacheable_class.getMethod("isReachable", NetworkInterface.class, int.class, int.class); } catch(ClassNotFoundException e) { // should never happen since we require JDK 1.5 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; } } 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, 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); } } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/FD_PING.java0000644000175000017500000001233611647260573025630 0ustar moellermoellerpackage org.jgroups.protocols; import org.jgroups.annotations.Property; import org.jgroups.annotations.Unsupported; import org.jgroups.stack.IpAddress; import org.jgroups.util.Util; import org.jgroups.logging.Log; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; /** * 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 */ @Unsupported 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) */ @Property(description="Command (script or executable) to ping a host: a return value of 0 means success, anything else is a failure. Default is ping") String cmd="ping"; @Property(description="Write the stdout of the command to the log. Default is true") boolean verbose=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()); } } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/FD_SIMPLE.java0000644000175000017500000002447011647260573026066 0ustar moellermoeller package org.jgroups.protocols; import org.jgroups.*; import org.jgroups.annotations.GuardedBy; import org.jgroups.annotations.Property; import org.jgroups.annotations.Unsupported; import org.jgroups.stack.Protocol; import org.jgroups.util.Promise; import org.jgroups.util.TimeScheduler; import java.io.*; import java.util.HashMap; import java.util.Iterator; import java.util.Map; 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.31 $ */ @Unsupported 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; @Property long interval=3000; // interval in msecs between are-you-alive messages @Property long timeout=3000; // time (in msecs) to wait for a response to are-you-alive final Vector
      members=new Vector
      (); final Map counters=new HashMap(); // keys=Addresses, vals=Integer (count) @Property int max_missed_hbs=5; // max number of missed responses until a member is suspected public void init() throws Exception { timer=getTransport().getTimer(); } 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.MSG: msg=(Message)evt.getArg(); sender=msg.getSrc(); resetCounter(sender); counter_reset=true; hdr=(FdHeader)msg.getHeader(this.id); 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(this.id, 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()) { case Event.SET_LOCAL_ADDRESS: local_addr=(Address)evt.getArg(); break; // 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=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=members_copy.elementAt(r); return retval; } int incrementCounter(Address mbr) { Integer cnt; int ret=0; if(mbr == null) return ret; synchronized(counters) { cnt=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=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 { 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; 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 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(id, 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)); } } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/FD_SOCK.java0000644000175000017500000013421511647260573025633 0ustar moellermoellerpackage org.jgroups.protocols; import org.jgroups.*; import org.jgroups.annotations.*; import org.jgroups.conf.PropertyConverters; import org.jgroups.stack.IpAddress; import org.jgroups.stack.Protocol; import org.jgroups.util.*; import java.io.*; import java.net.*; import java.util.*; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.Future; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.TimeUnit; /** * 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 */ @MBean(description="Failure detection protocol based on sockets connecting members") @DeprecatedProperty(names={"srv_sock_bind_addr"}) public class FD_SOCK extends Protocol implements Runnable { private static final int NORMAL_TERMINATION=9; private static final int ABNORMAL_TERMINATION=-1; /* ----------------------------------------- Properties -------------------------------------------------- */ @LocalAddress @Property(description="The NIC on which the ServerSocket should listen on. " + "The following special values are also recognized: GLOBAL, SITE_LOCAL, LINK_LOCAL and NON_LOOPBACK", systemProperty={Global.BIND_ADDR, Global.BIND_ADDR_OLD}, defaultValueIPv4=Global.NON_LOOPBACK_ADDRESS, defaultValueIPv6=Global.NON_LOOPBACK_ADDRESS) InetAddress bind_addr=null; @Property(name="bind_interface", converter=PropertyConverters.BindInterface.class, description="The interface (NIC) which should be used by this transport", dependsUpon="bind_addr") protected String bind_interface_str=null; @Property(description="Timeout for getting socket cache from coordinator. Default is 1000 msec") long get_cache_timeout=1000; @Property(description="Interval for broadcasting suspect messages. Default is 5000 msec") long suspect_msg_interval=5000; @Property(description="Number of attempts coordinator is solicited for socket cache until we give up. Default is 3") int num_tries=3; @Property(description="Start port for server socket. Default value of 0 picks a random port") int start_port=0; @Property(description="Start port for client socket. Default value of 0 picks a random port") int client_bind_port=0; @Property(description="Number of ports to probe for start_port and client_bind_port") int port_range=50; @Property(description="Whether to use KEEP_ALIVE on the ping socket or not. Default is true") private boolean keep_alive=true; @Property(description="Max time in millis to wait for ping Socket.connect() to return") private int sock_conn_timeout=1000; /* --------------------------------------------- JMX ------------------------------------------------------ */ private int num_suspect_events=0; private final BoundedList

      suspect_history=new BoundedList
      (20); /* --------------------------------------------- Fields ------------------------------------------------------ */ private final Vector
      members=new Vector
      (11); // list of group members (updated on VIEW_CHANGE) protected final Set
      suspected_mbrs=new HashSet
      (); private final Vector
      pingable_mbrs=new Vector
      (11); volatile boolean srv_sock_sent=false; // has own socket been broadcast yet ? /** Used to rendezvous on GET_CACHE and GET_CACHE_RSP */ private final Promise> get_cache_promise=new Promise>(); private volatile boolean got_cache_from_coord=false; // was cache already fetched ? private Address local_addr=null; // our own address private ServerSocket srv_sock=null; // server socket to which another member connects to monitor me private ServerSocketHandler srv_sock_handler=null; // accepts new connections on srv_sock private IpAddress srv_sock_addr=null; // pair of server_socket:port private Address ping_dest=null; // address of the member we monitor private Socket ping_sock=null; // socket to the member we monitor private InputStream ping_input=null; // input stream of the socket to the member we monitor @GuardedBy("this") private volatile Thread pinger_thread=null; // listens on ping_sock, suspects member if socket is closed /** Cache of member addresses and their ServerSocket addresses */ private final ConcurrentMap cache=Util.createConcurrentMap(11); private final Promise ping_addr_promise=new Promise(); // to fetch the ping_addr for ping_dest private final Object sock_mutex=new Object(); // for access to ping_sock, ping_input private TimeScheduler timer=null; private final BroadcastTask bcast_task=new BroadcastTask(); // to transmit SUSPECT message (until view change) private volatile boolean regular_sock_close=false; // used by interruptPingerThread() when new ping_dest is computed private boolean log_suspected_msgs=true; public FD_SOCK() { } @ManagedAttribute(description="Member address") public String getLocalAddress() {return local_addr != null? local_addr.toString() : "null";} @ManagedAttribute(description="List of cluster members") public String getMembers() {return members != null? members.toString() : "null";} @ManagedAttribute(description="List of pingable members of a cluster") public String getPingableMembers() {return pingable_mbrs != null? pingable_mbrs.toString() : "null";} @ManagedAttribute(description="Ping destination") public String getPingDest() {return ping_dest != null? ping_dest.toString() : "null";} @ManagedAttribute(description="Number of suspect event generated") public int getNumSuspectEventsGenerated() {return num_suspect_events;} public boolean isLogSuspectedMessages() { return log_suspected_msgs; } public void setLogSuspectedMessages(boolean log_suspected_msgs) { this.log_suspected_msgs=log_suspected_msgs; } @ManagedOperation(description="Print suspect history") public String printSuspectHistory() { StringBuilder sb=new StringBuilder(); for(Address suspect: suspect_history) { sb.append(new Date()).append(": ").append(suspect).append("\n"); } return sb.toString(); } @ManagedOperation 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(); } public void stop() { stopPingerThread(); stopServerSocket(true); // graceful close bcast_task.removeAll(); suspected_mbrs.clear(); } public void resetStats() { super.resetStats(); num_suspect_events=0; suspect_history.clear(); } public Object up(Event evt) { switch(evt.getType()) { case Event.MSG: Message msg=(Message) evt.getArg(); FdHeader hdr=(FdHeader)msg.getHeader(this.id); 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.isTraceEnabled()) log.trace("received SUSPECT message from " + msg.getSrc() + ": suspects=" + hdr.mbrs); suspect(hdr.mbrs); } 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) { 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) { 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(this.id, hdr); down_prot.down(new Event(Event.MSG, msg)); break; case FdHeader.GET_CACHE_RSP: if(hdr.cachedAddrs == 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.CONNECT: case Event.CONNECT_WITH_STATE_TRANSFER: case Event.CONNECT_USE_FLUSH: case Event.CONNECT_WITH_STATE_TRANSFER_USE_FLUSH: Object ret=down_prot.down(evt); try { startServerSocket(); } catch(Exception e) { throw new IllegalArgumentException("failed to start server socket", e); } return ret; case Event.DISCONNECT: stopServerSocket(true); // graceful close break; case Event.SET_LOCAL_ADDRESS: local_addr=(Address) evt.getArg(); break; case Event.VIEW_CHANGE: View v=(View) evt.getArg(); final Vector
      new_mbrs=v.getMembers(); synchronized(this) { members.removeAllElements(); members.addAll(new_mbrs); suspected_mbrs.retainAll(new_mbrs); cache.keySet().retainAll(members); // remove all entries in 'cache' which are not in the new membership bcast_task.adjustSuspectedMembers(members); pingable_mbrs.removeAllElements(); pingable_mbrs.addAll(members); if(log.isDebugEnabled()) log.debug("VIEW_CHANGE received: " + members); if(members.size() > 1) { if(isPingerThreadRunning()) { Address tmp_ping_dest=determinePingDest(); boolean hasNewPingDest = ping_dest != null && tmp_ping_dest != null && !ping_dest.equals(tmp_ping_dest); if(hasNewPingDest) { interruptPingerThread(); // allows the thread to use the new socket } } else startPingerThread(); // only starts if not yet running } else { ping_dest=null; stopPingerThread(); } } 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() { // 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; } } // 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; } if(log.isTraceEnabled()) log.trace("pinger_thread started"); // +++ remove while(isPingerThreadRunning()) { regular_sock_close=false; ping_dest=determinePingDest(); // gets the neighbor to our right if(log.isDebugEnabled()) log.debug("ping_dest is " + ping_dest + ", pingable_mbrs=" + pingable_mbrs); if(ping_dest == null || !isPingerThreadRunning()) break; IpAddress ping_addr=fetchPingAddress(ping_dest); if(ping_addr == null) { if(log.isTraceEnabled()) log.trace("socket address for " + ping_dest + " could not be fetched, retrying"); Util.sleep(1000); continue; } if(!setupPingSocket(ping_addr) && isPingerThreadRunning()) { // covers use cases #7 and #8 in ManualTests.txt if(log.isDebugEnabled()) log.debug("could not create socket to " + ping_dest); broadcastSuspectMessage(ping_dest); pingable_mbrs.removeElement(ping_dest); continue; } if(log.isTraceEnabled()) log.trace("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 " + ping_dest + " closed socket gracefully"); pingable_mbrs.removeElement(ping_dest); break; case ABNORMAL_TERMINATION: // -1 means EOF 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.isTraceEnabled()) log.trace("pinger thread terminated"); } private synchronized boolean isPingerThreadRunning(){ return pinger_thread != null && pinger_thread.isAlive() && !pinger_thread.isInterrupted(); } /* ----------------------------------- Private Methods -------------------------------------- */ void suspect(Set
      suspects) { if(suspects == null) return; final List
      eligible_mbrs=new ArrayList
      (); synchronized(this) { for(Address suspect: suspects) { suspect_history.add(suspect); suspected_mbrs.add(suspect); } eligible_mbrs.addAll(members); eligible_mbrs.removeAll(suspected_mbrs); } // Check if we're coord, then send up the stack if(local_addr != null && !eligible_mbrs.isEmpty()) { Address first=eligible_mbrs.get(0); if(local_addr.equals(first)) { if(log.isDebugEnabled()) log.debug("suspecting " + suspected_mbrs); for(Address suspect: suspects) { up_prot.up(new Event(Event.SUSPECT, suspect)); down_prot.down(new Event(Event.SUSPECT, suspect)); } } } } 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); pingable_mbrs.removeElement(ping_dest); } else { if(log.isDebugEnabled()) log.debug("socket to " + ping_dest + " was closed gracefully"); regular_sock_close=false; } } /** * Does *not* need to be synchronized on pinger_mutex because the caller (down()) already has the mutex acquired */ private synchronized void startPingerThread() { if(!isPingerThreadRunning()) { ThreadFactory factory=getThreadFactory(); pinger_thread=factory.newThread(this, "FD_SOCK pinger"); pinger_thread.setDaemon(true); pinger_thread.start(); } } /** * 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 */ private void interruptPingerThread() { if(isPingerThreadRunning()) { 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 } } private synchronized void stopPingerThread() { if(pinger_thread != null) { regular_sock_close=true; try { pinger_thread.interrupt(); pinger_thread.join(Global.THREAD_SHUTDOWN_WAIT_TIME); } catch(InterruptedException ignored) { Thread.currentThread().interrupt(); } pinger_thread=null; } ping_addr_promise.setResult(null); get_cache_promise.setResult(null); sendPingTermination(); // PATCH by Bruce Schuchardt (http://jira.jboss.com/jira/browse/JGRP-246) teardownPingSocket(); } // PATCH: send something so the connection handler can exit void sendPingTermination() { sendPingSignal(NORMAL_TERMINATION); } 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); } } } } void startServerSocket() throws Exception { srv_sock=Util.createServerSocket(getSocketFactory(), Global.FD_SOCK_SRV_SOCK, bind_addr, start_port, start_port+port_range); // 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 } } public 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) { return false; } try { SocketAddress destAddr=new InetSocketAddress(dest.getIpAddress(), dest.getPort()); ping_sock=new Socket(); int num_bind_attempts=0; int port=client_bind_port; for(;;) { try { ping_sock.bind(new InetSocketAddress(bind_addr, port)); break; } catch(IOException e) { if(num_bind_attempts++ > port_range) { log.error("failed creating client socket to " + dest, e); throw e; } port++; } } ping_sock.setSoLinger(true, 1); ping_sock.setKeepAlive(keep_alive); Util.connect(ping_sock, 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 && isPingerThreadRunning()) { if((coord=determineCoordinator()) != null) { if(coord.equals(local_addr)) { // we are the first member --> empty cache return; } hdr=new FdHeader(FdHeader.GET_CACHE); msg=new Message(coord, null, null); msg.setFlag(Message.OOB); msg.putHeader(this.id, 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; } } --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.isDebugEnabled()) log.debug("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(this.id, 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(this.id, 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) { return null; } // 1. Try to get the server socket address from the cache if((ret=cache.get(mbr)) != null) return ret; if(!isPingerThreadRunning()) return null; // 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(this.id, hdr); down_prot.down(new Event(Event.MSG, ping_addr_req)); ret=ping_addr_promise.getResult(500); if(ret != null) { return ret; } if(!isPingerThreadRunning()) return null; // 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(this.id, hdr); down_prot.down(new Event(Event.MSG, ping_addr_req)); ret=ping_addr_promise.getResult(500); return ret; } private Address determinePingDest() { Address tmp; if(pingable_mbrs == null || pingable_mbrs.size() < 2 || local_addr == null) return null; for(int i=0; i < pingable_mbrs.size(); i++) { tmp=pingable_mbrs.elementAt(i); if(local_addr.equals(tmp)) { if(i + 1 >= pingable_mbrs.size()) return pingable_mbrs.elementAt(0); else return pingable_mbrs.elementAt(i + 1); } } return null; } 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"; default: return "n/a"; } } /* ------------------------------- End of Private Methods ------------------------------------ */ public static class FdHeader extends Header { 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) public FdHeader() { } 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 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(); String getName() { return acceptor != null? acceptor.getName() : null; } 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 { // this will terminate thread, peer will receive SocketException (socket close) getSocketFactory().close(srv_sock); } 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; 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; /** 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; 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.isTraceEnabled()) log.trace("broadcasting SUSPECT message (suspected_mbrs=" + suspected_mbrs + ") to group"); synchronized(suspected_mbrs) { if(suspected_mbrs.isEmpty()) { stopTask(); if(log.isTraceEnabled()) log.trace("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(id, hdr); down_prot.down(new Event(Event.MSG, suspect_msg)); if(log.isTraceEnabled()) log.trace("task done"); } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/FILE_PING.java0000644000175000017500000002130211647260573026047 0ustar moellermoellerpackage org.jgroups.protocols; import org.jgroups.*; import org.jgroups.annotations.Experimental; import org.jgroups.annotations.Property; import org.jgroups.util.Promise; import org.jgroups.util.UUID; import org.jgroups.util.Util; import java.io.*; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; /** * 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 */ @Experimental public class FILE_PING extends Discovery { protected static final String SUFFIX=".node"; /* ----------------------------------------- Properties -------------------------------------------------- */ @Property(description="The absolute path of the shared file") protected String location=File.separator + "tmp" + File.separator + "jgroups"; @Property(description="Interval (in milliseconds) at which the own Address is written. 0 disables it.") protected long interval=60000; /* --------------------------------------------- Fields ------------------------------------------------------ */ protected File root_dir=null; protected FilenameFilter filter; private Future writer_future; public void init() throws Exception { super.init(); createRootDir(); } public void start() throws Exception { super.start(); if(interval > 0) writer_future=timer.scheduleWithFixedDelay(new WriterTask(), interval, interval, TimeUnit.MILLISECONDS); } public void stop() { if(writer_future != null) { writer_future.cancel(false); writer_future=null; } super.stop(); } public boolean isDynamic() { return true; } public void sendGetMembersRequest(String cluster_name, Promise promise, ViewId view_id) throws Exception{ List existing_mbrs=readAll(cluster_name); PhysicalAddress physical_addr=(PhysicalAddress)down(new Event(Event.GET_PHYSICAL_ADDRESS, local_addr)); List physical_addrs=Arrays.asList(physical_addr); PingData data=new PingData(local_addr, null, false, UUID.get(local_addr), physical_addrs); // If we don't find any files, return immediately if(existing_mbrs.isEmpty()) { if(promise != null) { promise.setResult(null); } } else { // 1. Send GET_MBRS_REQ message to members listed in the file for(PingData tmp: existing_mbrs) { Collection dests=tmp != null? tmp.getPhysicalAddrs() : null; if(dests == null) continue; for(final PhysicalAddress dest: dests) { if(dest == null || dest.equals(physical_addr)) continue; PingHeader hdr=new PingHeader(PingHeader.GET_MBRS_REQ, data, cluster_name); hdr.view_id=view_id; final Message msg=new Message(dest); msg.setFlag(Message.OOB); msg.putHeader(this.id, hdr); // needs to be getName(), so we might get "MPING" ! // down_prot.down(new Event(Event.MSG, msg)); 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(data, cluster_name); } public Object down(Event evt) { Object retval=super.down(evt); if(evt.getType() == Event.VIEW_CHANGE) handleView((View)evt.getArg()); return retval; } protected void createRootDir() { 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); } }; } // remove all files which are not from the current members protected void handleView(View view) { Collection

      mbrs=view.getMembers(); boolean is_coordinator=!mbrs.isEmpty() && mbrs.iterator().next().equals(local_addr); if(is_coordinator) { List data=readAll(group_addr); for(PingData entry: data) { Address addr=entry.getAddress(); if(addr != null && !mbrs.contains(addr)) { remove(group_addr, addr); } } } } protected void remove(String clustername, Address addr) { if(clustername == null || addr == null) return; File dir=new File(root_dir, clustername); if(!dir.exists()) return; try { String filename=addr instanceof UUID? ((UUID)addr).toStringLong() : addr.toString(); File file=new File(dir, filename + SUFFIX); if(log.isTraceEnabled()) log.trace("removing " + file); file.delete(); } catch(Throwable e) { log.error("failure removing data", e); } } /** * 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) { PingData data=readFile(file); if(data == null) { log.warn("failed reading " + file.getName() + ": removing it"); file.delete(); } else retval.add(data); } } return retval; } protected static PingData readFile(File file) { PingData retval=null; DataInputStream in=null; try { in=new DataInputStream(new FileInputStream(file)); PingData tmp=new PingData(); tmp.readFrom(in); return tmp; } catch(Exception e) { } finally { Util.close(in); } return retval; } protected void writeToFile(PingData data, String clustername) { DataOutputStream out=null; File dir=new File(root_dir, clustername); if(!dir.exists()) dir.mkdir(); String filename=addressAsString(local_addr); File file=new File(dir, filename + SUFFIX); file.deleteOnExit(); try { out=new DataOutputStream(new FileOutputStream(file)); data.writeTo(out); } catch(Exception e) { } finally { Util.close(out); } } protected class WriterTask implements Runnable { public void run() { PhysicalAddress physical_addr=(PhysicalAddress)down(new Event(Event.GET_PHYSICAL_ADDRESS, local_addr)); List physical_addrs=Arrays.asList(physical_addr); PingData data=new PingData(local_addr, null, false, UUID.get(local_addr), physical_addrs); writeToFile(data, group_addr); } } protected String addressAsString(Address address) { if (address == null) { return ""; } else if (address instanceof UUID) { return ((UUID) address).toStringLong(); } else { return address.toString(); } } }libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/FRAG.java0000644000175000017500000005150611647260573025243 0ustar moellermoeller package org.jgroups.protocols; import org.jgroups.Address; import org.jgroups.Event; import org.jgroups.Message; import org.jgroups.View; import org.jgroups.annotations.MBean; import org.jgroups.annotations.ManagedAttribute; import org.jgroups.annotations.Property; import org.jgroups.stack.Protocol; import org.jgroups.util.ExposedByteArrayOutputStream; import org.jgroups.util.ExposedDataOutputStream; import org.jgroups.util.Util; import org.jgroups.util.ExposedByteArrayInputStream; import java.io.ByteArrayInputStream; import java.io.DataInputStream; import java.util.*; import java.util.Map.Entry; import java.util.concurrent.atomic.AtomicInteger; /** * Fragmentation layer. Fragments messages larger than FRAG_SIZE into smaller * packets. Reassembles fragmented packets into bigger ones. The fragmentation * number is added to the messages as a header (and removed at the receiving side). *

      * Contrary to {@link org.jgroups.protocols.FRAG2}, FRAG marshals the entire message (including the headers) into * a byte[] buffer and the fragments that buffer. Because {@link org.jgroups.Message#size()} is called rather than * {@link org.jgroups.Message#getLength()}, and because of the overhead of marshalling, this will be slower than * FRAG2. *

      * 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 */ @MBean(description="Fragments messages larger than fragmentation size into smaller packets") public class FRAG extends Protocol { /* ----------------------------------------- Properties -------------------------------------------------- */ @Property(description="The max number of bytes in a message. Larger messages will be fragmented. Default is 8192 bytes") private int frag_size=8192; // conservative value @Property(description="The max size in bytes for the byte array output buffer", deprecatedMessage="not used anymore") @Deprecated private int max_retained_buffer=70000; /* --------------------------------------------- Fields ------------------------------------------------------ */ /** Contains a frag table per sender, this way it becomes easier to clean up if a sender leaves or crashes */ private final FragmentationList fragment_list=new FragmentationList(); private AtomicInteger curr_id=new AtomicInteger(1); private final Vector

      members=new Vector
      (11); @ManagedAttribute(description="Number of sent messages") long num_sent_msgs=0; @ManagedAttribute(description="Number of sent fragments") long num_sent_frags=0; @ManagedAttribute(description="Number of received messages") long num_received_msgs=0; @ManagedAttribute(description="Number of received fragments") long num_received_frags=0; 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;} public void init() throws Exception { super.init(); Map info=new HashMap(1); info.put("frag_size", frag_size); up_prot.up(new Event(Event.CONFIG, info)); down_prot.down(new Event(Event.CONFIG, 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, size); // 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(this.id); 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; left_mbrs=Util.determineLeftMembers(members, new_mbrs); members.clear(); members.addAll(new_mbrs); for(Address mbr: left_mbrs){ // the new view doesn't contain the sender, it must have left, // hence we will clear all of itsfragmentation 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, long size) { Address dest=msg.getDest(), src=msg.getSrc(); long frag_id=curr_id.getAndIncrement(); // used as seqnos int num_frags; try { // write message into a byte buffer and fragment it ExposedByteArrayOutputStream out_stream=new ExposedByteArrayOutputStream((int)(size + 50)); ExposedDataOutputStream dos=new ExposedDataOutputStream(out_stream); msg.writeTo(dos); byte[] buffer=out_stream.getRawBuffer(); byte[][] fragments=Util.fragmentBuffer(buffer, frag_size, dos.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++) { Message frag_msg=new Message(dest, src, fragments[i]); FragHeader hdr=new FragHeader(frag_id, i, num_frags); frag_msg.putHeader(this.id, hdr); Event evt=new Event(Event.MSG, frag_msg); down_prot.down(evt); } } catch(Exception e) { log.error("exception occurred trying to fragment message", 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) { Address sender=msg.getSrc(); FragmentationTable 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++; byte[] buf=frag_table.add(hdr.id, hdr.frag_id, hdr.num_frags, msg.getBuffer()); if(buf != null) { DataInputStream in=null; try { ByteArrayInputStream bis=new ExposedByteArrayInputStream(buf); in=new DataInputStream(bis); Message assembled_msg=new Message(false); assembled_msg.readFrom(in); assembled_msg.setSrc(sender); // needed ? YES, because fragments have a null src !! if(log.isTraceEnabled()) log.trace("assembled_msg is " + assembled_msg); 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 { synchronized(frag_tables) { FragmentationTable healthCheck=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 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++]=it.next(); } } return result; } public String toString() { 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 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 FragEntry { //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 */ FragEntry(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; FragEntry e=h.get(new Long(id)); if(e == null) { // Create new entry if not yet present e=new FragEntry(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"); Enumeration e=this.h.elements(); while(e.hasMoreElements()) { FragEntry 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(); } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/FRAG2.java0000644000175000017500000004005211647260573025317 0ustar moellermoellerpackage org.jgroups.protocols; import org.jgroups.Address; import org.jgroups.Event; import org.jgroups.Message; import org.jgroups.View; import org.jgroups.annotations.*; import org.jgroups.stack.Protocol; import org.jgroups.util.Range; import org.jgroups.util.Util; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Vector; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * 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 */ @MBean(description="Fragments messages larger than fragmentation size into smaller packets") @DeprecatedProperty(names={"overhead"}) public class FRAG2 extends Protocol { /* ----------------------------------------- Properties -------------------------------------------------- */ @Property(description="The max number of bytes in a message. Larger messages will be fragmented") int frag_size=1500; /* --------------------------------------------- Fields ------------------------------------------------------ */ /*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 ConcurrentMap> fragment_list=Util.createConcurrentMap(11); /** Used to assign fragmentation-specific sequence IDs (monotonically increasing) */ private int curr_id=1; private final Vector

      members=new Vector
      (11); @ManagedAttribute(description="Number of sent messages") AtomicLong num_sent_msgs=new AtomicLong(0); @ManagedAttribute(description="Number of received messages") AtomicLong num_received_msgs=new AtomicLong(0); @ManagedAttribute(description="Number of sent fragments") AtomicLong num_sent_frags=new AtomicLong(0); @ManagedAttribute(description="Number of received fragments") AtomicLong num_received_frags=new AtomicLong(0); 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++; } public void init() throws Exception { super.init(); int old_frag_size=frag_size; if(frag_size <=0) throw new Exception("frag_size=" + old_frag_size + ", new frag_size=" + frag_size + ": new frag_size is invalid"); TP transport=getTransport(); if(transport != null && transport.isEnableBundling()) { int max_bundle_size=transport.getMaxBundleSize(); if(frag_size >= max_bundle_size) throw new IllegalArgumentException("frag_size (" + frag_size + ") has to be < TP.max_bundle_size (" + max_bundle_size + ")"); } Map info=new HashMap(1); info.put("frag_size", frag_size); up_prot.up(new Event(Event.CONFIG, info)); down_prot.down(new Event(Event.CONFIG, 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 fragmentation 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: 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(this.id); 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: 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; left_mbrs=Util.determineLeftMembers(members, new_mbrs); members.clear(); members.addAll(new_mbrs); for(Address mbr: left_mbrs) { // the new view doesn't contain the sender, it must have left, hence we will clear its fragmentation tables fragment_list.remove(mbr); if(log.isTraceEnabled()) log.trace("[VIEW_CHANGE] removed " + mbr + " from fragmentation table"); } } @ManagedOperation(description="removes all fragments sent by mbr") public void clearFragmentsFor(Address mbr) { if(mbr == null) return; fragment_list.remove(mbr); if(log.isTraceEnabled()) log.trace("removed " + mbr + " from fragmentation table"); } @ManagedOperation(description="Removes all entries from the fragmentation table. " + "Dangerous: this might remove fragments that are still needed to assemble an entire message") public void clearAllFragments() { fragment_list.clear(); } /** 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) { try { byte[] buffer=msg.getRawBuffer(); List fragments=Util.computeFragOffsets(msg.getOffset(), msg.getLength(), frag_size); int num_frags=fragments.size(); num_sent_frags.addAndGet(num_frags); if(log.isTraceEnabled()) { Address dest=msg.getDest(); 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()); } long frag_id=getNextId(); // used as a seqno for(int i=0; i < fragments.size(); i++) { Range r=fragments.get(i); // don't copy the buffer, only src, dest and headers. Only copy the headers one time ! Message frag_msg=msg.copy(false, i == 0); frag_msg.setBuffer(buffer, (int)r.low, (int)r.high); FragHeader hdr=new FragHeader(frag_id, i, num_frags); frag_msg.putHeader(this.id, hdr); down_prot.down(new Event(Event.MSG, frag_msg)); } } 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) { Address sender=msg.getSrc(); Message assembled_msg=null; ConcurrentMap frag_table=fragment_list.get(sender); if(frag_table == null) { frag_table=Util.createConcurrentMap(16, .075f, 16); ConcurrentMap tmp=fragment_list.putIfAbsent(sender, frag_table); if(tmp != null) // value was already present frag_table=tmp; } num_received_frags.incrementAndGet(); FragEntry entry=frag_table.get(hdr.id); if(entry == null) { entry=new FragEntry(hdr.num_frags); FragEntry tmp=frag_table.putIfAbsent(hdr.id, entry); if(tmp != null) entry=tmp; } entry.lock(); try { entry.set(hdr.frag_id, msg); if(entry.isComplete()) { assembled_msg=entry.assembleMessage(); frag_table.remove(hdr.id); } } finally { entry.unlock(); } // 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); } } /** * 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.
      * All methods are unsynchronized, use getLock() to obtain a lock for concurrent access. */ private static class FragEntry { // each fragment is a byte buffer final Message fragments[]; //the number of fragments we have received int number_of_frags_recvd=0; private final Lock lock=new ReentrantLock(); /** * Creates a new entry * @param tot_frags the number of fragments to expect for this message */ private FragEntry(int tot_frags) { fragments=new Message[tot_frags]; for(int i=0; i < tot_frags; i++) fragments[i]=null; } /** Use to synchronize on FragEntry */ public void lock() { lock.lock(); } public void unlock() { lock.unlock(); } /** * 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 a simple check*/ if(number_of_frags_recvd < fragments.length) { return false; } /*then double check just in case*/ for(Message msg: fragments) { if(msg == 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 * */ private Message assembleMessage() { Message retval; byte[] combined_buffer, tmp; int combined_length=0, length, offset; int index=0; for(Message fragment: fragments) combined_length+=fragment.getLength(); combined_buffer=new byte[combined_length]; retval=fragments[0].copy(false); // doesn't copy the payload, but copies the headers for(int i=0; i < fragments.length; i++) { Message fragment=fragments[i]; fragments[i]=null; // help garbage collection a bit tmp=fragment.getRawBuffer(); length=fragment.getLength(); offset=fragment.getOffset(); System.arraycopy(tmp, offset, combined_buffer, index, length); index+=length; } retval.setBuffer(combined_buffer); return retval; } public String toString() { StringBuilder ret=new StringBuilder(); ret.append("[tot_frags=").append(fragments.length).append(", number_of_frags_recvd=").append(number_of_frags_recvd).append(']'); return ret.toString(); } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/FcHeader.java0000644000175000017500000000216711647260573026164 0ustar moellermoellerpackage org.jgroups.protocols; import org.jgroups.Global; import org.jgroups.Header; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; /** * Header used by various flow control protocols * @author Bela Ban */ public class FcHeader extends Header { 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 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(); } public String toString() { switch(type) { case REPLENISH: return "REPLENISH"; case CREDIT_REQUEST: return "CREDIT_REQUEST"; default: return ""; } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/FlowControl.java0000644000175000017500000005421311647260573026772 0ustar moellermoellerpackage org.jgroups.protocols; import org.jgroups.Address; import org.jgroups.Event; import org.jgroups.Message; import org.jgroups.View; import org.jgroups.annotations.MBean; import org.jgroups.annotations.ManagedAttribute; import org.jgroups.annotations.ManagedOperation; import org.jgroups.annotations.Property; import org.jgroups.stack.Protocol; import org.jgroups.util.Util; 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. * * @author Bela Ban */ @MBean(description="Simple flow control protocol based on a credit system") public abstract class FlowControl extends Protocol { protected final static FcHeader REPLENISH_HDR=new FcHeader(FcHeader.REPLENISH); protected final static FcHeader CREDIT_REQUEST_HDR=new FcHeader(FcHeader.CREDIT_REQUEST); /* ----------------------------------------- Properties -------------------------------------------------- */ /** * Max number of bytes to send per receiver until an ack must be received before continuing sending */ @Property(description="Max number of bytes to send per receiver until an ack must be received to proceed") protected 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. */ @Property(description="Max time (in milliseconds) to block. Default is 5000 msec") protected 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, * FlowControl blocks it for a max of 100 ms. */ protected Map max_block_times=null; /** * If we're down to (min_threshold * max_credits) bytes for P, we send more credits to P. Example: if * max_credits is 1'000'000, and min_threshold 0.25, then we send ca. 250'000 credits to P once we've got only * 250'000 credits left for P (we've received 750'000 bytes from P). */ @Property(description="The threshold (as a percentage of max_credits) at which a receiver sends more credits to " + "a sender. Example: if max_credits is 1'000'000, and min_threshold 0.25, then we send ca. 250'000 credits " + "to P once we've got only 250'000 credits left for P (we've received 750'000 bytes from P)") protected double min_threshold=0.40; /** * Computed as max_credits times min_theshold. If explicitly set, this will * override the above computation */ @Property(description="Computed as max_credits x min_theshold unless explicitly set") protected long min_credits=0; /** * 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 */ @Property(description="Does not block a down message if it is a result of handling an up message in the" + "same thread. Fixes JGRP-928") protected boolean ignore_synchronous_response=true; /* --------------------------------------------- JMX ------------------------------------------------------ */ protected int num_credit_requests_received=0, num_credit_requests_sent=0; protected int num_credit_responses_sent=0, num_credit_responses_received=0; /* --------------------------------------------- Fields ------------------------------------------------------ */ /** * Keeps track of credits per member at the receiver. For each message, the credits for the sender are decremented * by the size of the received message. When the credits fall below the threshold, we refill and send a REPLENISH * message to the sender. */ protected final Map received=Util.createConcurrentMap(); /** Whether FlowControl is still running, this is set to false when the protocol terminates (on stop()) */ protected volatile boolean running=true; protected boolean frag_size_received=false; /** * Thread that carries messages through up() and shouldn't be blocked * in down() if ignore_synchronous_response==true. JGRP-465. */ protected final ThreadLocal ignore_thread=new ThreadLocal() { protected Boolean initialValue() { return false; } }; public void resetStats() { super.resetStats(); num_credit_responses_sent=num_credit_responses_received=num_credit_requests_received=num_credit_requests_sent=0; } 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 abstract int getNumberOfBlockings(); public long getMaxBlockTime() { return max_block_time; } public void setMaxBlockTime(long t) { max_block_time=t; } @Property(description="Max times to block for the listed messages sizes (Message.getLength()). Example: \"1000:10,5000:30,10000:500\"") public void setMaxBlockTimes(String str) { if(str == null) return; Long prev_key=null, prev_val=null; List vals=Util.parseCommaDelimitedStrings(str); if(max_block_times == null) 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); } public String getMaxBlockTimes() { if(max_block_times == null) return "n/a"; StringBuilder sb=new StringBuilder(); boolean first=true; for(Map.Entry entry: max_block_times.entrySet()) { if(!first) { sb.append(", "); } else { first=false; } sb.append(entry.getKey()).append(":").append(entry.getValue()); } return sb.toString(); } public abstract long getTotalTimeBlocked(); @ManagedAttribute(description="Average time spent in a flow control block") public double getAverageTimeBlocked() { long number_of_blockings=getNumberOfBlockings(); return number_of_blockings == 0? 0.0 : getTotalTimeBlocked() / (double)number_of_blockings; } @ManagedAttribute(description="Number of credit requests received") public int getNumberOfCreditRequestsReceived() { return num_credit_requests_received; } @ManagedAttribute(description="Number of credit requests sent") public int getNumberOfCreditRequestsSent() { return num_credit_requests_sent; } @ManagedAttribute(description="Number of credit responses received") public int getNumberOfCreditResponsesReceived() { return num_credit_responses_received; } @ManagedAttribute(description="Number of credit responses sent") public int getNumberOfCreditResponsesSent() { return num_credit_responses_sent; } public abstract String printSenderCredits(); @ManagedOperation(description="Print receiver credits") public String printReceiverCredits() { return printMap(received); } public String printCredits() { StringBuilder sb=new StringBuilder(); sb.append("receivers:\n").append(printMap(received)); return sb.toString(); } public Map dumpStats() { Map retval=super.dumpStats(); retval.put("receivers", printMap(received)); return retval; } protected 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; } /** * Whether the protocol handles message with dest == null || dest.isMulticastAddress() * @return */ protected abstract boolean handleMulticastMessage(); protected abstract void handleCredit(Address sender, long increase); /** * Allows to unblock all blocked senders from an external program, e.g. JMX */ @ManagedOperation(description="Unblocks all senders") public void unblock() { ; } public void init() throws Exception { boolean min_credits_set = min_credits != 0; if(!min_credits_set) min_credits=(long)(max_credits * min_threshold); } public void start() throws Exception { super.start(); if(!frag_size_received) { log.warn("No fragmentation protocol was found. When flow control 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; ignore_thread.set(false); } @SuppressWarnings("unchecked") public Object down(Event evt) { switch(evt.getType()) { case Event.MSG: Message msg=(Message)evt.getArg(); if(msg.isFlagSet(Message.NO_FC)) break; Address dest=msg.getDest(); boolean multicast=dest == null || dest.isMulticastAddress(); boolean handle_multicasts=handleMulticastMessage(); boolean process=(handle_multicasts && multicast) || (!handle_multicasts && !multicast); if(!process) break; int length=msg.getLength(); if(length == 0) break; if(ignore_synchronous_response && ignore_thread.get()) { // JGRP-465 if(log.isTraceEnabled()) log.trace("bypassing flow control because of synchronous response " + Thread.currentThread()); break; } return handleDownMessage(evt, msg, dest, length); case Event.CONFIG: handleConfigEvent((Map)evt.getArg()); break; 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 } @SuppressWarnings("unchecked") public Object up(Event evt) { switch(evt.getType()) { case Event.MSG: Message msg=(Message)evt.getArg(); if(msg.isFlagSet(Message.NO_FC)) break; Address dest=msg.getDest(); boolean multicast=dest == null || dest.isMulticastAddress(); boolean handle_multicasts=handleMulticastMessage(); FcHeader hdr=(FcHeader)msg.getHeader(this.id); boolean process=(handle_multicasts && multicast) || (!handle_multicasts && !multicast) || hdr != null; if(!process) break; if(hdr != null) { switch(hdr.type) { case FcHeader.REPLENISH: num_credit_responses_received++; handleCredit(msg.getSrc(), (Long)msg.getObject()); break; case FcHeader.CREDIT_REQUEST: num_credit_requests_received++; Address sender=msg.getSrc(); Long requested_credits=(Long)msg.getObject(); if(requested_credits != null) handleCreditRequest(received, sender, requested_credits.longValue()); 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, 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) sendCredit(sender, new_credits); } case Event.VIEW_CHANGE: handleViewChange(((View)evt.getArg()).getMembers()); break; case Event.CONFIG: Map map=(Map)evt.getArg(); handleConfigEvent(map); break; } return up_prot.up(evt); } protected void handleConfigEvent(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 cause blockings. Frag size should be less than max_credits " + "(http://jira.jboss.com/jira/browse/JGRP-590)"); } frag_size_received=true; } } } protected abstract Object handleDownMessage(final Event evt, final Message msg, Address dest, int length); /** * Check whether sender has enough credits left. If not, send it some more * @param map The hashmap to use * @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 */ protected long adjustCredit(Map map, Address sender, int length) { Credit cred; if(sender == null || length == 0 || (cred=map.get(sender)) == null) return 0; if(log.isTraceEnabled()) log.trace(sender + " used " + length + " credits, " + (cred.get() - length) + " remaining"); return cred.decrementAndGet(length); } /** * @param map The map to modify * @param sender The sender who requests credits * @param requested_credits Number of bytes that the sender has left to send messages to us */ protected void handleCreditRequest(Map map, Address sender, long requested_credits) { if(requested_credits > 0 && sender != null) { Credit cred=map.get(sender); if(cred == null) return; if(log.isTraceEnabled()) log.trace("received credit request from " + sender + ": sending " + requested_credits + " credits"); cred.increment(requested_credits); sendCredit(sender, requested_credits); } } protected void sendCredit(Address dest, long credits) { if(log.isTraceEnabled()) if(log.isTraceEnabled()) log.trace("sending " + credits + " credits to " + dest); Message msg=new Message(dest, null, new Long(credits)); msg.setFlag(Message.OOB); msg.putHeader(this.id, 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_needed The number of bytes (of credits) left for dest */ protected void sendCreditRequest(final Address dest, Long credits_needed) { if(log.isTraceEnabled()) log.trace("sending request for " + credits_needed + " credits to " + dest); Message msg=new Message(dest, null, credits_needed); msg.putHeader(this.id, CREDIT_REQUEST_HDR); down_prot.down(new Event(Event.MSG, msg)); num_credit_requests_sent++; } protected void handleViewChange(Vector
      mbrs) { if(mbrs == null) return; if(log.isTraceEnabled()) log.trace("new membership: " + mbrs); // add members not in membership to received and sent hashmap (with full credits) for(Address addr: mbrs) { if(!received.containsKey(addr)) received.put(addr, new Credit(max_credits)); } // remove members that left for(Iterator
      it=received.keySet().iterator(); it.hasNext();) { Address addr=it.next(); if(!mbrs.contains(addr)) it.remove(); } } protected 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(); } protected class Credit { protected long credits_left; protected int num_blockings=0; protected long total_blocking_time=0; protected long last_credit_request=0; protected Credit(long credits) { this.credits_left=credits; } protected synchronized boolean decrementIfEnoughCredits(long credits, long timeout) { if(decrement(credits)) return true; if(timeout <= 0) return false; long start=System.currentTimeMillis(); try { this.wait(timeout); } catch(InterruptedException e) { } finally { total_blocking_time+=System.currentTimeMillis() - start; num_blockings++; } return decrement(credits); } protected boolean decrement(long credits) { if(credits <= credits_left) { credits_left-=credits; return true; } return false; } protected synchronized long decrementAndGet(long credits) { credits_left=Math.max(0, credits_left - credits); if(credits_left <= min_credits) { long credit_response=Math.min(max_credits, max_credits - credits_left); credits_left=max_credits; return credit_response; } return 0; } protected synchronized void increment(long credits) { credits_left=Math.min(max_credits, credits_left + credits); notifyAll(); } protected synchronized boolean needToSendCreditRequest() { long current_time=System.currentTimeMillis(); if(current_time - last_credit_request >= max_block_time) { last_credit_request=current_time; return true; } return false; } protected int getNumBlockings() {return num_blockings;} protected long getTotalBlockingTime() {return total_blocking_time;} protected synchronized long get() {return credits_left;} protected synchronized void set(long new_credits) { credits_left=Math.min(max_credits, new_credits); notifyAll(); } public String toString() { return String.valueOf(credits_left); } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/FragHeader.java0000644000175000017500000000203711647260573026507 0ustar moellermoellerpackage org.jgroups.protocols; import org.jgroups.Global; import org.jgroups.Header; import java.io.*; /** * @author Bela Ban */ public class FragHeader extends Header { 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 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(); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/HDRS.java0000644000175000017500000000362511647260573025263 0ustar moellermoeller package org.jgroups.protocols; import org.jgroups.Event; import org.jgroups.Message; import org.jgroups.Header; import org.jgroups.conf.ClassConfigurator; import org.jgroups.annotations.Unsupported; import org.jgroups.stack.Protocol; import java.util.Map; /** * Example of a protocol layer. Contains no real functionality, can be used as a template. */ @Unsupported public class HDRS extends Protocol { 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()) { Class clazz=ClassConfigurator.getProtocol(entry.getKey()); String name=clazz != null? clazz.getSimpleName() : null; 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 } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/HTOTAL.java0000644000175000017500000001256311647260573025517 0ustar moellermoeller package org.jgroups.protocols; import org.jgroups.*; import org.jgroups.annotations.Experimental; import org.jgroups.annotations.Property; import org.jgroups.stack.Protocol; import org.jgroups.util.Util; import java.io.*; 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 */ @Experimental 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; @Property private boolean use_multipoint_forwarding=false; public HTOTAL() { } public Object down(Event evt) { switch(evt.getType()) { case Event.VIEW_CHANGE: determineCoordinatorAndNextMember((View)evt.getArg()); break; case Event.SET_LOCAL_ADDRESS: local_addr=(Address)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 { if(msg.getSrc() == null) 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.VIEW_CHANGE: determineCoordinatorAndNextMember((View)evt.getArg()); break; case Event.MSG: Message msg=(Message)evt.getArg(); HTotalHeader hdr=(HTotalHeader)msg.getHeader(this.id); 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(this.id); if(hdr == null) { hdr=new HTotalHeader(msg.getDest(), msg.getSrc()); msg.putHeader(this.id, 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 { Address dest, src; public HTotalHeader() { } public HTotalHeader(Address dest, Address src) { this.dest=dest; this.src=src; } 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 int size() { return Util.size(dest) + Util.size(src); } public String toString() { return "dest=" + dest + ", src=" + src; } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/JDBC_PING.java0000644000175000017500000003627111647260573026045 0ustar moellermoellerpackage org.jgroups.protocols; import org.jgroups.Address; import org.jgroups.annotations.Property; import org.jgroups.util.Util; import javax.naming.InitialContext; import javax.naming.NamingException; import javax.sql.DataSource; import java.sql.*; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** *

      Discovery protocol using a JDBC connection to a shared database. * Connection options can be defined as configuration properties, or the JNDI * name of a DataSource can be provided (avoid providing both).

      * *

      Both the schema and the used SQL statements can be customized; make sure * the order of parameters of such customized SQL statements is maintained and * that compatible types are used for the columns. The recommended schema uses a * single table, with two String columns being used primary key (local address, * cluster name) and a third column to store the serialized form of the objects * needed by JGroups.

      * *

      A default table will be created at first connection, errors during this * operation are not considered critical. Set the initialize_sql * to an empty value to prevent this initial table creation, or change it to * create a customized table.

      * * @author Sanne Grinovero * @since 2.12 */ public class JDBC_PING extends FILE_PING { /* ----------------------------------------- Properties -------------------------------------------------- */ @Property(description = "The JDBC connection URL", writable = false) protected String connection_url = null; @Property(description = "The JDBC connection username", writable = false) protected String connection_username = null; @Property(description = "The JDBC connection password", writable = false) protected String connection_password = null; @Property(description = "The JDBC connection driver name", writable = false) protected String connection_driver = null; @Property(description = "If not empty, this SQL statement will be performed at startup." + "Customize it to create the needed table on those databases which permit table creation attempt without loosing data, such as " + "PostgreSQL and MySQL (using IF NOT EXISTS). To allow for creation attempts, errors performing this statement will be logged" + "but not considered fatal. To avoid any DDL operation, set this to an empty string.") protected String initialize_sql = "CREATE TABLE JGROUPSPING (" + "own_addr varchar(200) NOT NULL, " + "cluster_name varchar(200) NOT NULL, " + "ping_data varbinary(5000) DEFAULT NULL, " + "PRIMARY KEY (own_addr, cluster_name) )"; @Property(description = "SQL used to insert a new row. Customizable, but keep the order of parameters and pick compatible types: " + "1)Own Address, as String 2)Cluster name, as String 3)Serialized PingData as byte[]") protected String insert_single_sql = "INSERT INTO JGROUPSPING (own_addr, cluster_name, ping_data) values (?, ?, ?)"; @Property(description = "SQL used to delete a row. Customizable, but keep the order of parameters and pick compatible types: " + "1)Own Address, as String 2)Cluster name, as String") protected String delete_single_sql = "DELETE FROM JGROUPSPING WHERE own_addr=? AND cluster_name=?"; @Property(description = "SQL used to fetch all node's PingData. Customizable, but keep the order of parameters and pick compatible types: " + "only one parameter needed, String compatible, representing the Cluster name. Must return a byte[], the Serialized PingData as" + " it was stored by the insert_single_sql statement") protected String select_all_pingdata_sql = "SELECT ping_data FROM JGROUPSPING WHERE cluster_name=?"; @Property(description = "To use a DataSource registered in JNDI, specify the JNDI name here. " + "This is an alternative to all connection_* configuration options: if this property is not empty, then all connection related" + "properties must be empty.") protected String datasource_jndi_name; /* --------------------------------------------- Fields ------------------------------------------------------ */ private DataSource dataSourceFromJNDI = null; @Override public void init() throws Exception { super.init(); verifyconfigurationParameters(); if (stringIsEmpty(datasource_jndi_name)) { loadDriver(); } else { dataSourceFromJNDI = getDataSourceFromJNDI(datasource_jndi_name.trim()); } attemptSchemaInitialization(); } @Override public void stop() { try { deleteSelf(); } catch (SQLException e) { log.error("Error while unregistering of our own Address from JDBC_PING database during shutdown", e); } super.stop(); } protected void attemptSchemaInitialization() { if (stringIsEmpty(initialize_sql)) { log.info("Table creation step skipped: initialize_sql property is missing"); return; } Connection connection = getConnection(); if (connection != null) { try { try { PreparedStatement preparedStatement = connection.prepareStatement(initialize_sql); preparedStatement.execute(); log.info("Table created for JDBC_PING Discovery Protocol"); } catch (SQLException e) { if (log.isDebugEnabled()) { log.debug("Could not execute initialize_sql statement; not necessarily an error.", e); } else { //avoid printing out the stacktrace log.info("Could not execute initialize_sql statement; not necessarily an error. Set to debug logging level for details."); } } } finally { try { connection.close(); } catch (SQLException e) { log.error("Error closing connection", e); } } } } protected void loadDriver() { if (stringIsEmpty(connection_driver)) { return; } if (log.isDebugEnabled()) { log.debug("Registering JDBC Driver named '" + connection_driver + "'"); } try { Class.forName(connection_driver); } catch (ClassNotFoundException e) { throw new IllegalArgumentException("JDBC Driver required for JDBC_PING Discovery" + "protocol could not be loaded: '" + connection_driver + "'"); } } protected Connection getConnection() { if (dataSourceFromJNDI == null) { Connection connection; try { connection = DriverManager.getConnection( connection_url, connection_username, connection_password); } catch (SQLException e) { log.error("Could not open connection to database", e); return null; } if (connection == null) { log.error("Received null connection from the DriverManager!"); } return connection; } else { try { return dataSourceFromJNDI.getConnection(); } catch (SQLException e) { log.error("Could not open connection to database", e); return null; } } } @Override protected void createRootDir() { // No-Op to prevent file creations from super.init(). // TODO refactor this class and FILE_PING to have a common parent? // would also be nice to remove unwanted configuration properties which where inherited. } @Override protected void remove(String clustername, Address addr) { final String addressAsString = addressAsString(addr); try { delete(clustername, addressAsString); } catch (SQLException e) { log.error("Error", e); } } @Override protected List readAll(String clustername) { final Connection connection = getConnection(); if (connection != null) { try { return readAll(connection, clustername); } catch (SQLException e) { log.error("Error reading JDBC_PING table", e); return Collections.emptyList(); } finally { closeConnection(connection); } } else { return Collections.emptyList(); } } protected List readAll(Connection connection, String clustername) throws SQLException { PreparedStatement ps = connection.prepareStatement(select_all_pingdata_sql); try { ps.setString(1, clustername); ResultSet resultSet = ps.executeQuery(); ArrayList results = new ArrayList(); while (resultSet.next()) { byte[] bytes = resultSet.getBytes(1); PingData pingData = deserialize(bytes); results.add(pingData); } return results; } finally { ps.close(); } } @Override protected void writeToFile(PingData data, String clustername) { final String ownAddress = addressAsString(data.getAddress()); final Connection connection = getConnection(); if (connection != null) { try { delete(connection, clustername, ownAddress); insert(connection, data, clustername, ownAddress); } catch (SQLException e) { log.error("Error updating JDBC_PING table", e); } finally { closeConnection(connection); } } else { log.error("Failed to store PingData in database"); } } protected void insert(Connection connection, PingData data, String clustername, String address) throws SQLException { final byte[] serializedPingData = serializeWithoutView(data); PreparedStatement ps = connection.prepareStatement(insert_single_sql); try { ps.setString(1, address); ps.setString(2, clustername); ps.setBytes(3, serializedPingData); ps.executeUpdate(); if (log.isDebugEnabled()) log.debug("Registered " + address + " for clustername " + clustername + " into database."); } finally { ps.close(); } } protected void delete(Connection connection, String clustername, String addressToDelete) throws SQLException { PreparedStatement ps = connection.prepareStatement(delete_single_sql); try { ps.setString(1, addressToDelete); ps.setString(2, clustername); ps.executeUpdate(); if (log.isDebugEnabled()) log.debug("Removed " + addressToDelete + " for clustername " + clustername + " from database."); } finally { ps.close(); } } protected void delete(String clustername, String addressToDelete) throws SQLException { final Connection connection = getConnection(); if (connection != null) { try { delete(connection, clustername, addressToDelete); } catch (SQLException e) { log.error("Error updating JDBC_PING table", e); } finally { closeConnection(connection); } } else { log.error("Failed to delete PingData in database"); } } protected void deleteSelf() throws SQLException { final String ownAddress = addressAsString(local_addr); delete(group_addr, ownAddress); } protected void closeConnection(final Connection connection) { try { connection.close(); } catch (SQLException e) { log.error("Error closing connection to JDBC_PING database", e); } } protected DataSource getDataSourceFromJNDI(String name) { final DataSource dataSource; InitialContext ctx = null; try { ctx = new InitialContext(); Object wathever = ctx.lookup(name); if (wathever == null) { throw new IllegalArgumentException( "JNDI name " + name + " is not bound"); } else if (!(wathever instanceof DataSource)) { throw new IllegalArgumentException( "JNDI name " + name + " was found but is not a DataSource"); } else { dataSource = (DataSource) wathever; if (log.isDebugEnabled()) { log.debug( "Datasource found via JNDI lookup via name: '"+ name + "'."); } return dataSource; } } catch (NamingException e) { throw new IllegalArgumentException( "Could not lookup datasource " + name, e); } finally { if (ctx != null) { try { ctx.close(); } catch (NamingException e) { log.warn("Failed to close naming context.", e); } } } } protected void verifyconfigurationParameters() { if (stringIsEmpty(this.connection_url) || stringIsEmpty(this.connection_driver) || stringIsEmpty(this.connection_url) || stringIsEmpty(this.connection_username) ) { if (stringIsEmpty(this.datasource_jndi_name)) { throw new IllegalArgumentException("Either the 4 configuration properties starting with 'connection_' or the datasource_jndi_name must be set"); } } if (stringNotEmpty(this.connection_url) || stringNotEmpty(this.connection_driver) || stringNotEmpty(this.connection_url) || stringNotEmpty(this.connection_username) ) { if (stringNotEmpty(this.datasource_jndi_name)) { throw new IllegalArgumentException("When using the 'datasource_jndi_name' configuration property, all properties starting with 'connection_' must not be set"); } } if (stringIsEmpty(this.insert_single_sql)) { throw new IllegalArgumentException("The insert_single_sql configuration property is mandatory"); } if (stringIsEmpty(this.delete_single_sql)) { throw new IllegalArgumentException("The delete_single_sql configuration property is mandatory"); } if (stringIsEmpty(this.select_all_pingdata_sql)) { throw new IllegalArgumentException("The select_all_pingdata_sql configuration property is mandatory"); } } private static final boolean stringIsEmpty(final String value) { return value == null || value.trim().length() == 0; } private static final boolean stringNotEmpty(final String value) { return value != null && value.trim().length() >= 0; } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/LOOPBACK.java0000644000175000017500000000511311647260573025707 0ustar moellermoeller package org.jgroups.protocols; import java.net.InetAddress; import java.util.HashMap; import java.util.Map; import java.util.Properties; import org.jgroups.Address; import org.jgroups.Event; import org.jgroups.Message; import org.jgroups.PhysicalAddress; 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; private final PhysicalAddress physical_addr=new IpAddress(12345); public LOOPBACK() { } public boolean supportsMulticasting() { return false; } public String toString() { return "LOOPBACK(local address: " + local_addr + ')'; } public void sendMulticast(byte[] data, int offset, int length) throws Exception { } public void sendUnicast(PhysicalAddress dest, byte[] data, int offset, int length) throws Exception { } public String getInfo() { return null; } protected PhysicalAddress getPhysicalAddress() { return physical_addr; } /*------------------------------ Protocol interface ------------------------------ */ /** * 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: case Event.CONNECT_USE_FLUSH: case Event.CONNECT_WITH_STATE_TRANSFER_USE_FLUSH: group_addr=(String)evt.getArg(); break; } return null; } /*--------------------------- End of Protocol interface -------------------------- */ } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/Locking.java0000644000175000017500000013307611647260573026115 0ustar moellermoellerpackage org.jgroups.protocols; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Queue; import java.util.Set; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.LockSupport; import org.jgroups.Address; import org.jgroups.Event; import org.jgroups.Header; import org.jgroups.Message; import org.jgroups.View; import org.jgroups.annotations.MBean; import org.jgroups.annotations.ManagedAttribute; import org.jgroups.annotations.ManagedOperation; import org.jgroups.annotations.Property; import org.jgroups.blocks.locking.AwaitInfo; import org.jgroups.blocks.locking.LockInfo; import org.jgroups.blocks.locking.LockNotification; import org.jgroups.blocks.locking.Owner; import org.jgroups.stack.Protocol; import org.jgroups.util.Streamable; import org.jgroups.util.Util; /** * Base locking protocol, handling most of the protocol communication with other instances. To use distributed locking, * {@link org.jgroups.blocks.locking.LockService} is placed on a channel. LockService talks to a subclass of Locking * via events. * @author Bela Ban * @since 2.12 * @see org.jgroups.protocols.CENTRAL_LOCK * @see org.jgroups.protocols.PEER_LOCK */ @MBean(description="Based class for locking functionality") abstract public class Locking extends Protocol { @Property(description="bypasses message bundling if set") protected boolean bypass_bundling=true; protected Address local_addr; protected View view; // server side locks protected final ConcurrentMap server_locks=Util.createConcurrentMap(20); // client side locks protected final Map> client_locks=new HashMap>(); protected final Set lock_listeners=new HashSet(); protected static enum Type { GRANT_LOCK, // request to acquire a lock LOCK_GRANTED, // response to sender of GRANT_LOCK on succcessful lock acquisition LOCK_DENIED, // response to sender of GRANT_LOCK on unsuccessful lock acquisition (e.g. on tryLock()) RELEASE_LOCK, // request to release a lock CREATE_LOCK, // request to create a server lock (sent by coordinator to backups). Used by CentralLockService DELETE_LOCK, // request to delete a server lock (sent by coordinator to backups). Used by CentralLockService LOCK_AWAIT, // request to await until condition is signaled COND_SIG, // request to signal awaiting thread COND_SIG_ALL, // request to signal all awaiting threads SIG_RET, // response to alert of signal DELETE_LOCK_AWAIT, // request to delete a waiter CREATE_AWAITER, // request to create a server lock await (sent by coordinator to backups). Used by CentralLockService DELETE_AWAITER // request to delete a server lock await (sent by coordinator to backups). Used by CentralLockService } public Locking() { } public boolean getBypassBundling() { return bypass_bundling; } public void setBypassBundling(boolean bypass_bundling) { this.bypass_bundling=bypass_bundling; } public void addLockListener(LockNotification listener) { if(listener != null) lock_listeners.add(listener); } public void removeLockListener(LockNotification listener) { if(listener != null) lock_listeners.remove(listener); } @ManagedAttribute public String getAddress() { return local_addr != null? local_addr.toString() : null; } @ManagedAttribute public String getView() { return view != null? view.toString() : null; } public Object down(Event evt) { switch(evt.getType()) { case Event.LOCK: LockInfo info=(LockInfo)evt.getArg(); ClientLock lock=getLock(info.getName()); if(!info.isTrylock()) { if(info.isLockInterruptibly()) { try { lock.lockInterruptibly(); } catch(InterruptedException e) { Thread.currentThread().interrupt(); // has to be checked by caller who has to rethrow ... } } else lock.lock(); } else { if(info.isUseTimeout()) { try { return lock.tryLock(info.getTimeout(), info.getTimeUnit()); } catch(InterruptedException e) { Thread.currentThread().interrupt(); } } else { return lock.tryLock(); } } return null; case Event.UNLOCK: info=(LockInfo)evt.getArg(); lock=getLock(info.getName(), false); if(lock != null) lock.unlock(); return null; case Event.UNLOCK_ALL: unlockAll(); return null; case Event.LOCK_AWAIT: info=(LockInfo)evt.getArg(); lock=getLock(info.getName(), false); if (lock == null || !lock.acquired) { throw new IllegalMonitorStateException(); } Condition condition = lock.newCondition(); if (info.isUseTimeout()) { try { return condition.awaitNanos(info.getTimeUnit().toNanos( info.getTimeout())); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } else if (info.isLockInterruptibly()) { try { condition.await(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } else { condition.awaitUninterruptibly(); } break; case Event.LOCK_SIGNAL: AwaitInfo awaitInfo = (AwaitInfo)evt.getArg(); lock=getLock(awaitInfo.getName(), false); if (lock == null || !lock.acquired) { throw new IllegalMonitorStateException(); } sendSignalConditionRequest(awaitInfo.getName(), awaitInfo.isAll()); break; case Event.SET_LOCAL_ADDRESS: local_addr=(Address)evt.getArg(); break; case Event.VIEW_CHANGE: handleView((View)evt.getArg()); break; } return down_prot.down(evt); } public Object up(Event evt) { switch(evt.getType()) { case Event.MSG: Message msg=(Message)evt.getArg(); LockingHeader hdr=(LockingHeader)msg.getHeader(id); if(hdr == null) break; Request req=(Request)msg.getObject(); if(log.isTraceEnabled()) log.trace("[" + local_addr + "] <-- [" + msg.getSrc() + "] " + req); switch(req.type) { case GRANT_LOCK: case RELEASE_LOCK: handleLockRequest(req); break; case LOCK_GRANTED: handleLockGrantedResponse(req.lock_name, req.owner, msg.getSrc()); break; case LOCK_DENIED: handleLockDeniedResponse(req.lock_name, req.owner); break; case CREATE_LOCK: handleCreateLockRequest(req.lock_name, req.owner); break; case DELETE_LOCK: handleDeleteLockRequest(req.lock_name); break; case COND_SIG: case COND_SIG_ALL: handleSignalRequest(req); break; case LOCK_AWAIT: handleAwaitRequest(req.lock_name, req.owner); handleLockRequest(req); break; case DELETE_LOCK_AWAIT: handleDeleteAwaitRequest(req.lock_name, req.owner); break; case SIG_RET: handleSignalResponse(req.lock_name, req.owner); break; case CREATE_AWAITER: handleCreateAwaitingRequest(req.lock_name, req.owner); break; case DELETE_AWAITER: handleDeleteAwaitingRequest(req.lock_name, req.owner); break; default: log.error("Request of type " + req.type + " not known"); break; } return null; case Event.VIEW_CHANGE: handleView((View)evt.getArg()); break; } return up_prot.up(evt); } protected ClientLock getLock(String name) { return getLock(name, getOwner(), true); } protected ClientLock getLock(String name, boolean create_if_absent) { return getLock(name, getOwner(), create_if_absent); } @ManagedOperation(description="Unlocks all currently held locks") public void unlockAll() { List locks=new ArrayList(); synchronized(client_locks) { Collection> maps=client_locks.values(); for(Map map: maps) { locks.addAll(map.values()); } } for(ClientLock lock: locks) lock.unlock(); } @ManagedOperation(description="Dumps all locks") public String printLocks() { StringBuilder sb=new StringBuilder(); sb.append("server locks:\n"); for(Map.Entry entry: server_locks.entrySet()) { sb.append(entry.getKey()).append(": ").append(entry.getValue()).append("\n"); } sb.append("\nmy locks: "); synchronized(client_locks) { boolean first_element=true; for(Map.Entry> entry: client_locks.entrySet()) { if(first_element) first_element=false; else sb.append(", "); sb.append(entry.getKey()).append(" ("); Map owners=entry.getValue(); boolean first=true; for(Map.Entry entry2: owners.entrySet()) { if(first) first=false; else sb.append(", "); sb.append(entry2.getKey()); ClientLock cl=entry2.getValue(); if(!cl.acquired || cl.denied) sb.append(", unlocked"); } sb.append(")"); } } return sb.toString(); } protected void handleView(View view) { this.view=view; if(log.isDebugEnabled()) log.debug("view=" + view); List
      members=view.getMembers(); for(Map.Entry entry: server_locks.entrySet()) { entry.getValue().handleView(members); } for(Map.Entry entry: server_locks.entrySet()) { ServerLock lock=entry.getValue(); if(lock.isEmpty() && lock.current_owner == null) server_locks.remove(entry.getKey()); } } protected ClientLock createLock(String lock_name) { return new ClientLock(lock_name); } protected Owner getOwner() { return new Owner(local_addr, Thread.currentThread().getId()); } abstract protected void sendGrantLockRequest(String lock_name, Owner owner, long timeout, boolean is_trylock); abstract protected void sendReleaseLockRequest(String lock_name, Owner owner); abstract protected void sendAwaitConditionRequest(String lock_name, Owner owner); abstract protected void sendSignalConditionRequest(String lock_name, boolean all); abstract protected void sendDeleteAwaitConditionRequest(String lock_name, Owner owner); protected void sendRequest(Address dest, Type type, String lock_name, Owner owner, long timeout, boolean is_trylock) { Request req=new Request(type, lock_name, owner, timeout, is_trylock); Message msg=new Message(dest, null, req); msg.putHeader(id, new LockingHeader()); if(bypass_bundling) msg.setFlag(Message.DONT_BUNDLE); if(log.isTraceEnabled()) log.trace("[" + local_addr + "] --> [" + (dest == null? "ALL" : dest) + "] " + req); try { down_prot.down(new Event(Event.MSG, msg)); } catch(Exception ex) { log.error("failed sending " + type + " request: " + ex); } } protected void sendLockResponse(Type type, Owner dest, String lock_name) { Request rsp=new Request(type, lock_name, dest, 0); Message lock_granted_rsp=new Message(dest.getAddress(), null, rsp); lock_granted_rsp.putHeader(id, new LockingHeader()); if(bypass_bundling) lock_granted_rsp.setFlag(Message.DONT_BUNDLE); if(log.isTraceEnabled()) log.trace("[" + local_addr + "] --> [" + dest.getAddress() + "] " + rsp); try { down_prot.down(new Event(Event.MSG, lock_granted_rsp)); } catch(Exception ex) { log.error("failed sending " + type + " message to " + dest + ": " + ex); } } protected void sendSignalResponse(Owner dest, String lock_name) { Request rsp=new Request(Type.SIG_RET, lock_name, dest, 0); Message lock_granted_rsp=new Message(dest.getAddress(), null, rsp); lock_granted_rsp.putHeader(id, new LockingHeader()); if(bypass_bundling) lock_granted_rsp.setFlag(Message.DONT_BUNDLE); if(log.isTraceEnabled()) log.trace("[" + local_addr + "] --> [" + dest.getAddress() + "] " + rsp); try { down_prot.down(new Event(Event.MSG, lock_granted_rsp)); } catch(Exception ex) { log.error("failed sending " + Type.SIG_RET + " message to " + dest + ": " + ex); } } protected void handleLockRequest(Request req) { ServerLock lock=server_locks.get(req.lock_name); if(lock == null) { lock=new ServerLock(req.lock_name); ServerLock tmp=server_locks.putIfAbsent(req.lock_name, lock); if(tmp != null) lock=tmp; else { notifyLockCreated(req.lock_name); } } lock.handleRequest(req); // We remove the lock if there is no waiters or owner if(lock.isEmpty() && lock.current_owner == null && lock.condition.queue.isEmpty()) { server_locks.remove(req.lock_name); } } protected void handleLockGrantedResponse(String lock_name, Owner owner, Address sender) { ClientLock lock=getLock(lock_name, owner, false); if(lock != null) lock.handleLockGrantedResponse(owner, sender); } protected void handleLockDeniedResponse(String lock_name, Owner owner) { ClientLock lock=getLock(lock_name, owner, false); if(lock != null) lock.lockDenied(); } protected void handleAwaitRequest(String lock_name, Owner owner) { ServerLock lock=server_locks.get(lock_name); if (lock != null) { lock.condition.addWaiter(owner); } else { log.error("Condition await was received but lock was not created. Waiter may block forever"); } } protected void handleDeleteAwaitRequest(String lock_name, Owner owner) { ServerLock lock=server_locks.get(lock_name); if (lock != null) { lock.condition.removeWaiter(owner); } else { log.error("Condition await delete was received, but lock was gone"); } } protected void handleSignalResponse(String lock_name, Owner owner) { ClientLock lock=getLock(lock_name, owner, false); if(lock != null) { synchronized (lock.condition) { lock.condition.signaled(); } } else { log.error("Condition response was client lock was not present. Ignored signal."); } } protected void handleSignalRequest(Request req) { ServerLock lock=server_locks.get(req.lock_name); if (lock != null) { lock.handleRequest(req); } else { log.error("Condition signal was received but lock was not created. Couldn't notify anyone."); } } protected void handleCreateLockRequest(String lock_name, Owner owner) { synchronized(server_locks) { server_locks.put(lock_name, new ServerLock(lock_name, owner)); } } protected void handleDeleteLockRequest(String lock_name) { synchronized(server_locks) { ServerLock lock = server_locks.get(lock_name); synchronized (lock.condition) { if (lock.condition.queue.isEmpty()) { server_locks.remove(lock_name); } else { lock.current_owner = null; } } } } protected void handleCreateAwaitingRequest(String lock_name, Owner owner) { synchronized(server_locks) { ServerLock lock = server_locks.get(lock_name); if (lock == null) { lock = new ServerLock(lock_name); } lock.condition.queue.add(owner); } } protected void handleDeleteAwaitingRequest(String lock_name, Owner owner) { synchronized(server_locks) { ServerLock lock = server_locks.get(lock_name); if (lock != null) { synchronized (lock.condition) { lock.condition.queue.remove(owner); if (lock.condition.queue.isEmpty() && lock.current_owner == null) { server_locks.remove(lock_name); } } } } } protected ClientLock getLock(String name, Owner owner, boolean create_if_absent) { synchronized(client_locks) { Map owners=client_locks.get(name); if(owners == null) { if(!create_if_absent) return null; owners=new HashMap(); client_locks.put(name, owners); } ClientLock lock=owners.get(owner); if(lock == null) { if(!create_if_absent) return null; lock=createLock(name); owners.put(owner, lock); } return lock; } } protected void removeClientLock(String lock_name, Owner owner) { synchronized(client_locks) { Map owners=client_locks.get(lock_name); if(owners != null) { ClientLock lock=owners.remove(owner); if(lock != null) { if(owners.isEmpty()) client_locks.remove(lock_name); } } } } protected void notifyLockCreated(String lock_name) { for(LockNotification listener: lock_listeners) { try { listener.lockCreated(lock_name); } catch(Throwable t) { log.error("failed notifying " + listener, t); } } } protected void notifyLockDeleted(String lock_name) { for(LockNotification listener: lock_listeners) { try { listener.lockDeleted(lock_name); } catch(Throwable t) { log.error("failed notifying " + listener, t); } } } protected void notifyLocked(String lock_name, Owner owner) { for(LockNotification listener: lock_listeners) { try { listener.locked(lock_name, owner); } catch(Throwable t) { log.error("failed notifying " + listener, t); } } } protected void notifyUnlocked(String lock_name, Owner owner) { for(LockNotification listener: lock_listeners) { try { listener.unlocked(lock_name, owner); } catch(Throwable t) { log.error("failed notifying " + listener, t); } } } protected void notifyAwaiting(String lock_name, Owner owner) { for(LockNotification listener: lock_listeners) { try { listener.awaiting(lock_name, owner); } catch(Throwable t) { log.error("failed notifying " + listener, t); } } } protected void notifyAwaited(String lock_name, Owner owner) { for(LockNotification listener: lock_listeners) { try { listener.awaited(lock_name, owner); } catch(Throwable t) { log.error("failed notifying " + listener, t); } } } /** * Server side queue for handling of lock requests (lock, release). * @author Bela Ban */ protected class ServerLock { protected final String lock_name; protected Owner current_owner; protected final List queue=new ArrayList(); protected final ServerCondition condition; public ServerLock(String lock_name) { this.lock_name=lock_name; this.condition=new ServerCondition(this); } protected ServerLock(String lock_name, Owner owner) { this.lock_name=lock_name; this.current_owner=owner; this.condition=new ServerCondition(this); } protected synchronized void handleRequest(Request req) { switch(req.type) { case GRANT_LOCK: if(current_owner == null) { setOwner(req.owner); sendLockResponse(Type.LOCK_GRANTED, req.owner, req.lock_name); } else { if(current_owner.equals(req.owner)) { sendLockResponse(Type.LOCK_GRANTED, req.owner, req.lock_name); } else { if(req.is_trylock && req.timeout <= 0) sendLockResponse(Type.LOCK_DENIED, req.owner, req.lock_name); else addToQueue(req); } } break; case RELEASE_LOCK: case LOCK_AWAIT: if(current_owner == null) break; if(current_owner.equals(req.owner)) setOwner(null); else addToQueue(req); break; case COND_SIG: condition.signal(false); break; case COND_SIG_ALL: condition.signal(true); break; default: throw new IllegalArgumentException("type " + req.type + " is invalid here"); } processQueue(); } protected synchronized void handleView(List
      members) { if(current_owner != null && !members.contains(current_owner.getAddress())) { Owner tmp=current_owner; setOwner(null); if(log.isDebugEnabled()) log.debug("unlocked \"" + lock_name + "\" because owner " + tmp + " left"); } for(Iterator it=queue.iterator(); it.hasNext();) { Request req=it.next(); if(!members.contains(req.owner.getAddress())) it.remove(); } for(Iterator it=condition.queue.iterator(); it.hasNext();) { Owner own=it.next(); if(!members.contains(own.getAddress())) { it.remove(); } } processQueue(); } protected void addToQueue(Request req) { if(queue.isEmpty()) { if(req.type == Type.GRANT_LOCK) queue.add(req); return; // RELEASE_LOCK is discarded on an empty queue } // at this point the queue is not empty switch(req.type) { // If there is already a lock request from the same owner, discard the new lock request case GRANT_LOCK: if(!isRequestPresent(Type.GRANT_LOCK, req.owner)) queue.add(req); break; case RELEASE_LOCK: // Release the lock request from the same owner already in the queue // If there is no lock request, discard the unlock request removeRequest(Type.GRANT_LOCK, req.owner); break; } } /** Checks if a certain request from a given owner is already in the queue */ protected boolean isRequestPresent(Type type, Owner owner) { for(Request req: queue) if(req.type == type && req.owner.equals(owner)) return true; return false; } protected void removeRequest(Type type, Owner owner) { for(Iterator it=queue.iterator(); it.hasNext();) { Request req=it.next(); if(req.type == type && req.owner.equals(owner)) it.remove(); } } protected void processQueue() { if(current_owner == null) { while(!queue.isEmpty()) { Request req=queue.remove(0); if(req.type == Type.GRANT_LOCK) { setOwner(req.owner); sendLockResponse(Type.LOCK_GRANTED, req.owner, req.lock_name); break; } } } } protected void setOwner(Owner owner) { if(owner == null) { if(current_owner != null) { Owner tmp=current_owner; current_owner=null; notifyUnlocked(lock_name, tmp); } } else { current_owner=owner; notifyLocked(lock_name, owner); } } public boolean isEmpty() {return queue.isEmpty();} public String toString() { StringBuilder sb=new StringBuilder(); sb.append(current_owner); if(!queue.isEmpty()) { sb.append(", queue: "); for(Request req: queue) { sb.append(req.toStringShort()).append(" "); } } return sb.toString(); } } protected class ServerCondition { protected final ServerLock lock; protected final Queue queue=new ArrayDeque(); public ServerCondition(ServerLock lock) { this.lock = lock; } public synchronized void addWaiter(Owner waiter) { notifyAwaiting(lock.lock_name, waiter); if (log.isTraceEnabled()) { log.trace("Waiter [" + waiter + "] was added for " + lock.lock_name); } queue.add(waiter); } public synchronized void removeWaiter(Owner waiter) { notifyAwaited(lock.lock_name, waiter); if (log.isTraceEnabled()) { log.trace("Waiter [" + waiter + "] was removed for " + lock.lock_name); } queue.remove(waiter); } public synchronized void signal(boolean all) { if (queue.isEmpty()) { if (log.isTraceEnabled()) { log.trace("Signal for [" + lock.lock_name + "] ignored since, no one is waiting in queue."); } } Owner entry; if (all) { while ((entry = queue.poll()) != null) { notifyAwaited(lock.lock_name, entry); if (log.isTraceEnabled()) { log.trace("Signalled " + entry + " for " + lock.lock_name); } sendSignalResponse(entry, lock.lock_name); } } else { entry = queue.poll(); if (entry != null) { notifyAwaited(lock.lock_name, entry); if (log.isTraceEnabled()) { log.trace("Signalled " + entry + " for " + lock.lock_name); } sendSignalResponse(entry, lock.lock_name); } } } } protected class ClientLock implements Lock { protected final String name; protected Owner owner; protected volatile boolean acquired; protected volatile boolean denied; protected volatile boolean is_trylock; protected long timeout; protected final ClientCondition condition; public ClientLock(String name) { this.name=name; this.condition = new ClientCondition(this); } public void lock() { try { acquire(false); } catch(InterruptedException e) { // This should never happen } } public void lockInterruptibly() throws InterruptedException { acquire(true); } public boolean tryLock() { try { return acquireTryLock(0, false); } catch(InterruptedException e) { return false; } } public boolean tryLock(long time, TimeUnit unit) throws InterruptedException { return acquireTryLock(TimeUnit.MILLISECONDS.convert(time, unit), true); } public synchronized void unlock() { _unlock(false); } public Condition newCondition() { // Currently only 1 condition per Lock is supported return condition; } public String toString() { return name + " (locked=" + acquired +")"; } protected synchronized void lockGranted() { acquired=true; this.notifyAll(); } protected synchronized void lockDenied() { denied=true; this.notifyAll(); } protected void handleLockGrantedResponse(Owner owner, Address sender) { lockGranted(); } protected synchronized void acquire(boolean throwInterrupt) throws InterruptedException { if(!acquired) { owner=getOwner(); sendGrantLockRequest(name, owner, 0, false); boolean interrupted=false; while(!acquired) { try { this.wait(); } catch (InterruptedException e) { // If we haven't acquired the lock yet and were interrupted, then we have to clean up the lock // request and throw the exception if (throwInterrupt && !acquired) { _unlock(true); throw e; } // If we did get the lock then we will return with the lock and interrupt status. // If we don't throw exceptions then we just set the interrupt flag and let it loop around interrupted=true; } } if(interrupted) Thread.currentThread().interrupt(); } } protected synchronized void _unlock(boolean force) { if(!acquired && !denied && !force) return; this.timeout=0; this.is_trylock=false; sendReleaseLockRequest(name, owner); acquired=denied=false; notifyAll(); removeClientLock(name, owner); notifyLockDeleted(name); owner=null; } protected synchronized boolean acquireTryLock(long timeout, boolean use_timeout) throws InterruptedException { if(denied) return false; if(!acquired) { is_trylock=true; this.timeout=timeout; owner=getOwner(); sendGrantLockRequest(name, owner, timeout, true); long target_time=use_timeout? System.currentTimeMillis() + timeout : 0; boolean interrupted = false; while(!acquired && !denied) { if(use_timeout) { long wait_time=target_time - System.currentTimeMillis(); if(wait_time <= 0) break; else { this.timeout=wait_time; try { this.wait(wait_time); } catch (InterruptedException e) { // If we were interrupted and haven't received a response yet then we try to // clean up the lock request and throw the exception if (!acquired && !denied) { _unlock(true); throw e; } // In the case that we were told if we acquired or denied the lock then return that, but // make sure we set the interrupt status interrupted = true; } } } else { try { this.wait(); } catch(InterruptedException e) { interrupted = true; } } } if(interrupted) Thread.currentThread().interrupt(); } if(!acquired || denied) _unlock(true); return acquired && !denied; } } protected class ClientCondition implements Condition { protected final ClientLock lock; protected final AtomicBoolean signaled = new AtomicBoolean(false); /** * This is okay only having 1 since a client condition is 1 per * lock_name, thread id combination. */ protected volatile AtomicReference parker=new AtomicReference(); public ClientCondition(ClientLock lock) { this.lock = lock; } @Override public void await() throws InterruptedException { InterruptedException ex = null; try { await(true); } catch (InterruptedException e) { ex = e; throw ex; } finally { lock.lock(); // If we are throwing an InterruptedException // then clear the interrupt state as well. if (ex != null) { Thread.interrupted(); } } } @Override public void awaitUninterruptibly() { try { await(false); } catch(InterruptedException e) { // This should never happen } finally { lock.lock(); } } @Override public long awaitNanos(long nanosTimeout) throws InterruptedException { long beforeLock; InterruptedException ex = null; try { beforeLock = await(nanosTimeout) + System.nanoTime(); } catch (InterruptedException e) { ex = e; throw ex; } finally { lock.lock(); // If we are throwing an InterruptedException // then clear the interrupt state as well. if (ex != null) { Thread.interrupted(); } } return beforeLock - System.nanoTime(); } /** * Note this wait will only work correctly if the converted value is less * than 292 years. This is due to the limitation in System.nano and long * values that can only store up to 292 years (2263 nanoseconds). * * For more information please see {@link System#nanoTime()} */ @Override public boolean await(long time, TimeUnit unit) throws InterruptedException { return awaitNanos(unit.toNanos(time)) > 0; } @Override public boolean awaitUntil(Date deadline) throws InterruptedException { long waitUntilTime = deadline.getTime(); long currentTime = System.currentTimeMillis(); long waitTime = waitUntilTime - currentTime; if (waitTime > 0) { return await(waitTime, TimeUnit.MILLISECONDS); } else { return false; } } protected void await(boolean throwInterrupt) throws InterruptedException { if(!signaled.get()) { lock.acquired = false; sendAwaitConditionRequest(lock.name, lock.owner); boolean interrupted=false; while(!signaled.get()) { parker.set(Thread.currentThread()); LockSupport.park(this); if (Thread.interrupted()) { // If we were interrupted and haven't received a response yet then we try to // clean up the lock request and throw the exception if (!signaled.get()) { sendDeleteAwaitConditionRequest(lock.name, lock.owner); throw new InterruptedException(); } // In the case that we were signaled and interrupted // we want to return the signal but still interrupt // our thread interrupted = true; } } if(interrupted) Thread.currentThread().interrupt(); } // We set as if this signal was no released. This way if the // condition is reused again, but the client condition isn't lost // we won't think we were signaled immediately signaled.set(false); } protected long await(long nanoSeconds) throws InterruptedException { long target_nano=System.nanoTime() + nanoSeconds; if(!signaled.get()) { // We release the lock at the same time as waiting on the // condition lock.acquired = false; sendAwaitConditionRequest(lock.name, lock.owner); boolean interrupted = false; while(!signaled.get()) { long wait_nano=target_nano - System.nanoTime(); // If we waited max time break out if(wait_nano > 0) { parker.set(Thread.currentThread()); LockSupport.parkNanos(this, wait_nano); if (Thread.interrupted()) { // If we were interrupted and haven't received a response yet then we try to // clean up the lock request and throw the exception if (!signaled.get()) { sendDeleteAwaitConditionRequest(lock.name, lock.owner); throw new InterruptedException(); } // In the case that we were signaled and interrupted // we want to return the signal but still interrupt // our thread interrupted = true; } } else { break; } } if(interrupted) Thread.currentThread().interrupt(); } // We set as if this signal was no released. This way if the // condition is reused again, but the client condition isn't lost // we won't think we were signaled immediately // If we weren't signaled then delete our request if (!signaled.getAndSet(false)) { sendDeleteAwaitConditionRequest(lock.name, lock.owner); } return target_nano - System.nanoTime(); } @Override public void signal() { sendSignalConditionRequest(lock.name, false); } @Override public void signalAll() { sendSignalConditionRequest(lock.name, true); } protected void signaled() { signaled.set(true); Thread thread = parker.getAndSet(null); if (thread != null) LockSupport.unpark(thread); } } protected static class Request implements Streamable { protected Type type; protected String lock_name; protected Owner owner; protected long timeout=0; protected boolean is_trylock; public Request() { } public Request(Type type, String lock_name, Owner owner, long timeout) { this.type=type; this.lock_name=lock_name; this.owner=owner; this.timeout=timeout; } public Request(Type type, String lock_name, Owner owner, long timeout, boolean is_trylock) { this(type, lock_name, owner, timeout); this.is_trylock=is_trylock; } public void writeTo(DataOutputStream out) throws IOException { out.writeByte(type.ordinal()); Util.writeString(lock_name, out); Util.writeStreamable(owner, out); out.writeLong(timeout); out.writeBoolean(is_trylock); } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { type=Type.values()[in.readByte()]; lock_name=Util.readString(in); owner=(Owner)Util.readStreamable(Owner.class, in); timeout=in.readLong(); is_trylock=in.readBoolean(); } public String toString() { return type.name() + " [" + lock_name + ", owner=" + owner + (is_trylock? ", trylock " : " ") + (timeout > 0? "(timeout=" + timeout + ")" : "" + "]"); } public String toStringShort() { StringBuilder sb=new StringBuilder(); switch(type) { case RELEASE_LOCK: sb.append("U"); break; case GRANT_LOCK: sb.append(is_trylock? "TL" : "L"); break; default: sb.append("N/A"); break; } sb.append("(").append(lock_name).append(",").append(owner); if(timeout > 0) sb.append(",").append(timeout); sb.append(")"); return sb.toString(); } } public static class LockingHeader extends Header { public LockingHeader() { } public int size() { return 0; } public void writeTo(DataOutputStream out) throws IOException { } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/MERGE2.java0000644000175000017500000003171111647260573025441 0ustar moellermoellerpackage org.jgroups.protocols; import org.jgroups.*; import org.jgroups.annotations.*; import org.jgroups.stack.Protocol; import org.jgroups.util.TimeScheduler; import org.jgroups.util.Util; import java.util.*; import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * 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 */ @MBean(description="Protocol to discover subgroups existing due to a network partition") @DeprecatedProperty(names={"use_separate_thread"}) public class MERGE2 extends Protocol { /* ----------------------------------------- Properties -------------------------------------------------- */ @Property(description="Minimum time in msbetween runs to discover other clusters") protected long min_interval=5000; @Property(description="Maximum time in ms between runs to discover other clusters") protected long max_interval=20000; @Property(description="Number of inconsistent views with only 1 coord after a MERGE event is sent up") protected int inconsistent_view_threshold=1; @Property(description="When receiving a multicast message, checks if the sender is member of the cluster. " + "If not, initiates a merge") protected boolean merge_fast=true; @Property(description="The delay (in milliseconds) after which a merge fast execution is started") protected long merge_fast_delay=1000; /* ---------------------------------------------- JMX -------------------------------------------------------- */ @ManagedAttribute(writable=false, description="whether or not a merge task is currently running " + "(should be the case in a coordinator") public boolean isMergeTaskRunning() { return task.isRunning(); } /* --------------------------------------------- Fields ------------------------------------------------------ */ private Address local_addr=null; private View view; private final Set

      members=new HashSet
      (); private final Set
      merge_candidates=new CopyOnWriteArraySet
      (); private final FindSubgroupsTask task=new FindSubgroupsTask(); private volatile boolean is_coord=false; private TimeScheduler timer; @ManagedAttribute(description="Number of inconsistent 1-coord views until a MERGE event is sent up the stack") private int num_inconsistent_views=0; public MERGE2() { } public void init() throws Exception { timer=getTransport().getTimer(); if(timer == null) throw new Exception("timer cannot be retrieved"); if(min_interval <= 0 || max_interval <= 0) { throw new Exception("min_interval and max_interval have to be > 0"); } if(max_interval <= min_interval) { throw new Exception ("max_interval has to be greater than min_interval"); } } 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 Vector requiredDownServices() { Vector retval=new Vector(1); retval.addElement(new Integer(Event.FIND_INITIAL_MBRS)); return retval; } /** Discovers members and detects whether we have multiple coordinator. If so, kicks off a merge */ @ManagedOperation public void sendMergeSolicitation() { task.findAndNotify(); } @ManagedOperation public void startMergeTask() {task.start();} @ManagedOperation public void stopMergeTask() {task.stop();} public void stop() { is_coord=false; merge_candidates.clear(); task.stop(); } public Object down(Event evt) { switch(evt.getType()) { case Event.VIEW_CHANGE: Object ret=down_prot.down(evt); view=(View)evt.getArg(); Vector
      mbrs=view.getMembers(); if(mbrs == null || mbrs.isEmpty() || local_addr == null) { task.stop(); return ret; } members.clear(); members.addAll(mbrs); merge_candidates.removeAll(members); 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; case Event.SET_LOCAL_ADDRESS: local_addr=(Address)evt.getArg(); return down_prot.down(evt); default: return down_prot.down(evt); // Pass on to the layer below us } } public Object up(Event evt) { switch(evt.getType()) { case Event.MSG: if(!merge_fast) break; Message msg=(Message)evt.getArg(); Address dest=msg.getDest(); boolean multicast=dest == null || dest.isMulticastAddress(); if(!multicast) break; final Address sender=msg.getSrc(); if(!members.contains(sender) && merge_candidates.add(sender)) { timer.schedule(new Runnable() { public void run() { if(!members.contains(sender)) task.findAndNotify(); } }, merge_fast_delay, TimeUnit.MILLISECONDS); } break; } return up_prot.up(evt); } /** * 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; private Lock lock=new ReentrantLock(); public synchronized void start() { if(future == null || future.isDone() || future.isCancelled()) { future=timer.scheduleWithFixedDelay(new Runnable() { public void run() { findAndNotify(); } }, Math.max(5000L, computeInterval()), computeInterval(), TimeUnit.MILLISECONDS); } } public synchronized void stop() { if(future != null) { future.cancel(true); future=null; } } public synchronized boolean isRunning() { return future != null && !future.isDone() && !future.isCancelled(); } public void findAndNotify() { if(lock.tryLock()) { try { _findAndNotify(); } finally { lock.unlock(); } } } private void _findAndNotify() { List discovery_rsps=findAllViews(); if(log.isTraceEnabled()) { StringBuilder sb=new StringBuilder(); sb.append("Discovery results:\n"); for(PingData data: discovery_rsps) sb.append("[" + data.getAddress() + "]: coord=" + data.getCoordAddress()).append("\n"); log.trace(sb); } // Create a map of senders and the views they sent Map views=getViews(discovery_rsps); // A list of different views List different_views=detectDifferentViews(views); if(different_views.size() <= 1) { num_inconsistent_views=0; return; } Collection
      merge_participants=Util.determineMergeParticipants(views); if(merge_participants.size() == 1) { if(num_inconsistent_views < inconsistent_view_threshold) { if(log.isDebugEnabled()) log.debug("dropping MERGE for inconsistent views " + Util.printViews(different_views) + " as inconsistent view threshold (" + inconsistent_view_threshold + ") has not yet been reached (" + num_inconsistent_views + ")"); num_inconsistent_views++; return; } else num_inconsistent_views=0; } else num_inconsistent_views=0; if(log.isDebugEnabled()) { StringBuilder sb=new StringBuilder(); sb.append(local_addr + " found different views : " + Util.printViews(different_views) + "; sending up MERGE event with merge participants " + merge_participants + ".\n"); sb.append("Discovery results:\n"); for(PingData data: discovery_rsps) sb.append("[" + data.getAddress() + "]: coord=" + data.getCoordAddress()).append("\n"); log.debug(sb.toString()); } Event evt=new Event(Event.MERGE, views); try { up_prot.up(evt); } catch(Throwable t) { log.error("failed sending up MERGE event", t); } } /** * Returns a random value within [min_interval - max_interval] */ long computeInterval() { return min_interval + Util.random(max_interval - min_interval); } /** Returns a list of PingData with only the view from members around the cluster */ @SuppressWarnings("unchecked") private List findAllViews() { List retval=(List)down_prot.down(new Event(Event.FIND_ALL_VIEWS)); if(retval == null) return Collections.emptyList(); if(is_coord && local_addr != null) { PingData tmp=new PingData(local_addr, view, true); //let's make sure that we add ourself as a coordinator if(!retval.contains(tmp)) retval.add(tmp); } return retval; } public Map getViews(List initial_mbrs) { Map retval=new HashMap(); for(PingData response: initial_mbrs) { if(!response.isServer()) continue; Address sender=response.getAddress(); View view=response.getView(); if(sender == null || view == null) continue; retval.put(sender,view); } return retval; } public List detectDifferentViews(Map map) { final List ret=new ArrayList(); for(View view: map.values()) { if(view == null) continue; ViewId vid=view.getVid(); if(!Util.containsViewId(ret, vid)) ret.add(view); } return ret; } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/MERGE3.java0000644000175000017500000001424011647260573025440 0ustar moellermoeller package org.jgroups.protocols; import org.jgroups.*; import org.jgroups.annotations.DeprecatedProperty; import org.jgroups.annotations.Experimental; import org.jgroups.annotations.Property; import org.jgroups.annotations.Unsupported; import org.jgroups.stack.Protocol; import org.jgroups.util.TimeScheduler; import org.jgroups.util.Util; import java.io.*; import java.util.HashMap; import java.util.Map; import java.util.Vector; 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 its view. If another coordinator receives such a message, and its own * view differs from the received view, it immediately initiates a merge by sending up a MERGE event, * containing the received view and its own view *

        * * Provides: sends MERGE event with list of different views up the stack
        * @author Bela Ban, Oct 16 2001 */ @Experimental @Unsupported @DeprecatedProperty(names={"use_separate_thread"}) public class MERGE3 extends Protocol { Address local_addr=null; View view; @Property long min_interval=5000; // minimum time between executions of the FindSubgroups task @Property 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; public void init() throws Exception { timer=getTransport().getTimer(); if(min_interval <= 0 || max_interval <= 0) { throw new Exception("min_interval and max_interval have to be > 0"); } if(max_interval <= min_interval) { throw new Exception ("max_interval has to be greater than min_interval"); } } public Object up(Event evt) { switch(evt.getType()) { case Event.MSG: Message msg=(Message)evt.getArg(); CoordAnnouncement hdr=(CoordAnnouncement)msg.getHeader(this.id); if(hdr != null) { if(is_coord) { ViewId other=hdr.view.getViewId(); if(!Util.sameViewId(other, view.getViewId())) { Map views=new HashMap(); views.put(local_addr, view); views.put(msg.getSrc(), hdr.view); if(log.isDebugEnabled()) log.debug("detected different views (" + Util.printViews(views.values()) + "), sending up MERGE event"); up_prot.up(new Event(Event.MERGE, views)); } } return null; } else return up_prot.up(evt); } 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); view=(View)evt.getArg(); tmp=view.getMembers(); mbrs.clear(); mbrs.addAll(tmp); coord=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; case Event.SET_LOCAL_ADDRESS: local_addr=(Address)evt.getArg(); break; } return down_prot.down(evt); } void startCoordAnnouncerTask() { if(announcer_task_future == null || announcer_task_future.isDone()) { announcer_task_future=timer.scheduleWithDynamicInterval(new CoordinatorAnnouncer()); } } void stopCoordAnnouncerTask() { if(announcer_task_future != null) { announcer_task_future.cancel(false); announcer_task_future=null; } } /** * Returns a random value within [min_interval - max_interval] */ long computeInterval() { return min_interval + Util.random(max_interval - min_interval); } void sendView() { Message view_announcement=new Message(); // multicast to all CoordAnnouncement hdr=new CoordAnnouncement(view); view_announcement.putHeader(this.id, hdr); down_prot.down(new Event(Event.MSG, view_announcement)); } class CoordinatorAnnouncer implements TimeScheduler.Task { public long nextInterval() { return computeInterval(); } public void run() { if(is_coord) sendView(); } } public static class CoordAnnouncement extends Header { private View view; public CoordAnnouncement() { } public CoordAnnouncement(View view) { this.view=view; } public void writeTo(DataOutputStream out) throws IOException { Util.writeView(view, out); } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { view=Util.readView(in); } public int size() { return Util.size(view); } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/MERGEFAST.java0000644000175000017500000000637011647260573026040 0ustar moellermoellerpackage org.jgroups.protocols; import org.jgroups.*; import org.jgroups.annotations.Experimental; import org.jgroups.stack.Protocol; import org.jgroups.util.Util; import java.io.*; import java.util.HashMap; import java.util.Map; import java.util.Vector; /** * The coordinator attaches a small header with its view 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 */ @Experimental public class MERGEFAST extends Protocol { Address local_addr=null; View view; boolean is_coord=false; public Object down(Event evt) { switch(evt.getType()) { case Event.MSG: if(is_coord && view != null) { Message msg=(Message)evt.getArg(); Address dest=msg.getDest(); if(dest == null || dest.isMulticastAddress()) { msg.putHeader(this.id, new MergefastHeader(view)); } } 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) { switch(evt.getType()) { 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(this.id); up_prot.up(evt); if(hdr != null && view != null) { if(!Util.sameViewId(view.getViewId(), hdr.view.getViewId())) { Map views=new HashMap(); views.put(local_addr, view); views.put(msg.getSrc(), hdr.view); if(log.isDebugEnabled()) log.debug("detected different views (" + Util.printViews(views.values()) + "), sending up MERGE event"); up_prot.up(new Event(Event.MERGE, views)); } } return null; // event was already passed up } return up_prot.up(evt); } protected void handleViewChange(View v) { Vector
        mbrs=v.getMembers(); view=v; is_coord=mbrs != null && !mbrs.isEmpty() && local_addr.equals(mbrs.firstElement()); } public static class MergefastHeader extends Header { private View view=null; public MergefastHeader() { } public MergefastHeader(View view) { this.view=view; } public void writeTo(DataOutputStream out) throws IOException { Util.writeView(view, out); } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { view=Util.readView(in); } public int size() { return Util.size(view); } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/MFC.java0000644000175000017500000001260111647260573025122 0ustar moellermoellerpackage org.jgroups.protocols; import org.jgroups.Address; import org.jgroups.Event; import org.jgroups.Message; import org.jgroups.annotations.MBean; import org.jgroups.annotations.ManagedAttribute; import org.jgroups.annotations.ManagedOperation; import org.jgroups.util.CreditMap; import org.jgroups.util.Tuple; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.Vector; /** * 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 the actual number of bytes received *
            * @author Bela Ban */ @MBean(description="Simple flow control protocol based on a credit system") public class MFC extends FlowControl { /* --------------------------------------------- Fields ------------------------------------------------------ */ /** Maintains credits per member */ protected CreditMap credits; /** Last time a credit request was sent. Used to prevent credit request storms */ protected long last_credit_request=0; /** Allows to unblock a blocked sender from an external program, e.g. JMX */ @ManagedOperation(description="Unblock a sender") public void unblock() { if(log.isTraceEnabled()) log.trace("unblocking the sender and replenishing all members"); credits.replenishAll(); } @ManagedOperation(description="Print credits") public String printCredits() { return super.printCredits() + "\nsenders min credits: " + credits.computeLowestCreditWithAccumulated(); } @ManagedOperation(description="Print sender credits") public String printSenderCredits() { return credits.toString(); } @ManagedAttribute(description="Number of times flow control blocks sender") public int getNumberOfBlockings() { return credits.getNumBlockings(); } @ManagedAttribute(description="Total time (ms) spent in flow control block") public long getTotalTimeBlocked() { return credits.getTotalBlockTime(); } protected boolean handleMulticastMessage() { return true; } public void init() throws Exception { super.init(); credits=new CreditMap(max_credits); } public void stop() { super.stop(); credits.clear(); } protected Object handleDownMessage(final Event evt, final Message msg, Address dest, int length) { if(dest != null && !dest.isMulticastAddress()) { // 2nd line of defense, not really needed log.error(getClass().getSimpleName() + " doesn't handle unicast messages; passing message down"); return down_prot.down(evt); } long block_time=max_block_times != null? getMaxBlockTime(length) : max_block_time; while(running) { boolean rc=credits.decrement(length, block_time); if(rc || max_block_times != null || !running) break; if(needToSendCreditRequest()) { List> targets=credits.getMembersWithCreditsLessThan(min_credits); for(Tuple tuple: targets) sendCreditRequest(tuple.getVal1(), Math.min(max_credits, max_credits - tuple.getVal2())); } } // send message - either after regular processing, or after blocking (when enough credits are available again) return down_prot.down(evt); } protected synchronized boolean needToSendCreditRequest() { long curr_time=System.currentTimeMillis(); long wait_time=curr_time - last_credit_request; if(wait_time >= max_block_time) { last_credit_request=curr_time; return true; } return false; } protected void handleCredit(Address sender, long increase) { credits.replenish(sender, increase); if(log.isTraceEnabled()) { StringBuilder sb=new StringBuilder(); sb.append("received " + increase + " credits from ").append(sender).append(", new credits for " + sender + " : ") .append(credits.get(sender) + ", min_credits=" + credits.getMinCredits()); log.trace(sb); } } protected void handleViewChange(Vector
            mbrs) { super.handleViewChange(mbrs); Set
            keys=new HashSet
            (credits.keys()); for(Address key: keys) { if(!mbrs.contains(key)) credits.remove(key); } for(Address key: mbrs) credits.putIfAbsent(key); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/MPING.java0000644000175000017500000002770111647260573025376 0ustar moellermoellerpackage org.jgroups.protocols; import org.jgroups.Event; import org.jgroups.Global; import org.jgroups.Message; import org.jgroups.annotations.LocalAddress; import org.jgroups.annotations.Property; import org.jgroups.annotations.DeprecatedProperty; import org.jgroups.conf.PropertyConverters; import org.jgroups.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 */ @DeprecatedProperty(names="bind_to_all_interfaces") public class MPING extends PING implements Runnable { 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(); } /* ----------------------------------------- Properties -------------------------------------------------- */ @LocalAddress @Property(description="Bind address for multicast socket. " + "The following special values are also recognized: GLOBAL, SITE_LOCAL, LINK_LOCAL and NON_LOOPBACK", systemProperty={Global.BIND_ADDR, Global.BIND_ADDR_OLD}, defaultValueIPv4=Global.NON_LOOPBACK_ADDRESS, defaultValueIPv6=Global.NON_LOOPBACK_ADDRESS) InetAddress bind_addr=null; @Property(name="bind_interface", converter=PropertyConverters.BindInterface.class, description="The interface (NIC) which should be used by this transport",dependsUpon="bind_addr") protected String bind_interface_str=null; @Property(description="Time to live for discovery packets. Default is 8", systemProperty=Global.MPING_IP_TTL) int ip_ttl=8; @Property(name="mcast_addr", systemProperty=Global.MPING_MCAST_ADDR, defaultValueIPv4="230.5.6.7", defaultValueIPv6="ff0e::5:6:7") InetAddress mcast_addr=null; @Property(description="Multicast port for discovery packets. Default is 7555", systemProperty=Global.MPING_MCAST_PORT) int mcast_port=7555; @Property(description="If true, the transport should use all available interfaces to receive multicast messages. Default is false") 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. */ @Property(converter=PropertyConverters.NetworkInterfaceList.class, description="List of interfaces to receive multicasts on") 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 */ @Property(description="Whether send messages are sent on all interfaces. Default is false") 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. */ @Property(converter=PropertyConverters.NetworkInterfaceList.class, description="List of interfaces to send multicasts on") List send_interfaces=null; /* --------------------------------------------- Fields ------------------------------------------------------ */ private MulticastSocket mcast_sock=null; /** * If we have multiple mcast send sockets, e.g. send_interfaces or * send_on_all_interfaces enabled */ private MulticastSocket[] mcast_send_sockets=null; private volatile Thread receiver=null; public 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 void setMulticastAddress(String addr) throws UnknownHostException { mcast_addr=InetAddress.getByName(addr); } public int getMcastPort() { return mcast_port; } public void setMcastPort(int mcast_port) { this.mcast_port=mcast_port; } @SuppressWarnings("unchecked") 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 init() throws Exception { super.init(); if(log.isDebugEnabled()) log.debug("bind_addr=" + bind_addr + " mcast_addr=" + mcast_addr + ", mcast_port=" + mcast_port); } 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(getSocketFactory(), Global.MPING_MCAST_SOCK, mcast_addr, mcast_port, log); else mcast_sock=getSocketFactory().createMulticastSocket(Global.MPING_MCAST_SOCK, 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); ExposedByteArrayOutputStream out_stream=new ExposedByteArrayOutputStream(128); 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() { final byte[] receive_buf=new byte[65535]; DatagramPacket packet=new DatagramPacket(receive_buf, receive_buf.length); byte[] data; ByteArrayInputStream inp_stream; 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 ExposedByteArrayInputStream(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 { Util.close(inp); } } if(log.isTraceEnabled()) log.trace("receiver thread terminated"); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/PEER_LOCK.java0000644000175000017500000000720011647260573026057 0ustar moellermoellerpackage org.jgroups.protocols; /** * @author Bela Ban */ import java.util.ArrayList; import java.util.List; import java.util.Map; import org.jgroups.Address; import org.jgroups.View; import org.jgroups.annotations.Experimental; import org.jgroups.blocks.locking.Owner; /** * Implementation of a locking protocol which acquires locks by contacting all of the nodes of a cluster.

            * Unless a total order configuration is used (e.g. {@link org.jgroups.protocols.SEQUENCER} based), lock requests for * the same resource from different senders may be received in different order, so deadlocks can occur. Example: *
             * - Nodes A and B
             * - A and B call lock(X) at the same time
             * - A receives L(X,A) followed by L(X,B): locks X(A), queues L(X,B)
             * - B receives L(X,B) followed by L(X,A): locks X(B), queues L(X,A)
             * 
            * To acquire a lock, we need lock grants from both A and B, but this will never happen here. To fix this, either * add SEQUENCER to the configuration, so that all lock requests are received in the same global order at both A and B, * or use {@link java.util.concurrent.locks.Lock#tryLock(long,java.util.concurrent.TimeUnit)} with retries if a lock * cannot be acquired.

            * An alternative is also the {@link org.jgroups.protocols.CENTRAL_LOCK} protocol. * @author Bela Ban * @since 2.12 * @see Locking * @see CENTRAL_LOCK */ @Experimental public class PEER_LOCK extends Locking { public PEER_LOCK() { super(); } protected void sendGrantLockRequest(String lock_name, Owner owner, long timeout, boolean is_trylock) { sendRequest(null, Type.GRANT_LOCK, lock_name, owner, timeout, is_trylock); } protected void sendReleaseLockRequest(String lock_name, Owner owner) { sendRequest(null, Type.RELEASE_LOCK, lock_name, owner, 0, false); } @Override protected void sendAwaitConditionRequest(String lock_name, Owner owner) { sendRequest(null, Type.LOCK_AWAIT, lock_name, owner, 0, false); } @Override protected void sendSignalConditionRequest(String lock_name, boolean all) { sendRequest(null, all ? Type.COND_SIG_ALL : Type.COND_SIG, lock_name, null, 0, false); } @Override protected void sendDeleteAwaitConditionRequest(String lock_name, Owner owner) { sendRequest(null, Type.DELETE_LOCK_AWAIT, lock_name, owner, 0, false); } public void handleView(View view) { super.handleView(view); List

            members=view.getMembers(); synchronized(client_locks) { for(Map map: client_locks.values()) { for(ClientLock lock: map.values()) ((PeerLock)lock).retainAll(members); } } } protected ClientLock createLock(String lock_name) { return new PeerLock(lock_name); } /** * Lock implementation which grants a lock when all non faulty cluster members OK it. */ protected class PeerLock extends ClientLock { protected final List
            grants=new ArrayList
            (view.getMembers()); public PeerLock(String name) { super(name); } protected synchronized void retainAll(List
            members) { if(grants.isEmpty()) return; grants.retainAll(members); if(grants.isEmpty()) lockGranted(); } protected synchronized void handleLockGrantedResponse(Owner owner, Address sender) { if(grants.isEmpty()) return; grants.remove(sender); if(grants.isEmpty()) lockGranted(); } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/PERF_TP.java0000644000175000017500000000737211647260573025665 0ustar moellermoeller package org.jgroups.protocols; import org.jgroups.Address; import org.jgroups.Event; import org.jgroups.Message; import org.jgroups.annotations.Unsupported; import org.jgroups.stack.Protocol; /** * Measures the time for a message to travel from the channel to the transport * @author Bela Ban */ @Unsupported 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 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 -------------------------- */ } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/PING.java0000644000175000017500000000700611647260573025255 0ustar moellermoellerpackage org.jgroups.protocols; import org.jgroups.*; import org.jgroups.annotations.DeprecatedProperty; import org.jgroups.annotations.Property; import org.jgroups.util.Promise; import org.jgroups.util.UUID; import java.util.Arrays; import java.util.List; /** * 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. * 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. * @author Bela Ban */ @DeprecatedProperty(names={"gossip_host", "gossip_port", "gossip_refresh", "socket_conn_timeout", "socket_read_timeout", "discovery_timeout"}) public class PING extends Discovery { /* ----------------------------------------- Properties -------------------------------------------------- */ @Property(description="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; /* --------------------------------------------- Fields ------------------------------------------------------ */ protected final Promise discovery_reception=new Promise(); public void stop() { super.stop(); discovery_reception.reset(); } public boolean isDynamic() { return true; } public void sendGetMembersRequest(String cluster_name, Promise promise, ViewId view_id) throws Exception { PingData data=null; if(view_id == null) { PhysicalAddress physical_addr=(PhysicalAddress)down(new Event(Event.GET_PHYSICAL_ADDRESS, local_addr)); List physical_addrs=Arrays.asList(physical_addr); data=new PingData(local_addr, null, false, UUID.get(local_addr), physical_addrs); } PingHeader hdr=new PingHeader(PingHeader.GET_MBRS_REQ, data, cluster_name); hdr.view_id=view_id; Message msg=new Message(null); // mcast msg msg.setFlag(Message.OOB); msg.putHeader(this.id, 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(this.id); 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(); } 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"); } } } }libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/PRIO.java0000644000175000017500000002344411647260573025275 0ustar moellermoellerpackage org.jgroups.protocols; import java.util.Comparator; import java.util.concurrent.PriorityBlockingQueue; import org.jgroups.Event; import org.jgroups.Message; import org.jgroups.annotations.Experimental; import org.jgroups.annotations.Property; import org.jgroups.stack.Protocol; /** * This protocol will provide message sending and receiving prioritization. The protocol assumes that any prioritized * message will contain a PrioHeader header entry that will contain the byte value priority. Priority values are from * 0 to 255 where 0 is the highest priority. * * When a message is received (up/down), it is added to the up/downMessageQueue. The up/downMessageThread will block * on the queue until new message is added. Messages with the highest priority (0=highest) will bubble to the top * of the queue and those be processed before other messages received at the same time. * * Example of setting a message priority: * * // Create a message to send to everyone * Message message = new Message( null, null, messagePayload ); * // Add the priority protocol header * PrioHeader header = new PrioHeader( 1 ); * short protocolId = ClassConfigurator.getProtocolId(PRIO.class); * message.putHeader( protocolId, header); * * @author Michael Earl */ @Experimental public class PRIO extends Protocol { private PriorityBlockingQueue downMessageQueue; private PriorityBlockingQueue upMessageQueue; private DownMessageThread downMessageThread; private UpMessageThread upMessageThread; @Property(description="The number of miliseconds to sleep before after an error occurs before sending the next message") private int message_failure_sleep_time = 120000; // two seconds (bela: 2 minutes, is that what you wanted ?) @Property(description="true to prioritize outgoing messages") private boolean prioritize_down = true; @Property(description="true to prioritize incoming messages") private boolean prioritize_up = true; /** * 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 { if (prioritize_down) { downMessageQueue = new PriorityBlockingQueue( 100, new PriorityCompare() ); downMessageThread = new DownMessageThread( this, downMessageQueue ); downMessageThread.start(); } if (prioritize_up) { upMessageQueue = new PriorityBlockingQueue( 100, new PriorityCompare() ); upMessageThread = new UpMessageThread( this, upMessageQueue ); upMessageThread.start(); } } /** * 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() { if (downMessageThread != null) { downMessageThread.interrupt(); } if (upMessageThread != null) { upMessageThread.interrupt(); } } /** * 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) { switch(evt.getType()) { case Event.MSG: Message message = (Message)evt.getArg(); if ( message.isFlagSet( Message.OOB ) ) { return up_prot.up(evt); } else { PrioHeader hdr=(PrioHeader)message.getHeader(id); if(hdr != null) { log.trace( "Adding priority message " + hdr.getPriority() + " to UP queue" ); upMessageQueue.add( new PriorityMessage( evt, hdr.getPriority() ) ); // send with hdr.prio return null; } return up_prot.up(evt); } default: 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) { switch(evt.getType()) { case Event.MSG: Message message = (Message)evt.getArg(); if ( message.isFlagSet( Message.OOB ) ) { return down_prot.down(evt); } else { PrioHeader hdr=(PrioHeader)message.getHeader(id); if(hdr != null) { log.trace( "Adding priority message " + hdr.getPriority() + " to DOWN queue" ); downMessageQueue.add( new PriorityMessage( evt, hdr.getPriority() ) ); // send with hdr.prio return null; } return down_prot.down(evt); } default: return down_prot.down(evt); } } /** * This class is a simple wrapper to contain the Event, timestamp and priority of the message. * Instances of this class are added to the message queue */ private class PriorityMessage { Event event; long timestamp; byte priority; private PriorityMessage( Event event, byte priority ) { this.event = event; this.timestamp = System.currentTimeMillis(); this.priority = priority; } } /** * Thread to send messages to the down protocol. *

            * The messageQueue contains the prioritized messages */ private class DownMessageThread extends MessageThread { private DownMessageThread( PRIO prio, PriorityBlockingQueue messageQueue ) { super( prio, messageQueue ); } protected void handleMessage( PriorityMessage message ) { log.trace( "Sending priority " + message.priority + " message" ); down_prot.down( message.event ); } } /** * Thread to send messages to the up protocol. *

            * The messageQueue contains the prioritized messages */ private class UpMessageThread extends MessageThread { private UpMessageThread( PRIO prio, PriorityBlockingQueue messageQueue ) { super( prio, messageQueue ); } protected void handleMessage( PriorityMessage message ) { log.trace( "receiving priority " + message.priority + " message" ); up_prot.up( message.event ); } } /** * This Thread class will process PriorityMessage's off of the queue and call the handleMessage method * to send the message */ private abstract class MessageThread extends Thread { private PRIO prio; private PriorityBlockingQueue messageQueue; private MessageThread( PRIO prio, PriorityBlockingQueue messageQueue ) { this.prio = prio; this.messageQueue = messageQueue; setName( "PRIO " + (messageQueue == downMessageQueue ? "down" : "up") ); } protected abstract void handleMessage( PriorityMessage message ); @Override public void run() { while (true) { PriorityMessage priorityMessage = null; try { priorityMessage = messageQueue.take(); handleMessage( priorityMessage ); } catch( InterruptedException e ) { break; } catch (Exception e) { log.error( "Error handling message. Sleeping " + (prio.message_failure_sleep_time/1000) + " seconds", e ); try { sleep( prio.message_failure_sleep_time ); } catch (InterruptedException ex) { break; } /* * Add it back to the queue to be processed again */ messageQueue.add( priorityMessage ); } } } } /** * Comparator for PriorityMessage's */ private class PriorityCompare implements Comparator { /** * Compare two messages based on priority and time stamp in that order * @param msg1 - first message * @param msg2 - second message * @return int result of comparison */ @Override public int compare( PriorityMessage msg1, PriorityMessage msg2 ) { if ( msg1.priority > msg2.priority ) { return 1; } else if ( msg1.priority < msg2.priority ) { return -1; } else { if ( msg1.timestamp > msg2.timestamp ) { return 1; } else if ( msg1.timestamp < msg2.timestamp ) { return -1; } else { return 0; } } } } }libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/PingData.java0000644000175000017500000001230611647260573026206 0ustar moellermoeller package org.jgroups.protocols; import org.jgroups.*; import org.jgroups.util.Streamable; import org.jgroups.util.Util; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; /** * Encapsulates information about a cluster node, e.g. local address, coordinator's address, logical name and * physical address(es) * @author Bela Ban */ public class PingData implements Streamable { protected Address own_addr=null; protected View view=null; // only sent with merge-triggered discovery response (if ViewIds differ) protected ViewId view_id=null; // only sent with GMS-triggered discovery response protected boolean is_server=false; protected String logical_name=null; protected Collection physical_addrs=null; public PingData() { } public PingData(Address own_addr, View view, boolean is_server) { this.own_addr=own_addr; this.view=view; this.is_server=is_server; } public PingData(Address own_addr, View view, boolean is_server, String logical_name, Collection physical_addrs) { this(own_addr, view, is_server); this.logical_name=logical_name; if(physical_addrs != null) this.physical_addrs=new ArrayList(physical_addrs); } public PingData(Address own_addr, View view, ViewId view_id, boolean is_server, String logical_name, Collection physical_addrs) { this(own_addr, view, is_server, logical_name, physical_addrs); this.view_id=view_id; } public boolean isCoord() { Address coord_addr=getCoordAddress(); return is_server && own_addr != null && coord_addr != null && own_addr.equals(coord_addr); } public boolean hasCoord(){ Address coord_addr=getCoordAddress(); return is_server && own_addr != null && coord_addr != null; } public Address getAddress() { return own_addr; } public Address getCoordAddress() { if(view_id != null) return view_id.getCoordAddress(); return view != null? view.getVid().getCoordAddress() : null; } public Collection

            getMembers() { return view != null? view.getMembers() : null; } public View getView() { return view; } public void setView(View view) { this.view=view; } public ViewId getViewId() {return view_id;} public void setViewId(ViewId view_id) {this.view_id=view_id;} public boolean isServer() { return is_server; } public String getLogicalName() { return logical_name; } public Collection getPhysicalAddrs() { return physical_addrs; } public boolean equals(Object obj) { if(!(obj instanceof PingData)) return false; PingData other=(PingData)obj; return 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 String toString() { StringBuilder sb=new StringBuilder(); sb.append("own_addr=").append(own_addr); if(view_id != null) sb.append(", view_id=").append(view_id); if(view != null) { sb.append(", view="); if(view.size() > 10) sb.append(view.size() + " mbrs"); else sb.append(view); } sb.append(", is_server=").append(is_server).append(", is_coord=" + isCoord()); if(logical_name != null) sb.append(", logical_name=").append(logical_name); if(physical_addrs != null && !physical_addrs.isEmpty()) sb.append(", physical_addrs=").append(Util.printListWithDelimiter(physical_addrs, ", ")); return sb.toString(); } public void writeTo(DataOutputStream outstream) throws IOException { Util.writeAddress(own_addr, outstream); Util.writeView(view, outstream); Util.writeViewId(view_id, outstream); outstream.writeBoolean(is_server); Util.writeString(logical_name, outstream); Util.writeAddresses(physical_addrs, outstream); } public void readFrom(DataInputStream instream) throws IOException, IllegalAccessException, InstantiationException { own_addr=Util.readAddress(instream); view=Util.readView(instream); view_id=Util.readViewId(instream); is_server=instream.readBoolean(); logical_name=Util.readString(instream); physical_addrs=(Collection)Util.readAddresses(instream, ArrayList.class); } public int size() { int retval=Global.BYTE_SIZE; // for is_server retval+=Util.size(own_addr); retval+=Util.size(view); retval+=Util.size(view_id); retval+=Global.BYTE_SIZE; // presence byte for logical_name if(logical_name != null) retval+=logical_name.length() +2; retval+=Util.size(physical_addrs); return retval; } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/PingHeader.java0000644000175000017500000000513211647260573026524 0ustar moellermoeller package org.jgroups.protocols; import org.jgroups.Global; import org.jgroups.Header; import org.jgroups.ViewId; import org.jgroups.util.Util; import java.io.*; /** * @author Bela Ban */ public class PingHeader extends Header { public static final byte GET_MBRS_REQ=1; // arg = null public static final byte GET_MBRS_RSP=2; // arg = PingData (local_addr, coord_addr) public byte type=0; public PingData data=null; public String cluster_name=null; // when set, we don't need the address mappings, but only the view. // This is typically done for a merge-triggered discovery request public ViewId view_id=null; public PingHeader() { } public PingHeader(byte type, String cluster_name) { this.type=type; this.cluster_name=cluster_name; } public PingHeader(byte type, PingData data) { this.type=type; this.data=data; } public PingHeader(byte type, PingData data, String cluster_name) { this(type, data); this.cluster_name=cluster_name; } public int size() { int retval=Global.BYTE_SIZE *3; // type, data presence and cluster_name presence if(data != null) retval+=data.size(); if(cluster_name != null) retval += cluster_name.length() +2; retval+=Util.size(view_id); return retval; } public String toString() { StringBuilder sb=new StringBuilder(); sb.append("[PING: type=" + type2Str(type)); if(cluster_name != null) sb.append(", cluster=").append(cluster_name); if(data != null) sb.append(", arg=" + data); if(view_id != null) sb.append(", view_id=").append(view_id); sb.append(']'); return sb.toString(); } static 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 writeTo(DataOutputStream outstream) throws IOException { outstream.writeByte(type); Util.writeStreamable(data, outstream); Util.writeString(cluster_name, outstream); Util.writeViewId(view_id, outstream); } public void readFrom(DataInputStream instream) throws IOException, IllegalAccessException, InstantiationException { type=instream.readByte(); data=(PingData)Util.readStreamable(PingData.class, instream); cluster_name=Util.readString(instream); view_id=Util.readViewId(instream); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/PrioHeader.java0000644000175000017500000000266511647260573026550 0ustar moellermoellerpackage org.jgroups.protocols; import org.jgroups.Global; import org.jgroups.Header; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; /** * This Header class is used in conjunction with the PRIO protocol to prioritize message sending/receiving * Priority values are from 0 to 255 where 0 is the highest priority * * Example of setting a message priority: * * // Create a message to send to everyone * Message message = new Message( null, null, messagePayload ); * // Add the priority protocol header * PrioHeader header = new PrioHeader( 1 ); * short protocolId = ClassConfigurator.getProtocolId(PRIO.class); * message.putHeader( protocolId, header); * * @author Michael Earl */ public class PrioHeader extends Header { private byte priority = 0; public PrioHeader() { } public PrioHeader( byte priority ) { this.priority = priority; } public byte getPriority() { return priority; } public void setPriority( byte priority ) { this.priority = priority; } @Override public int size() { return Global.BYTE_SIZE; } public void writeTo( DataOutputStream outstream ) throws IOException { outstream.writeByte(priority); } public void readFrom( DataInputStream instream ) throws IOException, IllegalAccessException, InstantiationException { priority=instream.readByte(); } @Override public String toString() { return "PRIO priority=" + priority; } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/RATE_LIMITER.java0000644000175000017500000000707011647260573026441 0ustar moellermoellerpackage org.jgroups.protocols; import org.jgroups.Event; import org.jgroups.Message; import org.jgroups.annotations.*; import org.jgroups.stack.Protocol; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * Protocol which sends at most max_bytes in time_period milliseconds. Can be used instead of a flow control protocol, * e.g. FC or SFC (same position in the stack) * @author Bela Ban */ @Experimental @MBean(description="Limits the sending rate to max_bytes per time_period") public class RATE_LIMITER extends Protocol { @Property(description="Max number of bytes to be sent in time_period ms. Blocks the sender if exceeded until a new " + "time period has started") protected long max_bytes=500000; @Property(description="Number of milliseconds during which max_bytes bytes can be sent") protected long time_period=1000L; /** Keeps track of the number of bytes sent in the current time period */ @GuardedBy("lock") @ManagedAttribute protected long num_bytes_sent=0L; @GuardedBy("lock") protected long end_of_current_period=0L; protected final Lock lock=new ReentrantLock(); protected final Condition block=lock.newCondition(); @ManagedAttribute protected int num_blockings=0; @ManagedAttribute protected long total_block_time=0L; public Object down(Event evt) { if(evt.getType() == Event.MSG) { Message msg=(Message)evt.getArg(); int len=msg.getLength(); lock.lock(); try { if(len > max_bytes) { log.error("message length (" + len + " bytes) exceeded max_bytes (" + max_bytes + "); " + "adjusting max_bytes to " + len); max_bytes=len; } while(true) { boolean size_exceeded=num_bytes_sent + len >= max_bytes, time_exceeded=System.currentTimeMillis() > end_of_current_period; if(!size_exceeded && !time_exceeded) break; if(time_exceeded) { reset(); } else { // size exceeded long block_time=end_of_current_period - System.currentTimeMillis(); if(block_time > 0) { try { block.await(block_time, TimeUnit.MILLISECONDS); num_blockings++; total_block_time+=block_time; } catch(InterruptedException e) { } } } } } finally { num_bytes_sent+=len; lock.unlock(); } return down_prot.down(evt); } return down_prot.down(evt); } public void init() throws Exception { super.init(); if(time_period <= 0) throw new IllegalArgumentException("time_period needs to be positive"); } public void stop() { super.stop(); lock.lock(); try { reset(); } finally { lock.unlock(); } } protected void reset() { num_bytes_sent=0L; end_of_current_period=System.currentTimeMillis() + time_period; block.signalAll(); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/RELAY.java0000644000175000017500000006527011647260573025403 0ustar moellermoellerpackage org.jgroups.protocols; import org.jgroups.*; import org.jgroups.annotations.*; import org.jgroups.stack.AddressGenerator; import org.jgroups.stack.IpAddress; import org.jgroups.stack.Protocol; import org.jgroups.util.*; import org.jgroups.util.UUID; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.util.*; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; /** * Simple relaying protocol: RELAY is added to the top of the stack, creates a channel to a bridge cluster, * and - if coordinator - relays all multicast messages via the bridge cluster to the remote cluster.

            * * This is not a big virtual cluster, e.g. consisting of {A,B,C,X,Y,Z}, but 2 autonomous clusters * {A,B,C} and {X,Y,Z}, bridged together by RELAY. For example, when B multicasts a message M, A (if it happens to be * the coord) relays M to X (which happens to be the other cluster's coordinator). X then re-broadcasts M, with M.src * being a ProxyUUID(X,B). This means that the sender of M in the {X,Y,Z} cluster will be X for all practical purposes, * but the original sender B is also recorded, for sending back a response.

            * * See [1] and [2] for details.

            * [1] https://jira.jboss.org/browse/JGRP-747

            * [2] doc/design/RELAY.txt * * @author Bela Ban * @since 2.12 */ @Experimental @MBean(description="RELAY protocol") public class RELAY extends Protocol { /* ------------------------------------------ Properties ---------------------------------------------- */ @Property(description="Description of the local cluster, e.g. \"nyc\". This is added to every address, so it" + "should be short. This is a mandatory property and must be set",writable=false) protected String site; @Property(description="Properties of the bridge cluster (e.g. tcp.xml)") protected String bridge_props=null; @Property(description="Name of the bridge cluster") protected String bridge_name="bridge-cluster"; // @Property(description="If true, messages are relayed asynchronously, ie. via submission of a task to the timer thread pool") // protected boolean async=false; @Property(description="If set to false, don't perform relaying. Used e.g. for backup clusters; " + "unidirectional replication from one cluster to another, but not back. Can be changed at runtime") protected boolean relay=true; @Property(description="Drops views received from below and instead generates global views and passes them up. " + "A global view consists of the local view and the remote view, ordered by view ID. If true, no protocol" + "which requires (local) views can sit on top of RELAY") protected boolean present_global_views=true; /* --------------------------------------------- Fields ------------------------------------------------ */ protected Address local_addr; @ManagedAttribute protected volatile boolean is_coord=false; protected volatile Address coord=null; /** The bridge between the two local clusters, usually based on a TCP config */ protected JChannel bridge; /** The view of the local cluster */ protected View local_view; /** The view of the bridge cluster, usually consists of max 2 nodes */ protected View bridge_view; /** The view of the remote cluster */ protected View remote_view; /** The combined view of local and remote cluster */ protected View global_view; /** To generate new global views */ protected long global_view_id=0; protected TimeScheduler timer; protected Future remote_view_fetcher_future; @ManagedOperation public void setRelay(boolean relay) { this.relay=relay; } @ManagedAttribute public String getLocalView() { return local_view != null? local_view.toString() : "n/a"; } @ManagedAttribute public String getBridgeView() { return bridge_view != null? bridge_view.toString() : "n/a"; } @ManagedAttribute public String getRemoteView() { return remote_view != null? remote_view.toString() : "n/a"; } @ManagedAttribute public String getGlobalView() { return global_view != null? global_view.toString() : "n/a"; } public void init() throws Exception { super.init(); if(site == null || site.length() == 0) throw new IllegalArgumentException("\"site\" must be set"); timer=getTransport().getTimer(); JChannel channel=getProtocolStack().getChannel(); if(channel == null) throw new IllegalStateException("channel must be set"); channel.setAddressGenerator(new AddressGenerator() { public Address generateAddress() { return PayloadUUID.randomUUID(site); } }); } public void stop() { stopRemoteViewFetcher(); Util.close(bridge); } 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; // forward non local destinations to the coordinator, to relay to the remote cluster if(!isLocal(dest)) { forwardToCoord(msg); return null; } break; case Event.VIEW_CHANGE: handleView((View)evt.getArg()); break; case Event.DISCONNECT: Util.close(bridge); break; case Event.SET_LOCAL_ADDRESS: local_addr=(Address)evt.getArg(); break; case Event.GET_PHYSICAL_ADDRESS: // fix to prevent exception by JBossAS, which checks whether a physical // address is present and throw an ex if not // Remove this when the AS code removes that check PhysicalAddress addr=(PhysicalAddress)down_prot.down(evt); if(addr == null) addr=new IpAddress(6666); return addr; } return down_prot.down(evt); } public Object up(Event evt) { switch(evt.getType()) { case Event.MSG: Message msg=(Message)evt.getArg(); Address dest=msg.getDest(); RelayHeader hdr=(RelayHeader)msg.getHeader(getId()); if(hdr != null) { switch(hdr.type) { case DISSEMINATE: Message copy=msg.copy(); if(hdr.original_sender != null) copy.setSrc(hdr.original_sender); return up_prot.up(new Event(Event.MSG, copy)); case FORWARD: if(is_coord) forward(msg.getRawBuffer(), msg.getOffset(), msg.getLength()); break; case VIEW: return installView(msg.getRawBuffer(), msg.getOffset(), msg.getLength()); case BROADCAST_VIEW: // sendViewOnLocalCluster(msg.getSrc(), remote_view, global_view, false); break; default: throw new IllegalArgumentException(hdr.type + " is not a valid type"); } return null; } if(is_coord && relay && (dest == null || dest.isMulticastAddress()) && !msg.isFlagSet(Message.NO_RELAY)) { Message tmp=msg.copy(true, Global.BLOCKS_START_ID); // we only copy headers from building blocks try { byte[] buf=Util.streamableToByteBuffer(tmp); forward(buf, 0, buf.length); } catch(Exception e) { log.warn("failed relaying message", e); } } break; case Event.VIEW_CHANGE: handleView((View)evt.getArg()); // already sends up new view if needed if(present_global_views) return null; else break; } return up_prot.up(evt); } protected void handleView(final View view) { List

            new_mbrs=null; if(local_view != null) new_mbrs=Util.newMembers(local_view.getMembers(), view.getMembers()); local_view=view; coord=view.getMembers().firstElement(); boolean create_bridge=false; boolean is_new_coord=Util.isCoordinator(view, local_addr); if(is_coord) { if(!is_new_coord) { if(log.isTraceEnabled()) log.trace("I'm not coordinator anymore, closing the channel"); Util.close(bridge); is_coord=false; bridge=null; } } else if(is_new_coord) is_coord=create_bridge=true; if(is_coord) { // need to have a local view before JChannel.connect() returns; we don't want to change the viewAccepted() semantics sendViewOnLocalCluster(remote_view, generateGlobalView(view, remote_view, view instanceof MergeView), true, new_mbrs); if(create_bridge) createBridge(); sendViewToRemote(ViewData.create(view, null), false); } } protected Object installView(byte[] buf, int offset, int length) { try { ViewData data=(ViewData)Util.streamableFromByteBuffer(ViewData.class, buf, offset, length); if(data.uuids != null) UUID.add(data.uuids); remote_view=data.remote_view; if(global_view == null || (data.global_view != null &&!global_view.equals(data.global_view))) { global_view=data.global_view; synchronized(this) { if(data.global_view.getVid().getId() > global_view_id) global_view_id=data.global_view.getViewId().getId(); } if(present_global_views) return up_prot.up(new Event(Event.VIEW_CHANGE, global_view)); } } catch(Exception e) { log.error("failed installing view", e); } return null; } /** Forwards the message across the TCP link to the other local cluster */ protected void forward(byte[] buffer, int offset, int length) { Message msg=new Message(null, null, buffer, offset, length); msg.putHeader(id, new RelayHeader(RelayHeader.Type.FORWARD)); if(bridge != null) { try { bridge.send(msg); } catch(Throwable t) { log.error("failed forwarding message over bridge", t); } } } /** Wraps the message annd sends it to the current coordinator */ protected void forwardToCoord(Message msg) { Message tmp=msg.copy(true, Global.BLOCKS_START_ID); // // we only copy headers from building blocks if(tmp.getSrc() == null) tmp.setSrc(local_addr); try { byte[] buf=Util.streamableToByteBuffer(tmp); if(coord != null) { // optimization: if I'm the coord, simply relay to the remote cluster via the bridge if(coord.equals(local_addr)) { forward(buf, 0, buf.length); return; } tmp=new Message(coord, null, buf, 0, buf.length); // reusing tmp is OK here ... tmp.putHeader(id, new RelayHeader(RelayHeader.Type.FORWARD)); down_prot.down(new Event(Event.MSG, tmp)); } } catch(Exception e) { log.error("failed forwarding unicast message to coord", e); } } protected void sendViewToRemote(ViewData view_data, boolean use_seperate_thread) { try { if(bridge != null && bridge.isConnected()) { byte[] buf=Util.streamableToByteBuffer(view_data); final Message msg=new Message(null, null, buf); msg.putHeader(id, RelayHeader.create(RelayHeader.Type.VIEW)); if(use_seperate_thread) { timer.execute(new Runnable() { public void run() { try { bridge.send(msg); } catch(Exception e) { log.error("failed sending view to remote", e); } } }); } else bridge.send(msg); } } catch(Exception e) { log.error("failed sending view to remote", e); } } protected View generateGlobalView(View local_view, View remote_view) { return generateGlobalView(local_view, remote_view, false); } protected View generateGlobalView(View local_view, View remote_view, boolean merge) { Vector views=new Vector(2); if(local_view != null) views.add(local_view); if(remote_view != null) views.add(remote_view); Collections.sort(views, new Comparator() { public int compare(View v1, View v2) { ViewId vid1=v1.getViewId(), vid2=v2.getViewId(); Address creator1=vid1.getCoordAddress(), creator2=vid2.getCoordAddress(); int rc=creator1.compareTo(creator2); if(rc != 0) return rc; long id1=vid1.getId(), id2=vid2.getId(); return id1 > id2 ? 1 : id1 < id2? -1 : 0; } }); Vector
            combined_members=new Vector
            (); for(View view: views) combined_members.addAll(view.getMembers()); long new_view_id; synchronized(this) { new_view_id=global_view_id++; } Address view_creator=combined_members.isEmpty()? local_addr : combined_members.firstElement(); if(merge) return new MergeView(view_creator, new_view_id, combined_members, views); else return new View(view_creator, new_view_id, combined_members); } protected void createBridge() { try { if(log.isTraceEnabled()) log.trace("I'm the coordinator, creating a channel (props=" + bridge_props + ", cluster_name=" + bridge_name + ")"); bridge=new JChannel(bridge_props); bridge.setOpt(Channel.LOCAL, false); // don't receive my own messages bridge.setReceiver(new Receiver()); bridge.connect(bridge_name); } catch(ChannelException e) { log.error("failed creating bridge channel (props=" + bridge_props + ")", e); } } protected void sendOnLocalCluster(byte[] buf, int offset, int length) { try { Message msg=(Message)Util.streamableFromByteBuffer(Message.class, buf, offset, length); Address sender=msg.getSrc(); Address dest=msg.getDest(); if(!isLocal(dest)) { if(log.isWarnEnabled()) log.warn("[" + local_addr + "] dest=" + dest + " is not local (site=" + this.site + "); discarding it"); return; } // set myself to be the sender msg.setSrc(local_addr); // later, in RELAY, we'll take the original sender from the header and make it the sender msg.putHeader(id, RelayHeader.createDisseminateHeader(sender)); if(log.isTraceEnabled()) log.trace("received msg from " + sender + ", passing down the stack with dest=" + msg.getDest() + " and src=" + msg.getSrc()); down_prot.down(new Event(Event.MSG, msg)); } catch(Exception e) { log.error("failed sending on local cluster", e); } } protected void sendViewOnLocalCluster(View remote_view, View global_view, boolean use_seperate_thread, List
            new_mbrs) { sendViewOnLocalCluster(ViewData.create(remote_view, global_view), use_seperate_thread, new_mbrs); } protected void sendViewOnLocalCluster(ViewData data, boolean use_seperate_thread, final List
            new_mbrs) { try { final byte[] buffer=Util.streamableToByteBuffer(data); final List
            destinations=new ArrayList
            (); destinations.add(null); // send to all if(new_mbrs != null) destinations.addAll(new_mbrs); if(use_seperate_thread) { timer.execute(new Runnable() { public void run() { sendViewOnLocalCluster(destinations, buffer); } }); } else sendViewOnLocalCluster(destinations, buffer); } catch(Exception e) { log.error("failed sending view to local cluster", e); } } protected void sendViewOnLocalCluster(final List
            destinations, final byte[] buffer) { for(Address dest: destinations) { Message view_msg=new Message(dest, null, buffer); RelayHeader hdr=RelayHeader.create(RelayHeader.Type.VIEW); view_msg.putHeader(id, hdr); down_prot.down(new Event(Event.MSG, view_msg)); } } /** Does the payload match the 'site' ID. Checks only unicast destinations (multicast destinations return true) */ protected boolean isLocal(Address dest) { if(dest instanceof PayloadUUID) { String tmp=((PayloadUUID)dest).getPayload(); return tmp != null && tmp.equals(this.site); } else if(dest instanceof TopologyUUID) { String tmp=((TopologyUUID)dest).getSiteId(); return tmp != null && tmp.equals(this.site); } return true; } protected synchronized void startRemoteViewFetcher() { if(remote_view_fetcher_future == null || remote_view_fetcher_future.isDone()) { remote_view_fetcher_future=timer.scheduleWithFixedDelay(new RemoteViewFetcher(), 500, 2000, TimeUnit.MILLISECONDS); } } protected synchronized void stopRemoteViewFetcher() { if(remote_view_fetcher_future != null) { remote_view_fetcher_future.cancel(false); remote_view_fetcher_future=null; } } protected class Receiver extends ReceiverAdapter { public void receive(Message msg) { Address sender=msg.getSrc(); if(bridge.getAddress().equals(sender)) // discard my own messages return; RelayHeader hdr=(RelayHeader)msg.getHeader(id); switch(hdr.type) { case DISSEMINATE: // should not occur here, but we'll ignore it anyway break; case FORWARD: sendOnLocalCluster(msg.getRawBuffer(), msg.getOffset(), msg.getLength()); break; case VIEW: try { ViewData data=(ViewData)Util.streamableFromByteBuffer(ViewData.class, msg.getRawBuffer(), msg.getOffset(), msg.getLength()); // replace addrs with proxies if(data.remote_view != null) { List
            mbrs=new LinkedList
            (); for(Address mbr: data.remote_view.getMembers()) { mbrs.add(mbr); } data.remote_view=new View(data.remote_view.getViewId(), mbrs); } boolean merge=remote_view == null; stopRemoteViewFetcher(); data.global_view=generateGlobalView(local_view, data.remote_view, merge); sendViewOnLocalCluster(data, false, null); } catch(Exception e) { log.error("failed unmarshalling view from remote cluster", e); } break; case BROADCAST_VIEW: // no-op // our local view is seen as the remote view on the other side ! sendViewToRemote(ViewData.create(local_view, null), true); break; default: throw new IllegalArgumentException(hdr.type + " is not a valid type"); } } public void viewAccepted(View view) { if(bridge_view != null && bridge_view.getViewId().equals(view.getViewId())) return; int prev_members=bridge_view != null? bridge_view.size() : 0; bridge_view=view; switch(view.size()) { case 1: // the remote cluster disappeared, remove all of its addresses from the view if(prev_members > 1 && view.getMembers().firstElement().equals(bridge.getAddress())) { remote_view=null; View new_global_view=generateGlobalView(local_view, null); sendViewOnLocalCluster(null, new_global_view, false, null); } break; case 2: startRemoteViewFetcher(); break; default: // System.err.println("View size of > 2 is invalid: view=" + view); break; } } } protected class RemoteViewFetcher implements Runnable { public void run() { if(bridge == null || !bridge.isConnected() || remote_view != null) return; Message msg=new Message(); msg.putHeader(id, RelayHeader.create(RELAY.RelayHeader.Type.BROADCAST_VIEW)); try { bridge.send(msg); } catch(Exception e) { } } } public static class RelayHeader extends Header { public static enum Type {DISSEMINATE, FORWARD, VIEW, BROADCAST_VIEW}; protected Type type; protected Address original_sender; // with DISSEMINATE public RelayHeader() { } private RelayHeader(Type type) { this.type=type; } public static RelayHeader create(Type type) { return new RelayHeader(type); } public static RelayHeader createDisseminateHeader(Address original_sender) { RelayHeader retval=new RelayHeader(Type.DISSEMINATE); retval.original_sender=original_sender; return retval; } public int size() { int retval=Global.BYTE_SIZE; // type switch(type) { case DISSEMINATE: retval+=Util.size(original_sender); break; case FORWARD: case VIEW: case BROADCAST_VIEW: break; } return retval; } public void writeTo(DataOutputStream out) throws IOException { out.writeByte(type.ordinal()); switch(type) { case DISSEMINATE: Util.writeAddress(original_sender, out); break; case FORWARD: case VIEW: case BROADCAST_VIEW: break; } } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { type=Type.values()[in.readByte()]; switch(type) { case DISSEMINATE: original_sender=Util.readAddress(in); break; case FORWARD: case VIEW: case BROADCAST_VIEW: break; } } public String toString() { StringBuilder sb=new StringBuilder(type.toString()); switch(type) { case DISSEMINATE: sb.append(" (original sender=" + original_sender + ")"); break; case FORWARD: case VIEW: case BROADCAST_VIEW: break; } return sb.toString(); } } /** Contains local and remote views, and UUID information */ protected static class ViewData implements Streamable { protected View remote_view; protected View global_view; protected Map uuids; public ViewData() { } private ViewData(View remote_view, View global_view, Map uuids) { this.remote_view=remote_view; this.global_view=global_view; this.uuids=uuids; } public static ViewData create(View remote_view, View global_view) { Map tmp=UUID.getContents(); View rv=remote_view != null? remote_view.copy() : null; View gv=global_view != null? global_view.copy() : null; return new ViewData(rv, gv, tmp); } public void writeTo(DataOutputStream out) throws IOException { Util.writeView(remote_view, out); Util.writeView(global_view, out); out.writeInt(uuids.size()); for(Map.Entry entry: uuids.entrySet()) { Util.writeAddress(entry.getKey(), out); out.writeUTF(entry.getValue()); } } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { remote_view=Util.readView(in); global_view=Util.readView(in); int size=in.readInt(); uuids=new HashMap(); for(int i=0; i < size; i++) { Address addr=Util.readAddress(in); String name=in.readUTF(); uuids.put(addr, name); } } public String toString() { StringBuilder sb=new StringBuilder(); sb.append("global_view: " + global_view).append(", remote_view: ").append(remote_view); return sb.toString(); } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/S3_PING.java0000644000175000017500000040013211647260573025617 0ustar moellermoellerpackage org.jgroups.protocols; import org.jgroups.Address; import org.jgroups.annotations.Experimental; import org.jgroups.annotations.Property; import org.jgroups.annotations.Unsupported; 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 */ @Experimental public class S3_PING extends FILE_PING { @Property(description="The access key to AWS (S3)") protected String access_key=null; @Property(description="The secret access key to AWS (S3)") protected String secret_access_key=null; @Property(description="When non-null, we set location to prefix-UUID") protected String prefix=null; @Property(description="When non-null, we use this pre-signed URL for PUTs") protected String pre_signed_put_url=null; @Property(description="When non-null, we use this pre-signed URL for DELETEs") protected String pre_signed_delete_url=null; protected AWSAuthConnection conn=null; public void init() throws Exception { super.init(); //if(access_key == null || secret_access_key == null) // throw new IllegalArgumentException("access_key and secret_access_key must be non-null"); validateProperties(); conn=new AWSAuthConnection(access_key, secret_access_key); if(prefix != null && prefix.length() > 0) { ListAllMyBucketsResponse bucket_list=conn.listAllMyBuckets(null); List buckets=bucket_list.entries; if(buckets != null) { boolean found=false; for(Object tmp: buckets) { if(tmp instanceof Bucket) { Bucket bucket=(Bucket)tmp; if(bucket.name.startsWith(prefix)) { location=bucket.name; found=true; } } } if(!found) { location=prefix + "-" + java.util.UUID.randomUUID().toString(); } } } if(usingPreSignedUrls()) { PreSignedUrlParser parsedPut = new PreSignedUrlParser(pre_signed_put_url); location = parsedPut.getBucket(); } if(!conn.checkBucketExists(location)) { conn.createBucket(location, AWSAuthConnection.LOCATION_DEFAULT, null).connection.getResponseMessage(); } Runtime.getRuntime().addShutdownHook(new Thread() { public void run() { remove(group_addr, local_addr); } }); } protected void createRootDir() { ; // do *not* create root file system (don't remove !) } protected List readAll(String clustername) { if(clustername == null) return null; List retval=new ArrayList(); try { if (usingPreSignedUrls()) { PreSignedUrlParser parsedPut = new PreSignedUrlParser(pre_signed_put_url); clustername = parsedPut.getPrefix(); } ListBucketResponse rsp=conn.listBucket(location, sanitize(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 { PingData data=(PingData)Util.objectFromByteBuffer(buf); retval.add(data); } 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(PingData data, String clustername) { if(clustername == null || data == null) return; String filename=local_addr instanceof org.jgroups.util.UUID? ((org.jgroups.util.UUID)local_addr).toStringLong() : local_addr.toString(); String key=sanitize(clustername) + "/" + sanitize(filename); try { byte[] buf=Util.objectToByteBuffer(data); S3Object val=new S3Object(buf, null); if (usingPreSignedUrls()) { Map headers = new TreeMap(); headers.put("x-amz-acl", Arrays.asList("public-read")); conn.put(pre_signed_put_url, val, headers).connection.getResponseMessage(); } else { Map headers=new TreeMap(); headers.put("Content-Type", Arrays.asList("text/plain")); conn.put(location, key, val, headers).connection.getResponseMessage(); } } catch(Exception e) { log.error("failed marshalling " + data + " to buffer", e); } } protected void remove(String clustername, Address addr) { if(clustername == null || addr == null) return; String filename=addr instanceof org.jgroups.util.UUID? ((org.jgroups.util.UUID)addr).toStringLong() : addr.toString(); String key=sanitize(clustername) + "/" + sanitize(filename); try { Map headers=new TreeMap(); headers.put("Content-Type", Arrays.asList("text/plain")); if (usingPreSignedUrls()) { conn.delete(pre_signed_delete_url).connection.getResponseMessage(); } else { conn.delete(location, key, headers).connection.getResponseMessage(); } if(log.isTraceEnabled()) log.trace("removing " + location + "/" + key); } catch(Exception e) { log.error("failure removing data", e); } } protected void validateProperties() { if (pre_signed_put_url != null && pre_signed_delete_url != null) { PreSignedUrlParser parsedPut = new PreSignedUrlParser(pre_signed_put_url); PreSignedUrlParser parsedDelete = new PreSignedUrlParser(pre_signed_delete_url); if (!parsedPut.getBucket().equals(parsedDelete.getBucket()) || !parsedPut.getPrefix().equals(parsedDelete.getPrefix())) { throw new IllegalArgumentException("pre_signed_put_url and pre_signed_delete_url must have the same path"); } } else if (pre_signed_put_url != null || pre_signed_delete_url != null) { throw new IllegalArgumentException("pre_signed_put_url and pre_signed_delete_url must both be set or both unset"); } } protected boolean usingPreSignedUrls() { return pre_signed_put_url != null; } /** Sanitizes bucket and folder names according to AWS guidelines */ protected static String sanitize(final String name) { String retval=name; retval=retval.replace('/', '-'); retval=retval.replace('\\', '-'); return retval; } /** * Use this helper method to generate pre-signed S3 urls for use with S3_PING. * You'll need to generate urls for both the put and delete http methods. * Example: * Your AWS Access Key is "abcd". * Your AWS Secret Access Key is "efgh". * You want this node to write its information to "/S3_PING/DemoCluster/node1". * So, your bucket is "S3_PING" and your key is "DemoCluster/node1". * You want this to expire one year from now, or * (System.currentTimeMillis / 1000) + (60 * 60 * 24 * 365) * Let's assume that this equals 1316286684 * * Here's how to generate the value for the pre_signed_put_url property: * String putUrl = S3_PING.generatePreSignedUrl("abcd", "efgh", "put", * "S3_Ping", "DemoCluster/node1", * 1316286684); * * Here's how to generate the value for the pre_signed_delete_url property: * String deleteUrl = S3_PING.generatePreSignedUrl("abcd", "efgh", "delete", * "S3_Ping", "DemoCluster/node1", * 1316286684); * * @param awsAccessKey Your AWS Access Key * @param awsSecretAccessKey Your AWS Secret Access Key * @param method The HTTP method - use "put" or "delete" for use with S3_PING * @param bucket The S3 bucket you want to write to * @param key The key within the bucket to write to * @param expirationDate The date this pre-signed url should expire, in seconds since epoch * @return The pre-signed url to be used in pre_signed_put_url or pre_signed_delete_url properties */ public static String generatePreSignedUrl(String awsAccessKey, String awsSecretAccessKey, String method, String bucket, String key, long expirationDate) { Map headers = new HashMap(); if (method.equalsIgnoreCase("PUT")) { headers.put("x-amz-acl", Arrays.asList("public-read")); } return Utils.generateQueryStringAuthentication(awsAccessKey, awsSecretAccessKey, method, bucket, key, new HashMap(), headers, expirationDate); } /** * Utility class to parse S3 pre-signed URLs */ static class PreSignedUrlParser { String bucket = ""; String prefix = ""; public PreSignedUrlParser(String preSignedUrl) { try { URL url = new URL(preSignedUrl); String path = url.getPath(); String[] pathParts = path.split("/"); if (pathParts.length < 3) { throw new IllegalArgumentException("pre-signed url " + preSignedUrl + " must point to a file within a bucket"); } if (pathParts.length > 4) { throw new IllegalArgumentException("pre-signed url " + preSignedUrl + " may only have only subdirectory under a bucket"); } this.bucket = pathParts[1]; if (pathParts.length > 3) { this.prefix = pathParts[2]; } } catch (MalformedURLException ex) { throw new IllegalArgumentException("pre-signed url " + preSignedUrl + " is not a valid url"); } } public String getBucket() { return bucket; } public String getPrefix() { return prefix; } } /** * 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(); if(httpCode >= 200 && httpCode < 300) return true; if(httpCode == HttpURLConnection.HTTP_NOT_FOUND) // bucket doesn't exist return false; throw new IOException("bucket '" + bucket + "' could not be accessed (rsp=" + httpCode + " (" + response.getResponseMessage() + "). Maybe the bucket is owned by somebody else or " + "the authentication failed"); } /** * 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); } public Response put(String preSignedUrl, S3Object object, Map headers) throws IOException { HttpURLConnection request = makePreSignedRequest("PUT", preSignedUrl, headers); 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)); } public Response delete(String preSignedUrl) throws IOException { return new Response(makePreSignedRequest("DELETE", preSignedUrl, null)); } /** * 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; } private HttpURLConnection makePreSignedRequest(String method, String preSignedUrl, Map headers) throws IOException { URL url = new URL(preSignedUrl); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setRequestMethod(method); addHeaders(connection, headers); 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", ""); } if(this.awsAccessKeyId != null && this.awsSecretAccessKey != null) { 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; } public static String generateQueryStringAuthentication(String awsAccessKey, String awsSecretAccessKey, String method, String bucket, String key, Map pathArgs, Map headers) { int defaultExpiresIn = 300; // 5 minutes long expirationDate = (System.currentTimeMillis() / 1000) + defaultExpiresIn; return generateQueryStringAuthentication(awsAccessKey, awsSecretAccessKey, method, bucket, key, pathArgs, headers, expirationDate); } public static String generateQueryStringAuthentication(String awsAccessKey, String awsSecretAccessKey, String method, String bucket, String key, Map pathArgs, Map headers, long expirationDate) { method = method.toUpperCase(); // Method should always be uppercase String canonicalString = makeCanonicalString(method, bucket, key, pathArgs, headers, "" + expirationDate); String encodedCanonical = encode(awsSecretAccessKey, canonicalString, true); return "http://" + DEFAULT_HOST + "/" + bucket + "/" + key + "?" + "AWSAccessKeyId=" + awsAccessKey + "&Expires=" + expirationDate + "&Signature=" + encodedCanonical; } } // // 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 } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/SCOPE.java0000644000175000017500000004501611647260573025374 0ustar moellermoellerpackage org.jgroups.protocols; import org.jgroups.*; import org.jgroups.util.*; import org.jgroups.util.ThreadFactory; import org.jgroups.annotations.*; import org.jgroups.stack.Protocol; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.util.*; import java.util.Queue; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicBoolean; /** * Implements https://jira.jboss.org/jira/browse/JGRP-822, which allows for concurrent delivery of messages from the * same sender based on scopes. Similar to using OOB messages, but messages within the same scope are ordered. * @author Bela Ban * @since 2.10 */ @Experimental @MBean(description="Implementation of scopes (concurrent delivery of messages from the same sender)") public class SCOPE extends Protocol { protected int thread_pool_min_threads=2; protected int thread_pool_max_threads=10; protected long thread_pool_keep_alive_time=30000; @Property(description="Thread naming pattern for threads in this channel. Default is cl") protected String thread_naming_pattern="cl"; @Property(description="Time in milliseconds after which an expired scope will get removed. An expired scope is one " + "to which no messages have been added in max_expiration_time milliseconds. 0 never expires scopes") protected long expiration_time=30000; @Property(description="Interval in milliseconds at which the expiry task tries to remove expired scopes") protected long expiration_interval=60000; protected Future expiry_task=null; /** * Used to find the correct AckReceiverWindow on message reception and deliver it in the right order */ protected final ConcurrentMap> queues=Util.createConcurrentMap(); protected String cluster_name; protected Address local_addr; protected Executor thread_pool; protected ThreadGroup thread_group; protected ThreadFactory thread_factory; protected TimeScheduler timer; public SCOPE() { } @ManagedAttribute(description="Number of scopes in queues") public int getNumberOfReceiverScopes() { int retval=0; for(ConcurrentMap map: queues.values()) retval+=map.keySet().size(); return retval; } @ManagedAttribute(description="Total number of messages in all queues") public int getNumberOfMessages() { int retval=0; for(ConcurrentMap map: queues.values()) { for(MessageQueue queue: map.values()) retval+=queue.size(); } return retval; } @Property(name="thread_pool.min_threads",description="Minimum thread pool size for the regular thread pool") public void setThreadPoolMinThreads(int size) { thread_pool_min_threads=size; if(thread_pool instanceof ThreadPoolExecutor) ((ThreadPoolExecutor)thread_pool).setCorePoolSize(size); } public int getThreadPoolMinThreads() {return thread_pool_min_threads;} @Property(name="thread_pool.max_threads",description="Maximum thread pool size for the regular thread pool") public void setThreadPoolMaxThreads(int size) { thread_pool_max_threads=size; if(thread_pool instanceof ThreadPoolExecutor) ((ThreadPoolExecutor)thread_pool).setMaximumPoolSize(size); } public int getThreadPoolMaxThreads() {return thread_pool_max_threads;} @Property(name="thread_pool.keep_alive_time",description="Timeout in milliseconds to remove idle thread from regular pool") public void setThreadPoolKeepAliveTime(long time) { thread_pool_keep_alive_time=time; if(thread_pool instanceof ThreadPoolExecutor) ((ThreadPoolExecutor)thread_pool).setKeepAliveTime(time, TimeUnit.MILLISECONDS); } public long getThreadPoolKeepAliveTime() {return thread_pool_keep_alive_time;} @Experimental @ManagedOperation(description="Removes all queues and scopes - only used for testing, might get removed any time !") public void removeAllQueues() { queues.clear(); } /** * Multicasts an EXPIRE message to all members, and - on reception - each member removes the scope locally * @param scope */ @ManagedOperation(description="Expires the given scope around the cluster") public void expire(short scope) { ScopeHeader hdr=ScopeHeader.createExpireHeader(scope); Message expiry_msg=new Message(); expiry_msg.putHeader(Global.SCOPE_ID, hdr); expiry_msg.setFlag(Message.SCOPED); down_prot.down(new Event(Event.MSG, expiry_msg)); } public void removeScope(Address member, short scope) { if(member == null) return; ConcurrentMap val=queues.get(member); if(val != null) { MessageQueue queue=val.remove(scope); if(queue != null) queue.clear(); } } @ManagedOperation(description="Dumps all scopes associated with members") public String dumpScopes() { StringBuilder sb=new StringBuilder(); for(Map.Entry> entry: queues.entrySet()) { sb.append(entry.getKey()).append(": ").append(new TreeSet(entry.getValue().keySet())).append("\n"); } return sb.toString(); } @ManagedAttribute(description="Number of active thread in the pool") public int getNumActiveThreads() { if(thread_pool instanceof ThreadPoolExecutor) return ((ThreadPoolExecutor)thread_pool).getActiveCount(); return 0; } public void init() throws Exception { super.init(); timer=getTransport().getTimer(); thread_group=new ThreadGroup(getTransport().getPoolThreadGroup(), "SCOPE Threads"); thread_factory=new DefaultThreadFactory(thread_group, "SCOPE", false, true); setInAllThreadFactories(cluster_name, local_addr, thread_naming_pattern); // sanity check for expiration if((expiration_interval > 0 && expiration_time <= 0) || (expiration_interval <= 0 && expiration_time > 0)) throw new IllegalArgumentException("expiration_interval (" + expiration_interval + ") and expiration_time (" + expiration_time + ") don't match"); } public void start() throws Exception { super.start(); thread_pool=createThreadPool(thread_pool_min_threads, thread_pool_max_threads, thread_pool_keep_alive_time, thread_factory); if(expiration_interval > 0 && expiration_time > 0) startExpiryTask(); } public void stop() { super.stop(); stopExpiryTask(); shutdownThreadPool(thread_pool); for(ConcurrentMap map: queues.values()) { for(MessageQueue queue: map.values()) queue.release(); // to prevent a thread killed on shutdown() from holding on to the lock } } public Object down(Event evt) { switch(evt.getType()) { case Event.SET_LOCAL_ADDRESS: local_addr=(Address)evt.getArg(); break; case Event.VIEW_CHANGE: handleView((View)evt.getArg()); break; case Event.CONNECT: case Event.CONNECT_WITH_STATE_TRANSFER: case Event.CONNECT_USE_FLUSH: case Event.CONNECT_WITH_STATE_TRANSFER_USE_FLUSH: cluster_name=(String)evt.getArg(); setInAllThreadFactories(cluster_name, local_addr, thread_naming_pattern); break; } return down_prot.down(evt); } public Object up(Event evt) { switch(evt.getType()) { case Event.MSG: Message msg=(Message)evt.getArg(); // we don't handle unscoped or OOB messages if(!msg.isFlagSet(Message.SCOPED) || msg.isFlagSet(Message.OOB)) break; ScopeHeader hdr=(ScopeHeader)msg.getHeader(id); if(hdr == null) throw new IllegalStateException("message doesn't have a ScopeHeader attached"); if(hdr.type == ScopeHeader.EXPIRE) { removeScope(msg.getSrc(), hdr.scope); return null; } MessageQueue queue=getOrCreateQueue(msg.getSrc(), hdr.scope); queue.add(msg); if(!queue.acquire()) return null; QueueThread thread=new QueueThread(queue); thread_pool.execute(thread); return null; case Event.VIEW_CHANGE: handleView((View)evt.getArg()); break; } return up_prot.up(evt); } protected MessageQueue getOrCreateQueue(Address sender, short scope) { ConcurrentMap val=queues.get(sender); if(val == null) { val=Util.createConcurrentMap(); ConcurrentMap tmp=queues.putIfAbsent(sender, val); if(tmp != null) val=tmp; } MessageQueue queue=val.get(scope); if(queue == null) { queue=new MessageQueue(); MessageQueue old=val.putIfAbsent(scope, queue); if(old != null) queue=old; } return queue; } protected synchronized void startExpiryTask() { if(expiry_task == null || expiry_task.isDone()) expiry_task=timer.scheduleWithFixedDelay(new ExpiryTask(), expiration_interval, expiration_interval, TimeUnit.MILLISECONDS); } protected synchronized void stopExpiryTask() { if(expiry_task != null) { expiry_task.cancel(true); expiry_task=null; } } protected static ExecutorService createThreadPool(int min_threads, int max_threads, long keep_alive_time, final org.jgroups.util.ThreadFactory factory) { ThreadPoolExecutor pool=new ThreadManagerThreadPoolExecutor(min_threads, max_threads, keep_alive_time, TimeUnit.MILLISECONDS, new SynchronousQueue()); pool.setThreadFactory(factory); pool.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); return pool; } protected 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 setInAllThreadFactories(String cluster_name, Address local_address, String pattern) { ThreadFactory[] factories= {thread_factory}; for(ThreadFactory factory:factories) { if(pattern != null) factory.setPattern(pattern); if(cluster_name != null) factory.setClusterName(cluster_name); if(local_address != null) factory.setAddress(local_address.toString()); } } private void handleView(View view) { Vector

            members=view.getMembers(); // Remove all non members from receiver_table Set
            keys=new HashSet
            (queues.keySet()); keys.removeAll(members); for(Address key: keys) clearQueue(key); } public void clearQueue(Address member) { ConcurrentMap val=queues.remove(member); if(val != null) { Collection values=val.values(); for(MessageQueue queue: values) queue.clear(); } if(log.isTraceEnabled()) log.trace("removed " + member + " from receiver_table"); } protected static class MessageQueue { private final Queue queue=new ConcurrentLinkedQueue(); private final AtomicBoolean processing=new AtomicBoolean(false); private long last_update=System.currentTimeMillis(); public boolean acquire() { return processing.compareAndSet(false, true); } public boolean release() { boolean result=processing.compareAndSet(true, false); if(result) last_update=System.currentTimeMillis(); return result; } public void add(Message msg) { queue.add(msg); } public Message remove() { return queue.poll(); } public void clear() { queue.clear(); } public int size() { return queue.size(); } public long getLastUpdate() { return last_update; } } protected class QueueThread implements Runnable { protected final MessageQueue queue; protected boolean first=true; public QueueThread(MessageQueue queue) { this.queue=queue; } /** Try to remove as many messages as possible from the queue and pass them up. The outer and inner loop and * the size() check at the end prevent the following scenario: *
                     * - Threads T1 and T2
                     * - T1 has the CAS
                     * - T1: remove() == null
                     * - T2: add()
                     * - T2: attempt to set the CAS: false, return
                     * - T1: set the CAS to false, return
                     * ==> Result: we have a message in the queue that nobody takes care of !
                     * 
            *

            */ public void run() { while(true) { // outer loop if(first) // we already have the queue CAS acquired first=false; else { if(!queue.acquire()) return; } try { Message msg_to_deliver; while((msg_to_deliver=queue.remove()) != null) { // inner loop try { up_prot.up(new Event(Event.MSG, msg_to_deliver)); } catch(Throwable t) { log.error("couldn't deliver message " + msg_to_deliver, t); } } } finally { queue.release(); } // although ConcurrentLinkedQueue.size() iterates through the list, this is not costly, // as at this point, the queue is almost always empty, or has only a few elements if(queue.size() == 0) // prevents a concurrent add() (which returned) to leave a dangling message in the queue break; } } } protected class ExpiryTask implements Runnable { public void run() { try { _run(); } catch(Throwable t) { log.error("failed expiring old scopes", t); } } protected void _run() { long current_time=System.currentTimeMillis(); for(Map.Entry> entry: queues.entrySet()) { ConcurrentMap map=entry.getValue(); for(Iterator> it=map.entrySet().iterator(); it.hasNext();) { Map.Entry entry2=it.next(); Short scope=entry2.getKey(); MessageQueue queue=entry2.getValue(); long diff=current_time - queue.getLastUpdate(); if(diff >= expiration_time && queue.size() == 0) { it.remove(); if(log.isTraceEnabled()) log.trace("expired scope " + entry.getKey() + "::" + scope + " (" + diff + " ms old)"); } } } } } public static class ScopeHeader extends Header { public static final byte MSG = 1; public static final byte EXPIRE = 2; byte type; short scope=0; // starts with 1 public static ScopeHeader createMessageHeader(short scope) { return new ScopeHeader(MSG, scope); } public static ScopeHeader createExpireHeader(short scope) { return new ScopeHeader(EXPIRE, scope); } public ScopeHeader() { } private ScopeHeader(byte type, short scope) { this.type=type; this.scope=scope; } public short getScope() { return scope; } public int size() { switch(type) { case MSG: case EXPIRE: return Global.BYTE_SIZE + Global.SHORT_SIZE; default: throw new IllegalStateException("type has to be MSG or EXPIRE"); } } public void writeTo(DataOutputStream out) throws IOException { out.writeByte(type); switch(type) { case MSG: case EXPIRE: out.writeShort(scope); break; default: throw new IllegalStateException("type has to be MSG or EXPIRE"); } } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { type=in.readByte(); switch(type) { case MSG: case EXPIRE: scope=in.readShort(); break; default: throw new IllegalStateException("type has to be MSG or EXPIRE"); } } public String toString() { StringBuilder sb=new StringBuilder(typeToString(type)); switch(type) { case MSG: case EXPIRE: sb.append(": scope=").append(scope); break; default: sb.append("n/a"); } return sb.toString(); } public static String typeToString(byte type) { switch(type) { case MSG: return "MSG"; case EXPIRE: return "EXPIRE"; default: return "n/a"; } } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/SEQUENCER.java0000644000175000017500000003424711647260573026061 0ustar moellermoeller package org.jgroups.protocols; import org.jgroups.*; import org.jgroups.annotations.Experimental; import org.jgroups.annotations.MBean; import org.jgroups.annotations.ManagedAttribute; import org.jgroups.annotations.ManagedOperation; import org.jgroups.stack.Protocol; import org.jgroups.util.SeqnoTable; 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 */ @Experimental @MBean(description="Implementation of total order protocol using a sequencer") public class SEQUENCER extends Protocol { private Address local_addr=null, coord=null; private final Collection

            members=new ArrayList
            (); 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; @ManagedAttribute public boolean isCoordinator() {return is_coord;} public Address getCoordinator() {return coord;} public Address getLocalAddress() {return local_addr;} @ManagedAttribute public long getForwarded() {return forwarded_msgs;} @ManagedAttribute public long getBroadcast() {return bcast_msgs;} @ManagedAttribute public long getReceivedForwards() {return received_forwards;} @ManagedAttribute public long getReceivedBroadcasts() {return received_bcasts;} @ManagedOperation public void resetStats() { forwarded_msgs=bcast_msgs=received_forwards=received_bcasts=0L; } @ManagedOperation public Map dumpStats() { Map m=super.dumpStats(); 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; } @ManagedOperation public String printStats() { return dumpStats().toString(); } public Object down(Event evt) { switch(evt.getType()) { case Event.MSG: Message msg=(Message)evt.getArg(); if(msg.isFlagSet(Message.NO_TOTAL_ORDER)) break; 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(this.id, 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(this.id, 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.MSG: msg=(Message)evt.getArg(); if(msg.isFlagSet(Message.NO_TOTAL_ORDER)) break; hdr=(SequencerHeader)msg.getHeader(this.id); 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(this.id, 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) { if(msg.getSrc() == null) 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(this.id, 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(this.id); 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(this.id, 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(this.id); 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 { private static final byte FORWARD = 1; private static final byte BCAST = 2; private static final byte WRAPPED_BCAST = 3; 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 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; } } }libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/SFC.java0000644000175000017500000005016711647260573025141 0ustar moellermoellerpackage org.jgroups.protocols; import org.jgroups.*; import org.jgroups.annotations.*; import org.jgroups.stack.Protocol; import org.jgroups.util.BoundedList; import java.io.*; 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; /** * 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 */ @Experimental @Deprecated @MBean(description="Simple flow control protocol") public class SFC extends Protocol { /* ----------------------------------------- Properties -------------------------------------------------- */ @Property(description="Max number of bytes to send per receiver until an ack must be received to proceed. Default is 2000000 bytes") private long max_credits=2000000; @Property(description="Max time (in milliseconds) to block. Default is 5000 msec") private long max_block_time=5000; 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(); /** 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;} @ManagedAttribute public long getCredits() {return curr_credits_available;} @ManagedAttribute public long getBytesSent() {return num_bytes_sent;} @ManagedAttribute public long getBlockings() {return num_blockings;} @ManagedAttribute public long getCreditRequestsSent() {return num_credit_requests_sent;} @ManagedAttribute public long getCreditRequestsReceived() {return num_credit_requests_received;} @ManagedAttribute public long getReplenishmentsReceived() {return num_replenishments_received;} @ManagedAttribute public long getReplenishmentsSent() {return num_replenishments_sent;} @ManagedAttribute public long getTotalBlockingTime() {return total_block_time;} @ManagedAttribute public double getAverageBlockingTime() {return num_blockings == 0? 0 : total_block_time / num_blockings;} @ManagedOperation public String printBlockingTimes() { return blockings.toString(); } @ManagedOperation public String printReceived() { received_lock.lock(); try { return received.toString(); } finally { received_lock.unlock(); } } @ManagedOperation public String printPendingCreditors() { lock.lock(); try { return pending_creditors.toString(); } finally { lock.unlock(); } } @ManagedOperation public String printPendingRequesters() { received_lock.lock(); try { return pending_requesters.toString(); } finally { received_lock.unlock(); } } @ManagedOperation public void unblock() { lock.lock(); try { curr_credits_available=max_credits; credits_available.signalAll(); } finally { lock.unlock(); } } // ------------------- End of management information ---------------------- 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.CONFIG: Map map=(Map)evt.getArg(); handleConfigEvent(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(this.id); 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.CONFIG: Map map=(Map)evt.getArg(); handleConfigEvent(map); break; } return up_prot.up(evt); } public void init() throws Exception{ MAX_CREDITS=new Long(max_credits); curr_credits_available=max_credits; } 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 handleConfigEvent(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(this.id, 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(this.id, 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 { 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 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 ""; } } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/SHARED_LOOPBACK.java0000644000175000017500000001155111647260573026740 0ustar moellermoellerpackage org.jgroups.protocols; import org.jgroups.Address; import org.jgroups.Event; import org.jgroups.PhysicalAddress; import org.jgroups.stack.IpAddress; import java.util.Map; 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 */ public class SHARED_LOOPBACK extends TP { private static int next_port=10000; private PhysicalAddress physical_addr=null; private Address local_addr=null; /** 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 supportsMulticasting() { return false; } public String toString() { return "SHARED_LOOPBACK(local address: " + local_addr + ')'; } public void sendMulticast(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(local_addr, data, offset, length); } catch(Throwable t) { log.error("failed sending message to " + dest, t); } } } public void sendUnicast(PhysicalAddress 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(local_addr, data, offset, length); } protected void sendToSingleMember(Address dest, byte[] buf, 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(local_addr, buf, offset, length); } public String getInfo() { return toString(); } protected PhysicalAddress getPhysicalAddress() { return physical_addr; } /*------------------------------ Protocol interface ------------------------------ */ 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: case Event.CONNECT_USE_FLUSH: case Event.CONNECT_WITH_STATE_TRANSFER_USE_FLUSH: register(channel_name, local_addr, this); break; case Event.SET_LOCAL_ADDRESS: local_addr=(Address)evt.getArg(); break; case Event.DISCONNECT: unregister(channel_name, local_addr); break; } return retval; } private static 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 static 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); } } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/SHUFFLE.java0000644000175000017500000000676311647260573025625 0ustar moellermoellerpackage org.jgroups.protocols; import org.jgroups.Event; import org.jgroups.Message; import org.jgroups.annotations.Property; import org.jgroups.annotations.Unsupported; import org.jgroups.stack.Protocol; import org.jgroups.util.TimeScheduler; import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; /** * Reorders messages by buffering them and shuffling the result after TIMEOUT ms. * * @author Bela Ban * */ @Unsupported public class SHUFFLE extends Protocol { protected TimeScheduler timer=null; protected final List up_msgs=new LinkedList(); protected final List down_msgs=new LinkedList(); protected Future task=null; @Property protected boolean up=true; @Property protected boolean down=false; @Property(description="max number of messages before we bundle") protected int max_size=10; @Property(description="max time (ms) before we pass the bundled messages up or down") protected long max_time=1500L; public boolean isUp() { return up; } public void setUp(boolean up) { this.up=up; } public boolean isDown() { return down; } public void setDown(boolean down) { this.down=down; } public int getMaxSize() { return max_size; } public void setMaxSize(int max_size) { this.max_size=max_size; } public long getMaxTime() { return max_time; } public void setMaxTime(long max_time) { this.max_time=max_time; } public void init() throws Exception { super.init(); timer=getTransport().getTimer(); } public Object up(Event evt) { if(!up) return up_prot.up(evt); if(evt.getType() != Event.MSG) return up_prot.up(evt); Message msg=(Message)evt.getArg(); synchronized(up_msgs) { up_msgs.add(msg); } if(up_msgs.size() >= max_size) { shuffleAndSendMessages(); } else startTask(); return null; } public Object down(Event evt) { if(!down) return down_prot.down(evt); if(evt.getType() != Event.MSG) return down_prot.down(evt); Message msg=(Message)evt.getArg(); synchronized(down_msgs) { down_msgs.add(msg); } if(down_msgs.size() >= max_size) { shuffleAndSendMessages(); } else startTask(); return null; } private synchronized void startTask() { if(task == null || task.isDone() || task.isCancelled()) { task=timer.schedule(new Runnable() { public void run() { shuffleAndSendMessages(); } }, max_time, TimeUnit.MILLISECONDS); } } private void shuffleAndSendMessages() { synchronized(up_msgs) { if(!up_msgs.isEmpty()) { Collections.shuffle(up_msgs); for(Message msg: up_msgs) up_prot.up(new Event(Event.MSG, msg)); up_msgs.clear(); } } synchronized(down_msgs) { if(!down_msgs.isEmpty()) { Collections.shuffle(down_msgs); for(Message msg: down_msgs) down_prot.down(new Event(Event.MSG, msg)); down_msgs.clear(); } } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/SIZE.java0000644000175000017500000000735611647260573025302 0ustar moellermoeller package org.jgroups.protocols; import org.jgroups.Event; import org.jgroups.Message; import org.jgroups.annotations.Property; import org.jgroups.annotations.Unsupported; import org.jgroups.stack.Protocol; import org.jgroups.util.ExposedByteArrayOutputStream; import org.jgroups.util.ExposedDataOutputStream; import org.jgroups.util.Util; 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 */ @Unsupported public class SIZE extends Protocol { final Vector members=new Vector(); @Property boolean print_msg=false; @Property boolean raw_buffer=false; // just print size of message buffer /** Min size in bytes above which msgs should be printed */ @Property long min_size=0; final ExposedByteArrayOutputStream out_stream=new ExposedByteArrayOutputStream(65535); final ExposedDataOutputStream out=new ExposedDataOutputStream(out_stream); public void init() { } public Object up(Event evt) { switch(evt.getType()) { case Event.MSG: Message msg=(Message)evt.getArg(); int payload_size=msg.getLength(); if(raw_buffer) { if(log.isTraceEnabled()) log.trace("size of message buffer is " + payload_size + ", " + numHeaders(msg) + " headers"); } else { int 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) { synchronized(out_stream) { try { out_stream.reset(); out.reset(); 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(); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/SMACK.java0000644000175000017500000003264611647260573025366 0ustar moellermoellerpackage org.jgroups.protocols; import org.jgroups.*; import org.jgroups.annotations.Experimental; import org.jgroups.annotations.GuardedBy; import org.jgroups.annotations.Property; import org.jgroups.annotations.Unsupported; import org.jgroups.conf.PropertyConverters; import org.jgroups.stack.AckMcastSenderWindow; import org.jgroups.stack.AckReceiverWindow; import org.jgroups.stack.Protocol; import org.jgroups.stack.StaticInterval; import org.jgroups.util.Util; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; import java.util.Vector; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * 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 *
            Fix membershop bug: start a, b, kill b, restart b: b will be suspected by a. */ @Experimental @Unsupported public class SMACK extends Protocol implements AckMcastSenderWindow.RetransmitCommand { @Property(converter=PropertyConverters.LongArray.class) long[] timeout=new long[]{1000,2000,3000}; // retransmit timeouts (for AckMcastSenderWindow) @Property 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=Util.createConcurrentMap(); // keys=sender (Address), values=AckReceiverWindow final Map xmit_table=Util.createConcurrentMap(); // keeps track of num xmits / member (keys: mbr, val:num) Address local_addr=null; // my own address @GuardedBy("lock") long seqno=1; // seqno for msgs sent by this sender final Lock lock=new ReentrantLock(); // for access to seqno long vid=1; // for the fake view changes @Property boolean print_local_addr=true; public SMACK() { } public void stop() { if(sender_win != null) { sender_win.stop(); sender_win=null; } for(AckReceiverWindow win: receivers.values()) { win.reset(); } receivers.clear(); } public Object up(Event evt) { Address sender; switch(evt.getType()) { 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 || msg.isFlagSet(Message.NO_RELIABILITY)) break; sender=msg.getSrc(); SmackHeader hdr=(SmackHeader)msg.getHeader(this.id); 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(this.id, 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(this.id, 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(this.id, new SmackHeader(SmackHeader.LEAVE_ANNOUNCEMENT, -1)); down_prot.down(new Event(Event.MSG, leave_msg)); Util.sleep(100); 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(this.id, 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 || msg.isFlagSet(Message.NO_RELIABILITY)) break; if(msg.getDest() == null || msg.getDest().isMulticastAddress()) { lock.lock(); try { long msg_id=seqno; msg.putHeader(this.id, new SmackHeader(SmackHeader.MCAST, msg_id)); sender_win.add(msg_id, msg, new Vector
            (members)); if(log.isTraceEnabled()) log.trace("sending mcast #" + msg_id); seqno++; } finally { lock.unlock(); } } break; 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; } 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 { 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; public SmackHeader() { } public SmackHeader(byte type, long seqno) { this.type=type; this.seqno=seqno; } 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 for(Map.Entry entry: receivers.entrySet()) { Address member=entry.getKey(); if(!members.contains(member)) { AckReceiverWindow win=entry.getValue(); win.reset(); } } receivers.keySet().retainAll(members); } } boolean containsMember(Address mbr) { synchronized(members) { return members.contains(mbr); } } /* --------------------------------- End of Private methods ------------------------------------ */ }libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/STATS.java0000644000175000017500000001333111647260573025414 0ustar moellermoellerpackage 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 org.jgroups.annotations.ManagedAttribute; import java.util.*; /** * Provides various stats * @author Bela Ban */ 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 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(); } @ManagedAttribute public long getSentMessages() {return sent_msgs;} @ManagedAttribute public long getSentBytes() {return sent_bytes;} @ManagedAttribute public long getSentUnicastMessages() {return sent_ucasts;} @ManagedAttribute public long getSentUnicastBytes() {return sent_ucast_bytes;} @ManagedAttribute public long getSentMcastMessages() {return sent_mcasts;} @ManagedAttribute public long getSentMcastBytes() {return sent_mcast_bytes;} @ManagedAttribute public long getReceivedMessages() {return received_msgs;} @ManagedAttribute public long getReceivedBytes() {return received_bytes;} @ManagedAttribute public long getReceivedUnicastMessages() {return received_ucasts;} @ManagedAttribute public long getReceivedUnicastBytes() {return received_ucast_bytes;} @ManagedAttribute public long getReceivedMcastMessages() {return received_mcasts;} @ManagedAttribute 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(); } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/STOMP.java0000644000175000017500000006527211647260573025433 0ustar moellermoellerpackage org.jgroups.protocols; import org.jgroups.*; import org.jgroups.annotations.*; import org.jgroups.stack.Protocol; import org.jgroups.util.UUID; import org.jgroups.util.Util; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.EOFException; import java.io.IOException; import java.net.*; import java.nio.ByteBuffer; import java.util.*; import java.util.concurrent.ConcurrentMap; /** * Protocol which provides STOMP (http://stomp.codehaus.org/) support. Very simple implementation, with a * one-thread-per-connection model. Use for a few hundred clients max.

            * The intended use for this protocol is pub-sub with clients which handle text messages, e.g. stock updates, * SMS messages to mobile clients, SNMP traps etc.

            * Note that the full STOMP protocol has not yet been implemented, e.g. transactions are not supported. * todo: use a thread pool to handle incoming frames and to send messages to clients *

            * todo: add PING to test health of client connections *

            * @author Bela Ban * @since 2.11 */ @MBean(description="Server side STOPM protocol, STOMP clients can connect to it") @Experimental public class STOMP extends Protocol implements Runnable { /* ----------------------------------------- Properties ----------------------------------------------- */ @LocalAddress @Property(name="bind_addr", description="The bind address which should be used by the server socket. The following special values " + "are also recognized: GLOBAL, SITE_LOCAL, LINK_LOCAL and NON_LOOPBACK", defaultValueIPv4="0.0.0.0", defaultValueIPv6="::", systemProperty={Global.STOMP_BIND_ADDR},writable=false) protected InetAddress bind_addr=null; @Property(description="If set, then endpoint will be set to this address",systemProperty=Global.STOMP_ENDPOINT_ADDR) protected String endpoint_addr; @Property(description="Port on which the STOMP protocol listens for requests",writable=false) protected int port=8787; @Property(description="If set to false, then a destination of /a/b match /a/b/c, a/b/d, a/b/c/d etc") protected boolean exact_destination_match=true; @Property(description="If true, information such as a list of endpoints, or views, will be sent to all clients " + "(via the INFO command). This allows for example intelligent clients to connect to " + "a different server should a connection be closed.") protected boolean send_info=true; @Property(description="Forward received messages which don't have a StompHeader to clients") protected boolean forward_non_client_generated_msgs=false; /* --------------------------------------------- JMX ---------------------------------------------------*/ @ManagedAttribute(description="Number of client connections",writable=false) public int getNumConnections() {return connections.size();} @ManagedAttribute(description="Number of subscriptions",writable=false) public int getNumSubscriptions() {return subscriptions.size();} @ManagedAttribute(description="Print subscriptions",writable=false) public String getSubscriptions() {return subscriptions.keySet().toString();} @ManagedAttribute public String getEndpoints() {return endpoints.toString();} /* --------------------------------------------- Fields ------------------------------------------------------ */ protected Address local_addr; protected ServerSocket srv_sock; @ManagedAttribute(writable=false) protected String endpoint; protected Thread acceptor; protected final List connections=new LinkedList(); protected final Map endpoints=new HashMap(); protected View view; // Subscriptions and connections which are subscribed protected final ConcurrentMap> subscriptions=Util.createConcurrentMap(20); public static enum ClientVerb {CONNECT, SEND, SUBSCRIBE, UNSUBSCRIBE, BEGIN, COMMIT, ABORT, ACK, DISCONNECT} public static enum ServerVerb {MESSAGE, RECEIPT, ERROR, CONNECTED, INFO} public static final byte NULL_BYTE=0; public STOMP() { } public void start() throws Exception { super.start(); srv_sock=Util.createServerSocket(getSocketFactory(), Global.STOMP_SRV_SOCK, bind_addr, port); if(log.isDebugEnabled()) log.debug("server socket listening on " + srv_sock.getLocalSocketAddress()); if(acceptor == null) { acceptor=getThreadFactory().newThread(this, "STOMP acceptor"); acceptor.setDaemon(true); acceptor.start(); } endpoint=endpoint_addr != null? endpoint_addr : getAddress(); } public void stop() { if(log.isDebugEnabled()) log.debug("closing server socket " + srv_sock.getLocalSocketAddress()); if(acceptor != null && acceptor.isAlive()) { try { // this will terminate thread, peer will receive SocketException (socket close) getSocketFactory().close(srv_sock); } catch(Exception ex) { } } synchronized(connections) { for(Connection conn: connections) conn.stop(); connections.clear(); } acceptor=null; super.stop(); } // Acceptor loop public void run() { Socket client_sock; while(acceptor != null && srv_sock != null) { try { client_sock=srv_sock.accept(); if(log.isTraceEnabled()) log.trace("accepted connection from " + client_sock.getInetAddress() + ':' + client_sock.getPort()); Connection conn=new Connection(client_sock); Thread thread=getThreadFactory().newThread(conn, "STOMP client connection"); thread.setDaemon(true); synchronized(connections) { connections.add(conn); } thread.start(); conn.sendInfo(); } catch(IOException io_ex) { break; } } acceptor=null; } public Object down(Event evt) { switch(evt.getType()) { case Event.VIEW_CHANGE: handleView((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) { switch(evt.getType()) { case Event.MSG: Message msg=(Message)evt.getArg(); StompHeader hdr=(StompHeader)msg.getHeader(id); if(hdr == null) { if(forward_non_client_generated_msgs) { HashMap hdrs=new HashMap(); hdrs.put("sender", msg.getSrc().toString()); sendToClients(hdrs, msg.getRawBuffer(), msg.getOffset(), msg.getLength()); } break; } switch(hdr.type) { case MESSAGE: sendToClients(hdr.headers, msg.getRawBuffer(), msg.getOffset(), msg.getLength()); break; case ENDPOINT: String tmp_endpoint=hdr.headers.get("endpoint"); if(tmp_endpoint != null) { boolean update_clients; String old_endpoint=null; synchronized(endpoints) { endpoints.put(msg.getSrc(), tmp_endpoint); } update_clients=old_endpoint == null || !old_endpoint.equals(tmp_endpoint); if(update_clients && this.send_info) { synchronized(connections) { for(Connection conn: connections) { conn.writeResponse(ServerVerb.INFO, "endpoints", getAllEndpoints()); } } } } return null; default: throw new IllegalArgumentException("type " + hdr.type + " is not known"); } break; case Event.VIEW_CHANGE: handleView((View)evt.getArg()); break; } return up_prot.up(evt); } public static Frame readFrame(DataInputStream in) throws IOException { String verb=Util.readLine(in); if(verb == null) throw new EOFException("reading verb"); if(verb.length() == 0) return null; verb=verb.trim(); Map headers=new HashMap(); byte[] body=null; for(;;) { String header=Util.readLine(in); if(header == null) throw new EOFException("reading header"); if(header.length() == 0) break; int index=header.indexOf(":"); if(index != -1) headers.put(header.substring(0, index).trim(), header.substring(index+1).trim()); } if(headers.containsKey("content-length")) { int length=Integer.parseInt(headers.get("content-length")); body=new byte[length]; in.read(body, 0, body.length); } else { ByteBuffer buf=ByteBuffer.allocate(500); boolean terminate=false; for(;;) { int c=in.read(); if(c == -1 || c == 0) terminate=true; if(buf.remaining() == 0 || terminate) { if(body == null) { body=new byte[buf.position()]; System.arraycopy(buf.array(), buf.arrayOffset(), body, 0, buf.position()); } else { byte[] tmp=new byte[body.length + buf.position()]; System.arraycopy(body, 0, tmp, 0, body.length); try { System.arraycopy(buf.array(), buf.arrayOffset(), tmp, body.length, buf.position()); } catch(Throwable t) { } body=tmp; } buf.rewind(); } if(terminate) break; buf.put((byte)c); } } return new Frame(verb, headers, body); } protected void handleView(View view) { broadcastEndpoint(); List

            mbrs=view.getMembers(); this.view=view; synchronized(endpoints) { endpoints.keySet().retainAll(mbrs); } synchronized(connections) { for(Connection conn: connections) conn.sendInfo(); } } private String getAddress() { InetSocketAddress saddr=(InetSocketAddress)srv_sock.getLocalSocketAddress(); InetAddress tmp=saddr.getAddress(); if(!tmp.isAnyLocalAddress()) return tmp.getHostAddress() + ":" + srv_sock.getLocalPort(); for(Util.AddressScope scope: Util.AddressScope.values()) { try { InetAddress addr=Util.getAddress(scope); if(addr != null) return addr.getHostAddress() + ":" + srv_sock.getLocalPort(); } catch(SocketException e) { } } return null; } protected String getAllEndpoints() { synchronized(endpoints) { return Util.printListWithDelimiter(endpoints.values(), ","); } } // protected String getAllClients() { // StringBuilder sb=new StringBuilder(); // boolean first=true; // // synchronized(connections) { // for(Connection conn: connections) { // UUID session_id=conn.session_id; // if(session_id != null) { // if(first) // first=false; // else // sb.append(","); // sb.append(session_id); // } // } // } // // return sb.toString(); // } protected void broadcastEndpoint() { if(endpoint != null) { Message msg=new Message(); msg.putHeader(id, StompHeader.createHeader(StompHeader.Type.ENDPOINT, "endpoint", endpoint)); down_prot.down(new Event(Event.MSG, msg)); } } // private void sendToClients(String destination, String sender, byte[] buffer, int offset, int length) { // int len=50 + length + (ServerVerb.MESSAGE.name().length() + 2) // + (destination != null? destination.length()+ 2 : 0) // + (sender != null? sender.length() +2 : 0) // + (buffer != null? 20 : 0); // // ByteBuffer buf=ByteBuffer.allocate(len); // // StringBuilder sb=new StringBuilder(ServerVerb.MESSAGE.name()).append("\n"); // if(destination != null) // sb.append("destination: ").append(destination).append("\n"); // if(sender != null) // sb.append("sender: ").append(sender).append("\n"); // if(buffer != null) // sb.append("content-length: ").append(String.valueOf(length)).append("\n"); // sb.append("\n"); // // byte[] tmp=sb.toString().getBytes(); // // if(buffer != null) { // buf.put(tmp, 0, tmp.length); // buf.put(buffer, offset, length); // } // buf.put(NULL_BYTE); // // final Set target_connections=new HashSet(); // if(destination == null) { // synchronized(connections) { // target_connections.addAll(connections); // } // } // else { // if(!exact_destination_match) { // for(Map.Entry> entry: subscriptions.entrySet()) { // if(entry.getKey().startsWith(destination)) // target_connections.addAll(entry.getValue()); // } // } // else { // Set conns=subscriptions.get(destination); // if(conns != null) // target_connections.addAll(conns); // } // } // // for(Connection conn: target_connections) // conn.writeResponse(buf.array(), buf.arrayOffset(), buf.position()); // } private void sendToClients(Map headers, byte[] buffer, int offset, int length) { int len=50 + length + (ServerVerb.MESSAGE.name().length() + 2); if(headers != null) { for(Map.Entry entry: headers.entrySet()) { len+=entry.getKey().length() +2; len+=entry.getValue().length() +2; len+=5; // fill chars, such as ": " or "\n" } } len+=(buffer != null? 20 : 0); ByteBuffer buf=ByteBuffer.allocate(len); StringBuilder sb=new StringBuilder(ServerVerb.MESSAGE.name()).append("\n"); if(headers != null) { for(Map.Entry entry: headers.entrySet()) sb.append(entry.getKey()).append(": ").append(entry.getValue()).append("\n"); } if(buffer != null) sb.append("content-length: ").append(String.valueOf(length)).append("\n"); sb.append("\n"); byte[] tmp=sb.toString().getBytes(); if(buffer != null) { buf.put(tmp, 0, tmp.length); buf.put(buffer, offset, length); } buf.put(NULL_BYTE); final Set target_connections=new HashSet(); String destination=headers != null? headers.get("destination") : null; if(destination == null) { synchronized(connections) { target_connections.addAll(connections); } } else { if(!exact_destination_match) { for(Map.Entry> entry: subscriptions.entrySet()) { if(entry.getKey().startsWith(destination)) target_connections.addAll(entry.getValue()); } } else { Set conns=subscriptions.get(destination); if(conns != null) target_connections.addAll(conns); } } for(Connection conn: target_connections) conn.writeResponse(buf.array(), buf.arrayOffset(), buf.position()); } /** * Class which handles a connection to a client */ public class Connection implements Runnable { protected final Socket sock; protected final DataInputStream in; protected final DataOutputStream out; protected final UUID session_id=UUID.randomUUID(); public Connection(Socket sock) throws IOException { this.sock=sock; this.in=new DataInputStream(sock.getInputStream()); this.out=new DataOutputStream(sock.getOutputStream()); } public void stop() { if(log.isTraceEnabled()) log.trace("closing connection to " + sock.getRemoteSocketAddress()); Util.close(in); Util.close(out); Util.close(sock); } protected void remove() { synchronized(connections) { connections.remove(this); } for(Set conns: subscriptions.values()) { conns.remove(this); } for(Iterator>> it=subscriptions.entrySet().iterator(); it.hasNext();) { Map.Entry> entry=it.next(); if(entry.getValue().isEmpty()) it.remove(); } } public void run() { while(!sock.isClosed()) { try { Frame frame=readFrame(in); if(frame != null) { if(log.isTraceEnabled()) log.trace(frame); handleFrame(frame); } } catch(IOException ex) { stop(); remove(); } catch(Throwable t) { log.error("failure reading frame", t); } } } protected void handleFrame(Frame frame) { Map headers=frame.getHeaders(); ClientVerb verb=ClientVerb.valueOf(frame.getVerb()); switch(verb) { case CONNECT: writeResponse(ServerVerb.CONNECTED, "session-id", session_id.toString(), "password-check", "none"); break; case SEND: if(!headers.containsKey("sender")) { headers.put("sender", session_id.toString()); } Message msg=new Message(null, null, frame.getBody()); Header hdr=StompHeader.createHeader(StompHeader.Type.MESSAGE, headers); msg.putHeader(id, hdr); down_prot.down(new Event(Event.MSG, msg)); String receipt=headers.get("receipt"); if(receipt != null) writeResponse(ServerVerb.RECEIPT, "receipt-id", receipt); break; case SUBSCRIBE: String destination=headers.get("destination"); if(destination != null) { Set conns=subscriptions.get(destination); if(conns == null) { conns=new HashSet(); Set tmp=subscriptions.putIfAbsent(destination, conns); if(tmp != null) conns=tmp; } conns.add(this); } break; case UNSUBSCRIBE: destination=headers.get("destination"); if(destination != null) { Set conns=subscriptions.get(destination); if(conns != null) { if(conns.remove(this) && conns.isEmpty()) subscriptions.remove(destination); } } break; case BEGIN: break; case COMMIT: break; case ABORT: break; case ACK: break; case DISCONNECT: break; default: log.error("Verb " + frame.getVerb() + " is not handled"); break; } } public void sendInfo() { if(send_info) { writeResponse(ServerVerb.INFO, "local_addr", local_addr != null? local_addr.toString() : "n/a", "view", view.toString(), "endpoints", getAllEndpoints()); // "clients", getAllClients()); } } /** * Sends back a response. The keys_and_values vararg array needs to have an even number of elements * @param response * @param keys_and_values */ private void writeResponse(ServerVerb response, String ... keys_and_values) { String tmp=response.name(); try { out.write(tmp.getBytes()); out.write('\n'); for(int i=0; i < keys_and_values.length; i++) { String key=keys_and_values[i]; String val=keys_and_values[++i]; out.write((key + ": " + val + "\n").getBytes()); } out.write("\n".getBytes()); out.write(NULL_BYTE); out.flush(); } catch(IOException ex) { log.error("failed writing response " + response + ": " + ex); } } private void writeResponse(byte[] response, int offset, int length) { try { out.write(response, offset, length); out.flush(); } catch(IOException ex) { log.error("failed writing response: " + ex); } } } public static class Frame { final String verb; final Map headers; final byte[] body; public Frame(String verb, Map headers, byte[] body) { this.verb=verb; this.headers=headers; this.body=body; } public byte[] getBody() { return body; } public Map getHeaders() { return headers; } public String getVerb() { return verb; } public String toString() { StringBuilder sb=new StringBuilder(); sb.append(verb).append("\n"); if(headers != null && !headers.isEmpty()) { for(Map.Entry entry: headers.entrySet()) sb.append(entry.getKey()).append(": ").append(entry.getValue()).append("\n"); } if(body != null && body.length > 0) { sb.append("body: "); if(body.length < 50) sb.append(new String(body)).append(" (").append(body.length).append(" bytes)"); else sb.append(body.length).append(" bytes"); } return sb.toString(); } } public static class StompHeader extends org.jgroups.Header { public static enum Type {MESSAGE, ENDPOINT} protected Type type; protected final Map headers=new HashMap(); public StompHeader() { } private StompHeader(Type type) { this.type=type; } /** * Creates a new header * @param type * @param headers Keys and values to be added to the header hashmap. Needs to be an even number * @return */ public static StompHeader createHeader(Type type, String ... headers) { StompHeader retval=new StompHeader(type); if(headers != null) { for(int i=0; i < headers.length; i++) { String key=headers[i]; String value=headers[++i]; retval.headers.put(key, value); } } return retval; } public static StompHeader createHeader(Type type, Map headers) { StompHeader retval=new StompHeader(type); if(headers != null) retval.headers.putAll(headers); return retval; } public int size() { int retval=Global.INT_SIZE *2; // type + size of hashmap for(Map.Entry entry: headers.entrySet()) { retval+=entry.getKey().length() +2; retval+=entry.getValue().length() +2; } return retval; } public void writeTo(DataOutputStream out) throws IOException { out.writeInt(type.ordinal()); out.writeInt(headers.size()); for(Map.Entry entry: headers.entrySet()) { out.writeUTF(entry.getKey()); out.writeUTF(entry.getValue()); } } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { type=Type.values()[in.readInt()]; int size=in.readInt(); for(int i=0; i < size; i++) { String key=in.readUTF(); String value=in.readUTF(); headers.put(key, value); } } public String toString() { StringBuilder sb=new StringBuilder(type.toString()); sb.append("headers: ").append(headers); return sb.toString(); } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/TCP.java0000644000175000017500000001340011647260573025141 0ustar moellermoeller package org.jgroups.protocols; import org.jgroups.Address; import org.jgroups.PhysicalAddress; import org.jgroups.Global; import org.jgroups.annotations.ManagedAttribute; import org.jgroups.annotations.ManagedOperation; import org.jgroups.blocks.TCPConnectionMap; import org.jgroups.util.SocketFactory; import java.net.InetAddress; import java.util.Collection; /** * 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 outgoing hash table, the associated socket will be reused to send * message, otherwise a new socket is created and put in the hash table. When a * socket connection breaks or a member is removed from the group, the * corresponding items in the incoming and outgoing hash tables will be removed * as well. *

            * * This functionality is in TCPConnectionMap, which is 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 TCPConnectionMap.Receiver { private TCPConnectionMap ct=null; public TCP() {} @ManagedAttribute public int getOpenConnections() { return ct.getNumConnections(); } @ManagedOperation public String printConnections() { return ct.printConnections(); } public void setSocketFactory(SocketFactory factory) { super.setSocketFactory(factory); if(ct != null) ct.setSocketFactory(factory); } 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=createConnectionMap(reaper_interval, conn_expire_time, bind_addr, external_addr, bind_port, bind_port+port_range ); ct.setReceiveBufferSize(recv_buf_size); ct.setSendQueueSize(send_queue_size); ct.setUseSendQueues(use_send_queues); ct.setSendBufferSize(send_buf_size); ct.setSocketConnectionTimeout(sock_conn_timeout); ct.setTcpNodelay(tcp_nodelay); ct.setLinger(linger); ct.setSocketFactory(getSocketFactory()); // we first start threads in TP (http://jira.jboss.com/jira/browse/JGRP-626) super.start(); } public void stop() { if(log.isDebugEnabled()) log.debug("closing sockets and stopping threads"); ct.stop(); //not needed, but just in case super.stop(); } protected void handleConnect() throws Exception { if(isSingleton()) { if(connect_count == 0) { ct.start(); } super.handleConnect(); } else ct.start(); } protected void handleDisconnect() { if(isSingleton()) { super.handleDisconnect(); if(connect_count == 0) { ct.stop(); } } else ct.stop(); } /** * @param reaperInterval * @param connExpireTime * @param bindAddress * @param startPort * @throws Exception * @return TCPConnectionMap Subclasses override this method to initialize a different version of ConnectionMap */ protected TCPConnectionMap createConnectionMap(long reaperInterval, long connExpireTime, InetAddress bindAddress, InetAddress externalAddress, int startPort, int endPort ) throws Exception { TCPConnectionMap cTable; if(reaperInterval == 0 && connExpireTime == 0) { cTable=new TCPConnectionMap(Global.TCP_SRV_SOCK, getThreadFactory(), getSocketFactory(), 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 TCPConnectionMap(Global.TCP_SRV_SOCK, getThreadFactory(), getSocketFactory(), this, bindAddress, externalAddress, startPort, endPort, reaperInterval, connExpireTime ); } return cTable; } protected PhysicalAddress getPhysicalAddress() { return ct != null? (PhysicalAddress)ct.getLocalAddress() : null; } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/TCPGOSSIP.java0000644000175000017500000002414011647260573026071 0ustar moellermoeller package org.jgroups.protocols; import java.net.InetSocketAddress; import java.util.*; import java.util.concurrent.CopyOnWriteArrayList; import org.jgroups.*; import org.jgroups.annotations.DeprecatedProperty; import org.jgroups.annotations.ManagedOperation; import org.jgroups.annotations.Property; import org.jgroups.conf.PropertyConverters; import org.jgroups.stack.RouterStubManager; import org.jgroups.stack.RouterStub; import org.jgroups.util.Promise; import org.jgroups.util.Tuple; import org.jgroups.util.UUID; /** * 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 */ @DeprecatedProperty(names={"gossip_refresh_rate"}) public class TCPGOSSIP extends Discovery { /* ----------------------------------------- Properties -------------------------------------------------- */ @Property(description="Max time for socket creation. Default is 1000 msec") int sock_conn_timeout=1000; @Property(description="Max time in milliseconds to block on a read. 0 blocks forever") int sock_read_timeout=3000; @Property(description="Interval (ms) by which a disconnected stub attempts to reconnect to the GossipRouter") long reconnect_interval=10000L; @Property(name="initial_hosts", description="Comma delimited list of hosts to be contacted for initial membership", converter=PropertyConverters.InitialHosts2.class) public void setInitialHosts(List hosts) { if(hosts == null || hosts.isEmpty()) throw new IllegalArgumentException("initial_hosts must contain the address of at least one GossipRouter"); initial_hosts.addAll(hosts) ; } public List getInitialHosts() { return initial_hosts; } public boolean isDynamic() { return true; } /* --------------------------------------------- Fields ------------------------------------------------------ */ // (list of IpAddresses) hosts to be contacted for the initial membership private final List initial_hosts = new CopyOnWriteArrayList(); private volatile RouterStubManager stubManager; public void init() throws Exception { super.init(); stubManager = RouterStubManager.emptyGossipClientStubManager(this); if(timeout <= sock_conn_timeout) throw new IllegalArgumentException("timeout (" + timeout + ") must be greater than sock_conn_timeout (" + sock_conn_timeout + ")"); // we cannot use TCPGOSSIP together with TUNNEL (https://jira.jboss.org/jira/browse/JGRP-1101) TP transport=getTransport(); if(transport instanceof TUNNEL) throw new IllegalStateException("TCPGOSSIP cannot be used with TUNNEL; use either TUNNEL:PING or " + "TCP:TCPGOSSIP as valid configurations"); } public void start() throws Exception { super.start(); } public void stop() { super.stop(); stubManager.disconnectStubs(); } public void destroy() { stubManager.destroyStubs(); super.destroy(); } 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"); stubManager.destroyStubs(); stubManager = new RouterStubManager(this, group_addr, local_addr, reconnect_interval); for (InetSocketAddress host : initial_hosts) { stubManager.createAndRegisterStub(host.getHostName(), host.getPort(), null); } connectAllStubs(group_addr, local_addr); } } public void handleDisconnect() { stubManager.disconnectStubs(); } @SuppressWarnings("unchecked") public void sendGetMembersRequest(String cluster_name, Promise promise, ViewId view_id) throws Exception { if (group_addr == null) { if (log.isErrorEnabled()) log.error("cluster_name is null, cannot get membership"); return; } if (log.isTraceEnabled()) log.trace("fetching members from GossipRouter(s)"); final List responses = new LinkedList(); for (RouterStub stub : stubManager.getStubs()) { try { List rsps = stub.getMembers(group_addr); responses.addAll(rsps); } catch(Throwable t) { log.warn("failed fetching members from " + stub.getGossipRouterAddress() + ": " + t); } } final Set

            initial_mbrs = new HashSet
            (); for (PingData rsp : responses) { Address logical_addr = rsp.getAddress(); initial_mbrs.add(logical_addr); // 1. Set physical addresses Collection physical_addrs = rsp.getPhysicalAddrs(); if (physical_addrs != null) { for (PhysicalAddress physical_addr : physical_addrs) down(new Event(Event.SET_PHYSICAL_ADDRESS, new Tuple( logical_addr, physical_addr))); } // 2. Set logical name String logical_name = rsp.getLogicalName(); if (logical_name != null && logical_addr instanceof org.jgroups.util.UUID) org.jgroups.util.UUID.add(logical_addr, logical_name); } if (initial_mbrs.isEmpty()) { if (log.isTraceEnabled()) log.trace("[FIND_INITIAL_MBRS]: found no members"); return; } if (log.isTraceEnabled()) log.trace("consolidated mbrs from GossipRouter(s) are " + initial_mbrs); PhysicalAddress physical_addr=(PhysicalAddress)down_prot.down(new Event(Event.GET_PHYSICAL_ADDRESS, local_addr)); PingData data=new PingData(local_addr, null, false, UUID.get(local_addr), Arrays.asList(physical_addr)); for (Address mbr_addr : initial_mbrs) { Message msg = new Message(mbr_addr); msg.setFlag(Message.OOB); PingHeader hdr = new PingHeader(PingHeader.GET_MBRS_REQ, data, cluster_name); hdr.view_id=view_id; msg.putHeader(this.id, hdr); if (log.isTraceEnabled()) log.trace("[FIND_INITIAL_MBRS] sending GET_MBRS_REQ request to " + mbr_addr); down_prot.down(new Event(Event.MSG, msg)); } } @ManagedOperation public void addInitialHost(String hostname, int port) { //if there is such a stub already, remove and destroy it removeInitialHost(hostname, port); //now re-add it InetSocketAddress isa = new InetSocketAddress(hostname, port); initial_hosts.add(isa); RouterStub s = new RouterStub(isa.getHostName(), isa.getPort(),null,stubManager); connect(s, group_addr, local_addr); stubManager.registerStub(s); } @ManagedOperation public boolean removeInitialHost(String hostname, int port) { InetSocketAddress isa = new InetSocketAddress(hostname, port); RouterStub unregisterStub = stubManager.unregisterStub(isa); if(unregisterStub != null) { stubManager.stopReconnecting(unregisterStub); unregisterStub.destroy(); } return initial_hosts.remove(isa); } protected void connectAllStubs(String group, Address logical_addr) { String logical_name=org.jgroups.util.UUID.get(logical_addr); PhysicalAddress physical_addr=(PhysicalAddress)down_prot.down(new Event(Event.GET_PHYSICAL_ADDRESS, local_addr)); List physical_addrs=physical_addr != null? new ArrayList() : null; if(physical_addr != null) physical_addrs.add(physical_addr); for (RouterStub stub : stubManager.getStubs()) { try { if(log.isTraceEnabled()) log.trace("trying to connect to " + stub.getGossipRouterAddress()); stub.connect(group, logical_addr, logical_name, physical_addrs); } catch(Exception e) { if(log.isErrorEnabled()) log.error("failed connecting to " + stub.getGossipRouterAddress() + ": " + e); stubManager.startReconnecting(stub); } } } protected void connect(RouterStub stub, String group, Address logical_addr) { String logical_name = org.jgroups.util.UUID.get(logical_addr); PhysicalAddress physical_addr = (PhysicalAddress) down_prot.down(new Event(Event.GET_PHYSICAL_ADDRESS, local_addr)); List physical_addrs = physical_addr != null ? new ArrayList(): null; if (physical_addr != null) physical_addrs.add(physical_addr); try { if (log.isTraceEnabled()) log.trace("trying to connect to " + stub.getGossipRouterAddress()); stub.connect(group, logical_addr, logical_name, physical_addrs); } catch (Exception e) { if (log.isErrorEnabled()) log.error("failed connecting to " + stub.getGossipRouterAddress() + ": " + e); stubManager.startReconnecting(stub); } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/TCPPING.java0000644000175000017500000001426411647260573025630 0ustar moellermoeller package org.jgroups.protocols; import org.jgroups.*; import org.jgroups.annotations.ManagedAttribute; import org.jgroups.annotations.ManagedOperation; import org.jgroups.annotations.Property; import org.jgroups.conf.PropertyConverters; import org.jgroups.stack.IpAddress; import org.jgroups.util.BoundedList; import org.jgroups.util.Promise; import org.jgroups.util.UUID; import java.util.*; /** * 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 membership * 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 configuration, 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 { /* ----------------------------------------- Properties --------------------------------------- */ @Property(description="Number of ports to be probed for initial membership. Default is 1") private int port_range=1; @Property(name="initial_hosts", description="Comma delimited list of hosts to be contacted for initial membership", converter=PropertyConverters.InitialHosts.class, dependsUpon="port_range", systemProperty=Global.TCPPING_INITIAL_HOSTS) private List initial_hosts=null; @Property(description="max number of hosts to keep beyond the ones in initial_hosts") protected int max_dynamic_hosts=100; /* --------------------------------------------- Fields ------------------------------------------------------ */ /** * List of PhysicalAddresses */ /** https://jira.jboss.org/jira/browse/JGRP-989 */ protected final BoundedList dynamic_hosts=new BoundedList(max_dynamic_hosts); public TCPPING() { return_entire_cache=true; } public boolean isDynamic() { return false; } /** * 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 void setInitialHosts(List initial_hosts) { this.initial_hosts=initial_hosts; } public int getPortRange() { return port_range; } public void setPortRange(int port_range) { this.port_range=port_range; } @ManagedAttribute public String getDynamicHostList() { return dynamic_hosts.toString(); } @ManagedOperation public void clearDynamicHostList() { dynamic_hosts.clear(); } @ManagedAttribute public String getInitialHostsList() { return initial_hosts.toString(); } public void init() throws Exception { super.init(); } public void start() throws Exception { super.start(); } public void sendGetMembersRequest(String cluster_name, Promise promise, ViewId view_id) throws Exception{ PhysicalAddress physical_addr=(PhysicalAddress)down_prot.down(new Event(Event.GET_PHYSICAL_ADDRESS, local_addr)); PingData data=new PingData(local_addr, null, false, UUID.get(local_addr), Arrays.asList(physical_addr)); PingHeader hdr=new PingHeader(PingHeader.GET_MBRS_REQ, data, cluster_name); hdr.view_id=view_id; Set combined_target_members=new HashSet(initial_hosts); combined_target_members.addAll(dynamic_hosts); for(final Address addr: combined_target_members) { if(addr.equals(physical_addr)) continue; final Message msg=new Message(addr, null, null); msg.setFlag(Message.OOB); msg.putHeader(this.id, hdr); 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 " + addr + ": " + ex); } } }); } } public Object down(Event evt) { Object retval=super.down(evt); switch(evt.getType()) { case Event.VIEW_CHANGE: for(Address logical_addr: members) { PhysicalAddress physical_addr=(PhysicalAddress)down_prot.down(new Event(Event.GET_PHYSICAL_ADDRESS, logical_addr)); if(physical_addr != null && !initial_hosts.contains(physical_addr)) { dynamic_hosts.addIfAbsent(physical_addr); } } return_entire_cache=true; break; } return retval; } public void discoveryRequestReceived(Address sender, String logical_name, Collection physical_addrs) { super.discoveryRequestReceived(sender, logical_name, physical_addrs); if(physical_addrs != null) { for(PhysicalAddress addr: physical_addrs) { if(!initial_hosts.contains(addr)) dynamic_hosts.addIfAbsent(addr); } } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/TCP_NIO.java0000644000175000017500000001114111647260573025646 0ustar moellermoellerpackage org.jgroups.protocols; import org.jgroups.Address; import org.jgroups.PhysicalAddress; import org.jgroups.annotations.Experimental; import org.jgroups.annotations.ManagedAttribute; import org.jgroups.annotations.Property; import org.jgroups.annotations.Unsupported; import org.jgroups.blocks.BasicConnectionTable; import org.jgroups.blocks.ConnectionTableNIO; import java.net.InetAddress; import java.util.Collection; /** * Transport using NIO * @author Scott Marlow * @author Alex Fu * @author Bela Ban */ @Experimental @Unsupported 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) throws Exception { ConnectionTableNIO retval=null; if (ri == 0 && cet == 0) { retval = new ConnectionTableNIO(this, b_addr, bc_addr, s_port, e_port, 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, 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();} protected PhysicalAddress getPhysicalAddress() { return ct != null? (PhysicalAddress)ct.getLocalAddress() : null; } 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,bind_port,bind_port+port_range); 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); super.start(); } public void retainAll(Collection
            members) { ct.retainAll(members); } public void stop() { ct.stop(); super.stop(); } public int getReaderThreads() { return reader_threads; } public int getWriterThreads() { return writer_threads; } public int getProcessorThreads() { return processor_threads; } public int getProcessorMinThreads() { return processor_minThreads;} public int getProcessorMaxThreads() { return processor_maxThreads;} public int getProcessorQueueSize() { return processor_queueSize; } public long getProcessorKeepAliveTime() { return processor_keepAliveTime; } @ManagedAttribute public int getOpenConnections() {return ct.getNumConnections();} @Property private int reader_threads= 3; @Property private int writer_threads= 3; @Property private int processor_threads= 5; // PooledExecutor.createThreads() @Property private int processor_minThreads= 5; // PooledExecutor.setMinimumPoolSize() @Property private int processor_maxThreads= 5; // PooledExecutor.setMaxThreads() @Property private int processor_queueSize=100; // Number of queued requests that can be pending waiting // for a background thread to run the request. @Property private long 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; }libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/TP.java0000644000175000017500000026611511647260573025053 0ustar moellermoellerpackage org.jgroups.protocols; import org.jgroups.*; import org.jgroups.annotations.*; import org.jgroups.blocks.LazyRemovalCache; import org.jgroups.conf.PropertyConverters; import org.jgroups.logging.Log; import org.jgroups.logging.LogFactory; import org.jgroups.stack.Protocol; import org.jgroups.util.*; import org.jgroups.util.ThreadFactory; import org.jgroups.util.UUID; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; 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 #sendMulticast(byte[], int, int)} *
            • {@link #sendUnicast(org.jgroups.PhysicalAddress, 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.
            * The {@link #receive(Address, byte[], int, int)} method must * be called by subclasses when a unicast or multicast message has been received. * @author Bela Ban */ @MBean(description="Transport protocol") @DeprecatedProperty(names={"bind_to_all_interfaces", "use_incoming_packet_handler", "use_outgoing_packet_handler", "use_concurrent_stack", "prevent_port_reuse", "persistent_ports", "pm_expiry_time", "persistent_ports_file", "start_port", "end_port", "use_local_host", "marshaller_pool_size", "num_timer_threads", "timer.num_threads"}) public abstract class TP extends Protocol { protected static final byte LIST=1; // we have a list of messages rather than a single message when set protected static final byte MULTICAST=2; // message is a multicast (versus a unicast) message when set protected static final byte OOB=4; // message has OOB flag set (Message.OOB) protected static final boolean can_bind_to_mcast_addr; // are we running on Linux ? protected static NumberFormat f; static { can_bind_to_mcast_addr=Util.checkForLinux() || Util.checkForSolaris() || Util.checkForHp(); f=NumberFormat.getNumberInstance(); f.setGroupingUsed(false); f.setMaximumFractionDigits(2); } /* ------------------------------------------ JMX and Properties ------------------------------------------ */ @LocalAddress @Property(name="bind_addr", description="The bind address which should be used by this transport. The following special values " + "are also recognized: GLOBAL, SITE_LOCAL, LINK_LOCAL and NON_LOOPBACK", defaultValueIPv4=Global.NON_LOOPBACK_ADDRESS, defaultValueIPv6=Global.NON_LOOPBACK_ADDRESS, systemProperty={Global.BIND_ADDR, Global.BIND_ADDR_OLD},writable=false) protected InetAddress bind_addr=null; @Property(name="bind_interface", converter=PropertyConverters.BindInterface.class, description="The interface (NIC) which should be used by this transport", dependsUpon="bind_addr", exposeAsManagedAttribute=false) protected String bind_interface_str=null; @Property(description="If true, the transport should use all available interfaces to receive multicast messages") protected 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 overrides receive_on_all_interfaces. */ @Property(converter=PropertyConverters.NetworkInterfaceList.class, description="Comma delimited list of interfaces (IP addresses or interface names) to receive multicasts on") protected List receive_interfaces=null; @Property(description="Max number of elements in the logical address cache before eviction starts") protected int logical_addr_cache_max_size=200; @Property(description="Time (in ms) after which entries in the logical address cache marked as removable are removed") protected long logical_addr_cache_expiration=120000; /** The port to which the transport binds. 0 means to bind to any (ephemeral) port */ @Property(description="The port to which the transport binds. Default of 0 binds to any (ephemeral) port",writable=false) protected int bind_port=0; @Property(description="The range of valid ports, from bind_port to end_port. Infinite if 0") protected int port_range=50; // 27-6-2003 bgooren, Only try one port by default /** * 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 */ @Property(description="Messages to self are looped back immediately if true") protected 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 */ @Property(description="Discard packets with a different version if true. Default is false") protected boolean discard_incompatible_packets=false; @Property(description="Thread naming pattern for threads in this channel. Default is cl") protected String thread_naming_pattern="cl"; @Property(name="oob_thread_pool.enabled",description="Switch for enabling thread pool for OOB messages. " + "Default=true",writable=false) protected boolean oob_thread_pool_enabled=true; protected int oob_thread_pool_min_threads=2; protected int oob_thread_pool_max_threads=10; protected long oob_thread_pool_keep_alive_time=30000; @Property(name="oob_thread_pool.queue_enabled", description="Use queue to enqueue incoming OOB messages") protected boolean oob_thread_pool_queue_enabled=true; @Property(name="oob_thread_pool.queue_max_size",description="Maximum queue size for incoming OOB messages. Default is 500") protected int oob_thread_pool_queue_max_size=500; @Property(name="oob_thread_pool.rejection_policy", description="Thread rejection policy. Possible values are Abort, Discard, DiscardOldest and Run. Default is Discard") String oob_thread_pool_rejection_policy="discard"; protected int thread_pool_min_threads=2; protected int thread_pool_max_threads=10; protected long thread_pool_keep_alive_time=30000; @Property(name="thread_pool.enabled",description="Switch for enabling thread pool for regular messages. Default true") protected boolean thread_pool_enabled=true; @Property(name="thread_pool.queue_enabled", description="Use queue to enqueue incoming regular messages. Default is true") protected boolean thread_pool_queue_enabled=true; @Property(name="thread_pool.queue_max_size", description="Maximum queue size for incoming OOB messages. Default is 500") protected int thread_pool_queue_max_size=500; @Property(name="thread_pool.rejection_policy", description="Thread rejection policy. Possible values are Abort, Discard, DiscardOldest and Run. Default is Discard") protected String thread_pool_rejection_policy="Discard"; @Property(description="Type of timer to be used. Valid values are \"old\" (DefaultTimeScheduler, used up to 2.10), " + "\"new\" (TimeScheduler2) and \"wheel\". Note that this property might disappear " + "in future releases, if one of the 3 timers is chosen as default timer") protected String timer_type="new"; protected int timer_min_threads=4; protected int timer_max_threads=10; protected long timer_keep_alive_time=5000; @Property(name="timer.queue_max_size", description="Max number of elements on a timer queue") protected int timer_queue_max_size=500; // hashed timing wheel specific props @Property(name="timer.wheel_size", description="Number of ticks in the HashedTimingWheel timer. Only applicable if timer_type is \"wheel\"") protected int wheel_size=200; @Property(name="timer.tick_time", description="Tick duration in the HashedTimingWheel timer. Only applicable if timer_type is \"wheel\"") protected long tick_time=50L; @Property(description="Enable bundling of smaller messages into bigger ones. Default is true") protected boolean enable_bundling=true; /** Enable bundling for unicast messages. Ignored if enable_bundling is off */ @Property(description="Enable bundling of smaller messages into bigger ones for unicast messages. Default is false") protected boolean enable_unicast_bundling=false; @Property(description="Switch to enable diagnostic probing. Default is true") protected boolean enable_diagnostics=true; @Property(description="Address for diagnostic probing. Default is 224.0.75.75", defaultValueIPv4="224.0.75.75",defaultValueIPv6="ff0e::0:75:75") protected InetAddress diagnostics_addr=null; @Property(description="Port for diagnostic probing. Default is 7500") protected int diagnostics_port=7500; @Property(description="If assigned enable this transport to be a singleton (shared) transport") protected String singleton_name=null; /** Whether or not warnings about messages from different groups are logged - private flag, not for common use */ @Property(description="whether or not warnings about messages from different groups are logged") protected boolean log_discard_msgs=true; /** * 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 */ protected 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) */ protected long max_bundle_timeout=20; @Property(description="The type of bundler used. Has to be \"old\" (default) or \"new\"") protected String bundler_type="new"; @Experimental @Property(description="The max number of elements in a bundler if the bundler supports size limitations") protected int bundler_capacity=20000; @Property(name="max_bundle_size", description="Maximum number of bytes for messages to be queued until they are sent") public void setMaxBundleSize(int size) { if(size <= 0) throw new IllegalArgumentException("max_bundle_size (" + size + ") is <= 0"); max_bundle_size=size; } public long getMaxBundleTimeout() {return max_bundle_timeout;} @Property(name="max_bundle_timeout", description="Max number of milliseconds until queued messages are sent") public void setMaxBundleTimeout(long timeout) { if(timeout <= 0) { throw new IllegalArgumentException("max_bundle_timeout of " + timeout + " is invalid"); } max_bundle_timeout=timeout; } public int getMaxBundleSize() {return max_bundle_size;} @ManagedAttribute public int getBundlerBufferSize() { if(bundler instanceof TransferQueueBundler) return ((TransferQueueBundler)bundler).getBufferSize(); return 0; } @Property(name="oob_thread_pool.keep_alive_time", description="Timeout in ms to remove idle threads from the OOB pool") public void setOOBThreadPoolKeepAliveTime(long time) { oob_thread_pool_keep_alive_time=time; if(oob_thread_pool instanceof ThreadPoolExecutor) ((ThreadPoolExecutor)oob_thread_pool).setKeepAliveTime(time, TimeUnit.MILLISECONDS); } public long getOOBThreadPoolKeepAliveTime() {return oob_thread_pool_keep_alive_time;} @Property(name="oob_thread_pool.min_threads",description="Minimum thread pool size for the OOB thread pool") public void setOOBThreadPoolMinThreads(int size) { oob_thread_pool_min_threads=size; if(oob_thread_pool instanceof ThreadPoolExecutor) ((ThreadPoolExecutor)oob_thread_pool).setCorePoolSize(size); } public int getOOBThreadPoolMinThreads() {return oob_thread_pool_min_threads;} @Property(name="oob_thread_pool.max_threads",description="Max thread pool size for the OOB thread pool") public void setOOBThreadPoolMaxThreads(int size) { oob_thread_pool_max_threads=size; if(oob_thread_pool instanceof ThreadPoolExecutor) ((ThreadPoolExecutor)oob_thread_pool).setMaximumPoolSize(size); } public int getOOBThreadPoolMaxThreads() {return oob_thread_pool_max_threads;} @Property(name="thread_pool.min_threads",description="Minimum thread pool size for the regular thread pool") public void setThreadPoolMinThreads(int size) { thread_pool_min_threads=size; if(thread_pool instanceof ThreadPoolExecutor) ((ThreadPoolExecutor)thread_pool).setCorePoolSize(size); } public int getThreadPoolMinThreads() {return thread_pool_min_threads;} @Property(name="thread_pool.max_threads",description="Maximum thread pool size for the regular thread pool") public void setThreadPoolMaxThreads(int size) { thread_pool_max_threads=size; if(thread_pool instanceof ThreadPoolExecutor) ((ThreadPoolExecutor)thread_pool).setMaximumPoolSize(size); } public int getThreadPoolMaxThreads() {return thread_pool_max_threads;} @Property(name="thread_pool.keep_alive_time",description="Timeout in milliseconds to remove idle thread from regular pool") public void setThreadPoolKeepAliveTime(long time) { thread_pool_keep_alive_time=time; if(thread_pool instanceof ThreadPoolExecutor) ((ThreadPoolExecutor)thread_pool).setKeepAliveTime(time, TimeUnit.MILLISECONDS); } public long getThreadPoolKeepAliveTime() {return thread_pool_keep_alive_time;} @Property(name="timer.min_threads",description="Minimum thread pool size for the timer thread pool") public void setTimerMinThreads(int size) { timer_min_threads=size; if(timer != null) timer.setMinThreads(size); } public int getTimerMinThreads() {return timer_min_threads;} @Property(name="timer.max_threads",description="Max thread pool size for the timer thread pool") public void setTimerMaxThreads(int size) { timer_max_threads=size; if(timer != null) timer.setMaxThreads(size); } public int getTimerMaxThreads() {return timer_max_threads;} @Property(name="timer.keep_alive_time", description="Timeout in ms to remove idle threads from the timer pool") public void setTimerKeepAliveTime(long time) { timer_keep_alive_time=time; if(timer != null) timer.setKeepAliveTime(time); } public long getTimerKeepAliveTime() {return timer_keep_alive_time;} @ManagedAttribute public int getTimerQueueSize() { if(timer instanceof TimeScheduler2) return ((TimeScheduler2)timer).getQueueSize(); return 0; } /* --------------------------------------------- JMX ---------------------------------------------- */ @ManagedAttribute(description="Number of messages sent") protected long num_msgs_sent=0; @ManagedAttribute(description="Number of messages received") protected long num_msgs_received=0; @ManagedAttribute(description="Number of bytes sent") protected long num_bytes_sent=0; @ManagedAttribute(description="Number of bytes received") protected long num_bytes_received=0; /** The name of the group to which this member is connected. With a shared transport, the channel name is * in TP.ProtocolAdapter (cluster_name), and this field is not used */ @ManagedAttribute(description="Channel (cluster) name") protected String channel_name=null; @ManagedAttribute(description="Number of OOB messages received") protected long num_oob_msgs_received=0; @ManagedAttribute(description="Number of regular messages received") protected long num_incoming_msgs_received=0; @ManagedAttribute(description="Class of the timer implementation") public String getTimerClass() { return timer != null? timer.getClass().getSimpleName() : "null"; } /* --------------------------------------------- Fields ------------------------------------------------------ */ /** The address (host and port) of this member. Null by default when a shared transport is used */ protected Address local_addr=null; /** The members of this group (updated when a member joins or leaves). With a shared transport, * members contains *all* members from all channels sitting on the shared transport */ protected final Set
            members=new CopyOnWriteArraySet
            (); protected ThreadGroup pool_thread_group=new ThreadGroup(Util.getGlobalThreadGroup(), "Thread Pools"); /** Keeps track of connects and disconnects, in order to start and stop threads */ protected int connect_count=0; //http://jira.jboss.org/jira/browse/JGRP-849 protected final ReentrantLock connectLock = new ReentrantLock(); // ================================== OOB thread pool ======================== protected Executor oob_thread_pool; /** Factory which is used by oob_thread_pool */ protected ThreadFactory oob_thread_factory=null; /** Used if oob_thread_pool is a ThreadPoolExecutor and oob_thread_pool_queue_enabled is true */ protected BlockingQueue oob_thread_pool_queue=null; // ================================== Regular thread pool ====================== /** The thread pool which handles unmarshalling, version checks and dispatching of regular messages */ protected Executor thread_pool; /** Factory which is used by oob_thread_pool */ protected ThreadFactory default_thread_factory=null; /** Used if thread_pool is a ThreadPoolExecutor and thread_pool_queue_enabled is true */ protected BlockingQueue thread_pool_queue=null; // ================================== Timer thread pool ========================= protected TimeScheduler timer=null; protected ThreadFactory timer_thread_factory; // ================================ Default thread factory ======================== /** Used by all threads created by JGroups outside of the thread pools */ protected ThreadFactory global_thread_factory=null; // ================================= Default SocketFactory ======================== protected SocketFactory socket_factory=new DefaultSocketFactory(); protected Bundler bundler=null; protected DiagnosticsHandler diag_handler=null; protected final List preregistered_probe_handlers=new LinkedList(); /** * 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. */ protected final ConcurrentMap up_prots=Util.createConcurrentMap(16, 0.75f, 16); /** The header including the cluster name, sent with each message. Not used with a shared transport (instead * TP.ProtocolAdapter attaches the header to the message */ protected TpHeader header; /** * Cache which maintains mappings between logical and physical addresses. When sending a message to a logical * address, we look up the physical address from logical_addr_cache and send the message to the physical address *
            * The keys are logical addresses, the values physical addresses */ protected LazyRemovalCache logical_addr_cache=null; // last time we sent a discovery request protected long last_discovery_request=0; Future logical_addr_cache_reaper=null; protected static final LazyRemovalCache.Printable print_function=new LazyRemovalCache.Printable() { public java.lang.String print(final Address logical_addr, final PhysicalAddress physical_addr) { StringBuilder sb=new StringBuilder(); String tmp_logical_name=UUID.get(logical_addr); if(tmp_logical_name != null) sb.append(tmp_logical_name).append(": "); if(logical_addr instanceof UUID) sb.append(((UUID)logical_addr).toStringLong()); else sb.append(logical_addr); sb.append(": ").append(physical_addr).append("\n"); return sb.toString(); } }; /** Cache keeping track of WHO_HAS requests for physical addresses (given a logical address) and expiring * them after 5000ms */ protected AgeOutCache
            who_has_cache=null; /** * Creates the TP protocol, and initializes the state variables, does * however not start any sockets or threads. */ protected TP() { } /** Whether or not hardware multicasting is supported */ public abstract boolean supportsMulticasting(); public boolean isMulticastCapable() {return supportsMulticasting();} public String toString() { if(!isSingleton()) return local_addr != null? name + "(local address: " + local_addr + ')' : name; else return name + " (singleton=" + singleton_name + ")"; } 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 ThreadGroup getPoolThreadGroup() { return pool_thread_group; } public void setThreadPoolQueueEnabled(boolean flag) {thread_pool_queue_enabled=flag;} 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); } 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); } public ThreadFactory getTimerThreadFactory() { return timer_thread_factory; } public void setTimerThreadFactory(ThreadFactory factory) { timer_thread_factory=factory; timer.setThreadFactory(factory); } public TimeScheduler getTimer() {return timer;} public ThreadFactory getThreadFactory() { return global_thread_factory; } public void setThreadFactory(ThreadFactory factory) { global_thread_factory=factory; } public SocketFactory getSocketFactory() { return socket_factory; } public void setSocketFactory(SocketFactory factory) { if(factory != null) socket_factory=factory; } /** * 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" */ public String getThreadNamingPattern() {return thread_naming_pattern;} 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 InetAddress getBindAddressAsInetAddress() {return 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 List getReceiveInterfaces() {return receive_interfaces;} /** @deprecated This property was removed in 2.7*/ public static boolean isSendOnAllInterfaces() {return false;} /** @deprecated This property was removed in 2.7*/ public static List getSendInterfaces() {return null;} 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 isEnableUnicastBundling() {return enable_unicast_bundling;} public void setEnableUnicastBundling(boolean enable_unicast_bundling) {this.enable_unicast_bundling=enable_unicast_bundling;} public void setPortRange(int range) {this.port_range=range;} public int getPortRange() {return port_range ;} /** @deprecated the concurrent stack is used by default */ @Deprecated public void setUseConcurrentStack(boolean flag) {} public boolean isOOBThreadPoolEnabled() { return oob_thread_pool_enabled; } public boolean isDefaulThreadPoolEnabled() { return thread_pool_enabled; } public boolean isLoopback() {return loopback;} public void setLoopback(boolean b) {loopback=b;} /** @deprecated With the concurrent stack being the default, this property is ignored */ public static boolean isUseIncomingPacketHandler() {return false;} public ConcurrentMap getUpProtocols() {return up_prots;} @ManagedAttribute(description="Current number of threads in the OOB thread pool") public int getOOBPoolSize() { return oob_thread_pool instanceof ThreadPoolExecutor? ((ThreadPoolExecutor)oob_thread_pool).getPoolSize() : 0; } public long getOOBMessages() { return num_oob_msgs_received; } @ManagedAttribute(description="Number of messages in the OOB thread pool's queue") public int getOOBQueueSize() { return oob_thread_pool_queue != null? oob_thread_pool_queue.size() : 0; } public int getOOBMaxQueueSize() { return oob_thread_pool_queue_max_size; } public void setOOBRejectionPolicy(String rejection_policy) { RejectedExecutionHandler handler=parseRejectionPolicy(rejection_policy); if(oob_thread_pool instanceof ThreadPoolExecutor) ((ThreadPoolExecutor)oob_thread_pool).setRejectedExecutionHandler(new ShutdownRejectedExecutionHandler(handler)); } @ManagedAttribute(description="Current number of threads in the default thread pool") public int getRegularPoolSize() { return thread_pool instanceof ThreadPoolExecutor? ((ThreadPoolExecutor)thread_pool).getPoolSize() : 0; } public long getRegularMessages() { return num_incoming_msgs_received; } @ManagedAttribute(description="Number of messages in the default thread pool's queue") public int getRegularQueueSize() { return thread_pool_queue != null? thread_pool_queue.size() : 0; } public int getRegularMaxQueueSize() { return thread_pool_queue_max_size; } @ManagedAttribute(name="TimerTasks",description="Number of timer tasks queued up for execution") public int getNumTimerTasks() { return timer != null? timer.size() : -1; } @ManagedOperation public String dumpTimerTasks() { return timer.dumpTimerTasks(); } @ManagedAttribute(description="Number of threads currently in the pool") public int getTimerThreads() { return timer.getCurrentThreads(); } public void setRegularRejectionPolicy(String rejection_policy) { RejectedExecutionHandler handler=parseRejectionPolicy(rejection_policy); if(thread_pool instanceof ThreadPoolExecutor) ((ThreadPoolExecutor)thread_pool).setRejectedExecutionHandler(new ShutdownRejectedExecutionHandler(handler)); } public void setLogDiscardMessages(boolean flag) { log_discard_msgs=flag; } public boolean getLogDiscardMessages() { return log_discard_msgs; } @ManagedOperation(description="Dumps the contents of the logical address cache") public String printLogicalAddressCache() { return logical_addr_cache.printCache(print_function); } @ManagedOperation(description="Evicts elements in the logical address cache which have expired") public void evictLogicalAddressCache() { logical_addr_cache.removeMarkedElements(); fetchLocalAddresses(); } /** * 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 sendMulticast(byte[] data, int offset, int length) throws Exception; /** * Send a unicast to 1 member. Note that the destination address is a *physical*, not a logical address * @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 sendUnicast(PhysicalAddress dest, byte[] data, int offset, int length) throws Exception; public abstract String getInfo(); /* ------------------------------------------------------------------------------- */ /*------------------------------ Protocol interface ------------------------------ */ public void init() throws Exception { super.init(); // Create the default thread factory if(global_thread_factory == null) global_thread_factory=new DefaultThreadFactory(Util.getGlobalThreadGroup(), "", false); // Create the timer and the associated thread factory - depends on singleton_name if(timer_thread_factory == null) timer_thread_factory=new LazyThreadFactory(Util.getGlobalThreadGroup(), "Timer", true, true); if(isSingleton()) timer_thread_factory.setIncludeClusterName(false); if(default_thread_factory == null) default_thread_factory=new DefaultThreadFactory(pool_thread_group, "Incoming", false, true); if(oob_thread_factory == null) oob_thread_factory=new DefaultThreadFactory(pool_thread_group, "OOB", false, true); // local_addr is null when shared transport, channel_name is not used setInAllThreadFactories(channel_name, local_addr, thread_naming_pattern); if(timer_type.equalsIgnoreCase("old")) { if(timer_min_threads < 2) { log.warn("timer.min_threads should not be less than 2 for timer_type=\"old\"; setting value to 2 (from " + timer_min_threads + ")"); timer_min_threads=2; } timer=new DefaultTimeScheduler(timer_thread_factory, timer_min_threads); } else if(timer_type.equalsIgnoreCase("new")) { timer=new TimeScheduler2(timer_thread_factory, timer_min_threads, timer_max_threads, timer_keep_alive_time, timer_queue_max_size); } else if(timer_type.equalsIgnoreCase("wheel")) { timer=new HashedTimingWheel(timer_thread_factory, timer_min_threads, timer_max_threads, timer_keep_alive_time, timer_queue_max_size, wheel_size, tick_time); } else { throw new Exception("timer_type has to be either \"old\", \"new\" or \"wheel\""); } who_has_cache=new AgeOutCache
            (timer, 5000L); verifyRejectionPolicy(oob_thread_pool_rejection_policy); verifyRejectionPolicy(thread_pool_rejection_policy); // ========================================== OOB thread pool ============================== if(oob_thread_pool == null) { 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 == null) { 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(bind_addr != null) { Map m=new HashMap(1); m.put("bind_addr", bind_addr); up(new Event(Event.CONFIG, m)); } logical_addr_cache=new LazyRemovalCache(logical_addr_cache_max_size, logical_addr_cache_expiration); if(logical_addr_cache_reaper == null || logical_addr_cache_reaper.isDone()) { if(logical_addr_cache_expiration <= 0) throw new IllegalArgumentException("logical_addr_cache_expiration has to be > 0"); logical_addr_cache_reaper=timer.scheduleWithFixedDelay(new Runnable() { public void run() { logical_addr_cache.removeMarkedElements(); fetchLocalAddresses(); } public String toString() { return "TP.LogicalAddressCacheReaper (interval=" + logical_addr_cache_expiration + " ms)"; } }, logical_addr_cache_expiration, logical_addr_cache_expiration, TimeUnit.MILLISECONDS); } } public void destroy() { super.destroy(); if(logical_addr_cache_reaper != null) { logical_addr_cache_reaper.cancel(false); logical_addr_cache_reaper=null; } if(timer != null) { timer.stop(); } // 3. Stop the thread pools if(oob_thread_pool instanceof ThreadPoolExecutor) { shutdownThreadPool(oob_thread_pool); } if(thread_pool instanceof ThreadPoolExecutor) { shutdownThreadPool(thread_pool); } } /** * Creates the unicast and multicast sockets and starts the unicast and multicast receiver threads */ public void start() throws Exception { fetchLocalAddresses(); 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(enable_bundling) { if(bundler_type.equals("new")) bundler=new TransferQueueBundler(bundler_capacity); else if(bundler_type.equals("old")) bundler=new DefaultBundler(); else log.warn("bundler_type \"" + bundler_type + "\" not known; using default bundler"); if(bundler == null) bundler=new DefaultBundler(); bundler.start(); } // local_addr is null when shared transport setInAllThreadFactories(channel_name, local_addr, thread_naming_pattern); } public void stop() { if(diag_handler != null) { diag_handler.stop(); diag_handler=null; } preregistered_probe_handlers.clear(); if(bundler != null) bundler.stop(); } protected void handleConnect() throws Exception { connect_count++; } protected void handleDisconnect() { connect_count=Math.max(0, connect_count -1); } public String getSingletonName() { return singleton_name; } public boolean isSingleton(){ return singleton_name != null; } /** * handle the UP event. * @param evt - the event being send from the stack */ public Object up(Event evt) { if(isSingleton()) { 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(this.id, new TpHeader(channel_name)); msg.putHeaderIfAbsent(this.id, header); } if(!isSingleton()) 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 send it up the stack. // 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(); if(dest instanceof PhysicalAddress) { // We can modify the message because it won't get retransmitted. The only time we have a physical address // as dest is when TCPPING sends the initial discovery requests to initial_hosts: this is below UNICAST, // so no retransmission msg.setDest(null); } final boolean multicast=dest == null || dest.isMulticastAddress(); if(loopback && (multicast || (dest.equals(msg.getSrc()) && 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, multicast, 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()) { log.error("failed sending message to " + (dest == null? "cluster" : dest) + " (" + msg.size() + " bytes): " + e + ", cause: " + e.getCause()); } } 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.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. */ protected void setSourceAddress(Message msg) { if(msg.getSrc() == null && local_addr != null) // should already be set by TP.ProtocolAdapter in shared transport case ! msg.setSrc(local_addr); } protected void passMessageUp(Message msg, boolean perform_cluster_name_matching, boolean multicast, boolean discard_own_mcast) { TpHeader hdr=(TpHeader)msg.getHeader(this.id); if(hdr == null) { 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").toString()); return; } if(log.isTraceEnabled()) log.trace(new StringBuilder("received ").append(msg).append(", headers are ").append(msg.printHeaders())); String ch_name=hdr.channel_name; final Protocol tmp_prot=isSingleton()? up_prots.get(ch_name) : up_prot; if(tmp_prot != null) { boolean is_protocol_adapter=tmp_prot instanceof ProtocolAdapter; // Discard if message's cluster name is not the same as our cluster name if(!is_protocol_adapter && perform_cluster_name_matching && channel_name != null && !channel_name.equals(ch_name)) { if(log.isWarnEnabled() && log_discard_msgs) log.warn(new StringBuilder("discarded message from different cluster \"").append(ch_name). append("\" (our cluster is \"").append(channel_name).append("\"). Sender was ").append(msg.getSrc()).toString()); return; } if(loopback && multicast && discard_own_mcast) { Address local=is_protocol_adapter? ((ProtocolAdapter)tmp_prot).getAddress() : local_addr; if(local != null && local.equals(msg.getSrc())) return; } tmp_prot.up(new Event(Event.MSG, msg)); } } /** * Subclasses must call this method when a unicast or multicast message has been received. * Declared final so subclasses cannot override this method. * * @param sender * @param data * @param offset * @param length */ protected void receive(Address sender, byte[] data, int offset, int length) { if(data == null) return; try { // determine whether OOB or not by looking at first byte of 'data' byte oob_flag=data[Global.SHORT_SIZE]; // we need to skip the first 2 bytes (version) if((oob_flag & OOB) == OOB) { num_oob_msgs_received++; dispatchToThreadPool(oob_thread_pool, sender, data, offset, length); } else { num_incoming_msgs_received++; dispatchToThreadPool(thread_pool, sender, data, offset, length); } } catch(Throwable t) { if(log.isErrorEnabled()) log.error(new StringBuilder("failed handling data from ").append(sender).toString(), t); } } protected void dispatchToThreadPool(Executor pool, 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(sender, data, offset, length)); } else { byte[] tmp=new byte[length]; System.arraycopy(data, offset, tmp, 0, length); pool.execute(new IncomingPacket(sender, tmp, 0, length)); } } /** Serializes and sends a message. This method is not reentrant */ protected 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) || msg.isFlagSet(Message.DONT_BUNDLE))) { 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); return; } } // we can create between 300'000 - 400'000 output streams and do the marshalling per second, // so this is not a bottleneck ! ExposedByteArrayOutputStream out_stream=new ExposedByteArrayOutputStream((int)(msg.size() + 50)); ExposedDataOutputStream dos=new ExposedDataOutputStream(out_stream); writeMessage(msg, dos, multicast); Buffer buf=new Buffer(out_stream.getRawBuffer(), 0, out_stream.size()); doSend(buf, dest, multicast); // we don't need to close() or flush() any of the 2 streams above, as these ops are no-ops } protected void doSend(Buffer buf, Address dest, boolean multicast) throws Exception { if(stats) { num_msgs_sent++; num_bytes_sent+=buf.getLength(); } if(multicast) { sendMulticast(buf.getBuf(), buf.getOffset(), buf.getLength()); } else { sendToSingleMember(dest, buf.getBuf(), buf.getOffset(), buf.getLength()); } } protected void sendToSingleMember(Address dest, byte[] buf, int offset, int length) throws Exception { PhysicalAddress physical_dest=dest instanceof PhysicalAddress? (PhysicalAddress)dest : getPhysicalAddressFromCache(dest); if(physical_dest == null) { if(!who_has_cache.contains(dest)) { who_has_cache.add(dest); if(log.isWarnEnabled()) log.warn(local_addr+ ": no physical address for " + dest + ", dropping message"); up(new Event(Event.GET_PHYSICAL_ADDRESS, dest)); } return; } sendUnicast(physical_dest, buf, offset, length); } protected void sendToAllPhysicalAddresses(byte[] buf, int offset, int length) throws Exception { Set dests=new HashSet(logical_addr_cache.nonRemovedValues()); if(!logical_addr_cache.containsKeys(members)) { long current_time=0; synchronized(this) { if(last_discovery_request == 0 || (current_time=System.currentTimeMillis()) - last_discovery_request >= 10000) { last_discovery_request=current_time == 0? System.currentTimeMillis() : current_time; if(log.isWarnEnabled()) log.warn("logical address cache didn't contain all physical address, sending up a discovery request"); up_prot.up(new Event(Event.FIND_INITIAL_MBRS)); } } } for(PhysicalAddress dest: dests) { try { sendUnicast(dest, buf, offset, length); } catch(Throwable t) { if(log.isErrorEnabled()) log.error("failure sending message to " + dest + ": " + t); } } } /** * This method needs to be synchronized on out_stream when it is called * @param msg * @return * @throws java.io.IOException */ protected 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); } protected static Message readMessage(DataInputStream instream) throws Exception { Message msg=new Message(false); // don't create headers, readFrom() will do this msg.readFrom(instream); return msg; } /** * Write a lits of messages with the same destination and *mostly* the same src addresses. The message list is * marshalled as follows: *
                 * List: * | version | flags | dest | src | [Message*] |
                 *
                 * Message:  | presence | leading | flags | [src] | length | [buffer] | size | [Headers*] |
                 *
                 * 
            * @param dest * @param src * @param msgs * @param dos * @param multicast * @throws Exception */ protected static void writeMessageList(Address dest, Address src, List msgs, DataOutputStream dos, boolean multicast) throws Exception { dos.writeShort(Version.version); byte flags=LIST; if(multicast) flags+=MULTICAST; dos.writeByte(flags); Util.writeAddress(dest, dos); Util.writeAddress(src, dos); if(msgs != null) { for(Message msg: msgs) { dos.writeBoolean(true); msg.writeToNoAddrs(src, dos); } } dos.writeBoolean(false); // terminating presence - no more messages will follow } protected static List readMessageList(DataInputStream in) throws Exception { List list=new LinkedList(); Address dest=Util.readAddress(in); Address src=Util.readAddress(in); while(in.readBoolean()) { Message msg=new Message(false); msg.readFrom(in); msg.setDest(dest); if(msg.getSrc() == null) msg.setSrc(src); list.add(msg); } return list; } @SuppressWarnings("unchecked") protected Object handleDownEvent(Event evt) { switch(evt.getType()) { case Event.TMP_VIEW: case Event.VIEW_CHANGE: synchronized(members) { View view=(View)evt.getArg(); members.clear(); if(!isSingleton()) { Vector
            tmpvec=view.getMembers(); members.addAll(tmpvec); } else { // add all members from all clusters for(Protocol prot: up_prots.values()) { if(prot instanceof ProtocolAdapter) { ProtocolAdapter ad=(ProtocolAdapter)prot; Set
            tmp=ad.getMembers(); members.addAll(tmp); } } } // fix for https://jira.jboss.org/jira/browse/JGRP-918 logical_addr_cache.retainAll(members); fetchLocalAddresses(); UUID.retainAll(members); } break; case Event.CONNECT: case Event.CONNECT_WITH_STATE_TRANSFER: case Event.CONNECT_USE_FLUSH: case Event.CONNECT_WITH_STATE_TRANSFER_USE_FLUSH: channel_name=(String)evt.getArg(); header=new TpHeader(channel_name); // local_addr is null when shared transport 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.GET_PHYSICAL_ADDRESS: return getPhysicalAddressFromCache((Address)evt.getArg()); case Event.GET_LOGICAL_PHYSICAL_MAPPINGS: return logical_addr_cache.contents(); case Event.SET_PHYSICAL_ADDRESS: Tuple tuple=(Tuple)evt.getArg(); addPhysicalAddressToCache(tuple.getVal1(), tuple.getVal2()); break; case Event.REMOVE_ADDRESS: removeLogicalAddressFromCache((Address)evt.getArg()); break; case Event.SET_LOCAL_ADDRESS: if(!isSingleton()) local_addr=(Address)evt.getArg(); registerLocalAddress((Address)evt.getArg()); break; } return null; } /** * Associates the address with the physical address fetched from the cache * @param addr * @return true if registered successfully, otherwise false (e.g. physical addr could not be fetched) */ protected void registerLocalAddress(Address addr) { PhysicalAddress physical_addr=getPhysicalAddress(); if(physical_addr != null && addr != null) addPhysicalAddressToCache(addr, physical_addr); } /** * Grabs the local address (or addresses in the shared transport case) and registers them with the physical address * in the transport's cache */ protected void fetchLocalAddresses() { if(!isSingleton()) { if(local_addr != null) { registerLocalAddress(local_addr); } else { Address addr=(Address)up_prot.up(new Event(Event.GET_LOCAL_ADDRESS)); local_addr=addr; registerLocalAddress(addr); } } else { for(Protocol prot: up_prots.values()) { Address addr=(Address)prot.up(new Event(Event.GET_LOCAL_ADDRESS)); registerLocalAddress(addr); } } } protected void setThreadNames() { if(diag_handler != null) global_thread_factory.renameThread(DiagnosticsHandler.THREAD_NAME, diag_handler.getThread()); if(bundler instanceof TransferQueueBundler) { global_thread_factory.renameThread(TransferQueueBundler.THREAD_NAME, ((TransferQueueBundler)bundler).getThread()); } } protected void unsetThreadNames() { if(diag_handler != null && diag_handler.getThread() != null) diag_handler.getThread().setName(DiagnosticsHandler.THREAD_NAME); if(bundler instanceof TransferQueueBundler) { Thread thread=((TransferQueueBundler)bundler).getThread(); if(thread != null) global_thread_factory.renameThread(TransferQueueBundler.THREAD_NAME, thread); } } protected 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=isSingleton(); 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 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); RejectedExecutionHandler handler=parseRejectionPolicy(rejection_policy); pool.setRejectedExecutionHandler(new ShutdownRejectedExecutionHandler(handler)); return pool; } protected 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) { } } } protected 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 static RejectedExecutionHandler parseRejectionPolicy(String rejection_policy) { if(rejection_policy == null) throw new IllegalArgumentException("rejection policy is null"); if(rejection_policy.equalsIgnoreCase("abort")) return new ThreadPoolExecutor.AbortPolicy(); if(rejection_policy.equalsIgnoreCase("discard")) return new ThreadPoolExecutor.DiscardPolicy(); if(rejection_policy.equalsIgnoreCase("discardoldest")) return new ThreadPoolExecutor.DiscardOldestPolicy(); if(rejection_policy.equalsIgnoreCase("run")) return new ThreadPoolExecutor.CallerRunsPolicy(); throw new IllegalArgumentException("rejection policy \"" + rejection_policy + "\" not known"); } 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); } } } protected void addPhysicalAddressToCache(Address logical_addr, PhysicalAddress physical_addr) { if(logical_addr != null && physical_addr != null) logical_addr_cache.add(logical_addr, physical_addr); } protected PhysicalAddress getPhysicalAddressFromCache(Address logical_addr) { return logical_addr != null? logical_addr_cache.get(logical_addr) : null; } protected void removeLogicalAddressFromCache(Address logical_addr) { if(logical_addr != null) { logical_addr_cache.remove(logical_addr); fetchLocalAddresses(); } } /** Clears the cache. Do not use, this is only for unit testing ! */ public void clearLogicalAddressCache() { logical_addr_cache.clear(true); fetchLocalAddresses(); } protected abstract PhysicalAddress getPhysicalAddress(); /* ----------------------------- End of Private Methods ---------------------------------------- */ /* ----------------------------- Inner Classes ---------------------------------------- */ class IncomingPacket implements Runnable { final Address sender; final byte[] buf; final int offset, length; IncomingPacket(Address sender, byte[] buf, int offset, int length) { this.sender=sender; this.buf=buf; this.offset=offset; this.length=length; } /** Code copied from handleIncomingPacket */ public void run() { short version; byte flags; ExposedByteArrayInputStream in_stream; 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.toString()); } if(discard_incompatible_packets) return; } flags=dis.readByte(); boolean is_message_list=(flags & LIST) == LIST; boolean multicast=(flags & MULTICAST) == MULTICAST; if(is_message_list) { // used if message bundling is enabled List msgs=readMessageList(dis); 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); handleMyMessage(msg, multicast); } } catch(Throwable t) { if(log.isErrorEnabled()) log.error("failed handling incoming message", t); } finally { Util.close(dis); } } private void handleMyMessage(Message msg, boolean multicast) { if(stats) { num_msgs_received++; num_bytes_received+=msg.getLength(); } passMessageUp(msg, true, multicast, true); } } protected interface Bundler { void start(); void stop(); void send(Message msg) throws Exception; } private class DefaultBundler implements Bundler { static final int MIN_NUMBER_OF_BUNDLING_TASKS=2; /** 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(); final Log log=LogFactory.getLog(getClass()); public void start() { } public void stop() { } public void send(Message msg) throws Exception { long length=msg.size(); boolean do_schedule=false; checkLength(length); lock.lock(); try { if(count + length >= max_bundle_size) { sendBundledMessages(msgs); } addMessage(msg); count+=length; if(num_bundling_tasks < MIN_NUMBER_OF_BUNDLING_TASKS) { num_bundling_tasks++; do_schedule=true; } } finally { lock.unlock(); } if(do_schedule) timer.schedule(new BundlingTimer(), max_bundle_timeout, TimeUnit.MILLISECONDS); } /** Run with lock acquired */ private void addMessage(Message msg) { Address dst=msg.getDest(); String cluster_name; if(!isSingleton()) cluster_name=TP.this.channel_name; else { TpHeader hdr=(TpHeader)msg.getHeader(id); cluster_name=hdr.channel_name; } SingletonAddress dest=new SingletonAddress(cluster_name, dst); 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++; } /** * 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(final Map> 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(msgs.size()).append(" destination(s)"); if(msgs.size() > 1) sb.append(" (dests=").append(msgs.keySet()).append(")"); log.trace(sb); } ExposedByteArrayOutputStream bundler_out_stream=new ExposedByteArrayOutputStream((int)(count + 50)); ExposedDataOutputStream bundler_dos=new ExposedDataOutputStream(bundler_out_stream); for(Map.Entry> entry: msgs.entrySet()) { List list=entry.getValue(); if(list.isEmpty()) continue; SingletonAddress dst=entry.getKey(); Address dest=dst.getAddress(); Address src_addr=list.get(0).getSrc(); boolean multicast=dest == null || dest.isMulticastAddress(); try { bundler_out_stream.reset(); bundler_dos.reset(); writeMessageList(dest, src_addr, list, bundler_dos, multicast); // flushes output stream when done Buffer buffer=new Buffer(bundler_out_stream.getRawBuffer(), 0, bundler_out_stream.size()); doSend(buffer, dest, multicast); } catch(Throwable e) { if(log.isErrorEnabled()) log.error("exception sending bundled msgs", e); } } msgs.clear(); count=0; } 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() { lock.lock(); try { if(!msgs.isEmpty()) { try { sendBundledMessages(msgs); } catch(Exception e) { log.error("failed sending bundled messages", e); } } } finally { num_bundling_tasks--; lock.unlock(); } } public String toString() { return getClass().getSimpleName(); } } } private class TransferQueueBundler implements Bundler, Runnable { final int threshold; final BlockingQueue buffer; volatile Thread bundler_thread; final Log log=LogFactory.getLog(getClass()); /** Keys are destinations, values are lists of Messages */ final Map> msgs=new HashMap>(36); long count=0; // current number of bytes accumulated int num_msgs=0; long next_bundle_time; volatile boolean running=true; public static final String THREAD_NAME="TransferQueueBundler"; public TransferQueueBundler(int capacity) { if(capacity <=0) throw new IllegalArgumentException("Bundler capacity cannot be " + capacity); buffer=new LinkedBlockingQueue(capacity); threshold=(int)(capacity * .9); // 90% of capacity } public void start() { if(bundler_thread == null || !bundler_thread.isAlive()) { bundler_thread=getThreadFactory().newThread(this, THREAD_NAME); running=true; bundler_thread.start(); } } public Thread getThread() {return bundler_thread;} public void stop() { running=false; if(bundler_thread != null) bundler_thread.interrupt(); } public void send(Message msg) throws Exception { long length=msg.size(); checkLength(length); buffer.put(msg); } public int getBufferSize() { return buffer.size(); } public void run() { next_bundle_time=System.currentTimeMillis() + max_bundle_timeout; while(running) { Message msg=null; long sleep_time=next_bundle_time - System.currentTimeMillis(); try { if(count == 0) msg=buffer.take(); else msg=buffer.poll(sleep_time, TimeUnit.MILLISECONDS); long size=msg != null? msg.size() : 0; boolean send_msgs=(msg != null && count + size >= max_bundle_size) || buffer.size() >= threshold || System.currentTimeMillis() >= next_bundle_time; if(send_msgs) { next_bundle_time=System.currentTimeMillis() + max_bundle_timeout; try { if(!msgs.isEmpty()) { sendBundledMessages(msgs); msgs.clear(); } count=0; } catch(Exception e) { log.error("failed sending bundled messages: " + e.getMessage()); } } if(msg != null) { count+=size; addMessage(msg); } } catch(Throwable t) { } } } 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 void addMessage(Message msg) { Address dst=msg.getDest(); String cluster_name; if(!isSingleton()) cluster_name=TP.this.channel_name; else { TpHeader hdr=(TpHeader)msg.getHeader(id); cluster_name=hdr.channel_name; } SingletonAddress dest=new SingletonAddress(cluster_name, dst); List tmp=msgs.get(dest); if(tmp == null) { tmp=new LinkedList(); msgs.put(dest, tmp); } tmp.add(msg); num_msgs++; } /** * 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(final Map> msgs) { boolean multicast; if(log.isTraceEnabled()) { double percentage=100.0 / max_bundle_size * count; StringBuilder sb=new StringBuilder("sending ").append(num_msgs).append(" msgs ("); sb.append(count).append(" bytes (" + f.format(percentage) + "% of max_bundle_size)"); sb.append(" to ").append(msgs.size()).append(" destination(s)"); if(msgs.size() > 1) sb.append(" (dests=").append(msgs.keySet()).append(")"); log.trace(sb); num_msgs=0; } ExposedByteArrayOutputStream bundler_out_stream=new ExposedByteArrayOutputStream((int)(count + 50)); ExposedDataOutputStream bundler_dos=new ExposedDataOutputStream(bundler_out_stream); for(Map.Entry> entry: msgs.entrySet()) { List list=entry.getValue(); if(list.isEmpty()) continue; SingletonAddress dst=entry.getKey(); Address dest=dst.getAddress(); Address src_addr=list.get(0).getSrc(); multicast=dest == null || dest.isMulticastAddress(); try { bundler_out_stream.reset(); bundler_dos.reset(); writeMessageList(dest, src_addr, list, bundler_dos, multicast); // flushes output stream when done Buffer buf=new Buffer(bundler_out_stream.getRawBuffer(), 0, bundler_out_stream.size()); doSend(buf, dest, multicast); } catch(Throwable e) { if(log.isErrorEnabled()) log.error("exception sending bundled msgs: " + e + ":, cause: " + e.getCause()); } } } } 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(); } // /** // * Maps UUIDs to physical addresses // */ // public interface AddressMapper { // /** // * Given a UUID, pick one physical address from a list. If the UUID is null, the message needs to be sent to // * the entire cluster. In UDP, for example, we would pick a multicast address // * @param uuid The UUID. Null for a cluster wide destination // * @param physical_addrs A list of physical addresses // * @return an address from the list // */ // Address pick(UUID uuid, List
            physical_addrs); // } protected 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 CopyOnWriteArraySet(); 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("uuids")) { retval.put("uuids", printLogicalAddressCache()); if(!isSingleton() && !retval.containsKey("local_addr")) retval.put("local_addr", local_addr != null? local_addr.toString() : null); 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()); } if(key.equals("info")) { if(singleton_name != null && singleton_name.length() > 0) retval.put("singleton_name", singleton_name); } } return retval; } public String[] supportedKeys() { return new String[]{"dump", "keys", "uuids", "info"}; } }); // 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) diag_sock=Util.createMulticastSocket(getSocketFactory(), Global.TP_DIAG_MCAST_SOCK, diagnostics_addr, diagnostics_port, log); else diag_sock=getSocketFactory().createMulticastSocket(Global.TP_DIAG_MCAST_SOCK, diagnostics_port); 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) getSocketFactory().close(diag_sock); 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]; // requests are small (responses might be bigger) 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 socket_ex) { } catch(Throwable e) { if(log.isErrorEnabled()) log.error("failure handling diagnostics request", 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("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(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); } } } } /** * Used when the transport is shared (singleton_name is not null). Maintains the cluster name, local address and * view */ public static class ProtocolAdapter extends Protocol implements ProbeHandler { String cluster_name; final short transport_id; TpHeader header; final Set
            members=new CopyOnWriteArraySet
            (); final ThreadFactory factory; protected SocketFactory socket_factory=new DefaultSocketFactory(); Address local_addr; // TODO [JGRP-1194] - Revisit implementation of TUNNEL and shared transport static final ThreadLocal thread_local=new ThreadLocal(); public ProtocolAdapter(String cluster_name, Address local_addr, short transport_id, Protocol up, Protocol down, String pattern) { this.cluster_name=cluster_name; this.local_addr=local_addr; this.transport_id=transport_id; 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(local_addr != null) factory.setAddress(local_addr.toString()); if(cluster_name != null) factory.setClusterName(cluster_name); } @ManagedAttribute(description="Name of the cluster to which this adapter proxies") public String getClusterName() { return cluster_name; } public Address getAddress() { return local_addr; } @ManagedAttribute(name="Address", description="local address") public String getAddressAsString() { return local_addr != null? local_addr.toString() : null; } @ManagedAttribute(name="AddressUUID", description="local address") public String getAddressAsUUID() { return (local_addr instanceof UUID)? ((UUID)local_addr).toStringLong() : null; } @ManagedAttribute(description="ID of the transport") public short getTransportName() { return transport_id; } public Set
            getMembers() { return Collections.unmodifiableSet(members); } public ThreadFactory getThreadFactory() { return factory; } public SocketFactory getSocketFactory() { return socket_factory; } public void setSocketFactory(SocketFactory factory) { if(factory != null) socket_factory=factory; } public void start() throws Exception { TP tp=getTransport(); if(tp != null) tp.registerProbeHandler(this); } public void stop() { TP tp=getTransport(); if(tp != null) tp.unregisterProbeHandler(this); } public Object down(Event evt) { switch(evt.getType()) { case Event.MSG: Message msg=(Message)evt.getArg(); msg.putHeader(transport_id, header); if(msg.getSrc() == null) msg.setSrc(local_addr); break; case Event.VIEW_CHANGE: View view=(View)evt.getArg(); Vector
            tmp=view.getMembers(); members.clear(); members.addAll(tmp); break; case Event.DISCONNECT: // TODO [JGRP-1194] - Revisit implementation of TUNNEL and shared transport thread_local.set(this); break; case Event.CONNECT: case Event.CONNECT_WITH_STATE_TRANSFER: case Event.CONNECT_USE_FLUSH: case Event.CONNECT_WITH_STATE_TRANSFER_USE_FLUSH: // TODO [JGRP-1194] - Revisit implementation of TUNNEL and shared transport thread_local.set(this); cluster_name=(String)evt.getArg(); factory.setClusterName(cluster_name); this.header=new TpHeader(cluster_name); break; case Event.SET_LOCAL_ADDRESS: Address addr=(Address)evt.getArg(); if(addr != null) { local_addr=addr; factory.setAddress(addr.toString()); // used for thread naming } break; } return down_prot.down(evt); } public String getName() { return "TP.ProtocolAdapter"; } public String toString() { return cluster_name + " (" + transport_id + ")"; } public Map handleProbe(String... keys) { HashMap retval=new HashMap(); retval.put("cluster", cluster_name); retval.put("local_addr", local_addr != null? local_addr.toString() : null); retval.put("local_addr (UUID)", local_addr instanceof UUID? ((UUID)local_addr).toStringLong() : null); retval.put("transport_id", Short.toString(transport_id)); return retval; } public String[] supportedKeys() { return null; } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/TRACE.java0000644000175000017500000000153411647260573025356 0ustar moellermoeller package org.jgroups.protocols; import org.jgroups.Event; import org.jgroups.annotations.Unsupported; import org.jgroups.stack.Protocol; @Unsupported public class TRACE extends Protocol { public 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"; } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/TUNNEL.java0000644000175000017500000004075511647260573025535 0ustar moellermoeller package org.jgroups.protocols; import java.io.DataInputStream; import java.net.DatagramSocket; import java.net.InetSocketAddress; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import org.jgroups.Address; import org.jgroups.Event; import org.jgroups.Global; import org.jgroups.Message; import org.jgroups.PhysicalAddress; import org.jgroups.annotations.Experimental; import org.jgroups.annotations.Property; import org.jgroups.stack.GossipData; import org.jgroups.stack.GossipRouter; import org.jgroups.stack.RouterStubManager; import org.jgroups.stack.IpAddress; import org.jgroups.stack.Protocol; import org.jgroups.stack.RouterStub; import org.jgroups.util.Buffer; import org.jgroups.util.ExposedByteArrayOutputStream; import org.jgroups.util.ExposedDataOutputStream; import org.jgroups.util.Util; /** * 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 * @author Vladimir Blagojevic */ @Experimental public class TUNNEL extends TP { /* * ----------------------------------------- Properties * -------------------------------------------------- */ @Deprecated @Property(name = "router_host", deprecatedMessage = "router_host is deprecated. Specify target GRs using gossip_router_hosts", description = "Router host address") private String router_host = null; @Deprecated @Property(name = "router_port", deprecatedMessage = "router_port is deprecated. Specify target GRs using gossip_router_hosts", description = "Router port") private int router_port = 0; @Property(description = "Interval in msec to attempt connecting back to router in case of torn connection. Default is 5000 msec") private long reconnect_interval = 5000; @Property(description="Should TCP no delay flag be turned on") boolean tcp_nodelay=false; /* * --------------------------------------------- Fields * ------------------------------------------------------ */ private final List gossip_router_hosts = new ArrayList(); private TUNNELPolicy tunnel_policy = new DefaultTUNNELPolicy(); private DatagramSocket sock; private volatile RouterStubManager stubManager; public TUNNEL() { } public boolean supportsMulticasting() { return false; } @Property public void setGossipRouterHosts(String hosts) throws UnknownHostException { gossip_router_hosts.clear(); // if we get passed value of List#toString() we have to strip [] if (hosts.startsWith("[") && hosts.endsWith("]")) { hosts = hosts.substring(1, hosts.length() - 1); } gossip_router_hosts.addAll(Util.parseCommaDelimitedHosts2(hosts, 1)); } public String toString() { return "TUNNEL"; } @Deprecated public String getRouterHost() { return router_host; } @Deprecated public void setRouterHost(String router_host) { this.router_host = router_host; } @Deprecated public int getRouterPort() { return router_port; } @Deprecated 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 synchronized void setTUNNELPolicy(TUNNELPolicy policy) { if (policy == null) throw new IllegalArgumentException("Tunnel policy has to be non null"); tunnel_policy = policy; } public void init() throws Exception { super.init(); if(enable_bundling) { log.warn("bundling is currently not supported by TUNNEL; bundling is disabled"); enable_bundling=false; } if (timer == null) throw new Exception("timer cannot be retrieved from protocol stack"); // Postpone TUNNEL and shared transport until 3.0 timeframe // TODO [JGRP-1194] - Revisit implementation of TUNNEL and shared transport if (isSingleton()) { throw new Exception("TUNNEL and shared transport mode are not supported!"); } if ((router_host == null || router_port == 0) && gossip_router_hosts.isEmpty()) { throw new Exception("either router_host and router_port have to be set or a list of gossip routers"); } if (router_host != null && router_port != 0 && !gossip_router_hosts.isEmpty()) { throw new Exception("cannot specify both router host and port along with gossip_router_hosts"); } if (router_host != null && router_port != 0 && gossip_router_hosts.isEmpty()) { gossip_router_hosts.add(new InetSocketAddress(router_host, router_port)); } if (log.isDebugEnabled()) { log.debug("GossipRouters are:" + gossip_router_hosts.toString()); } stubManager = RouterStubManager.emptyGossipClientStubManager(this); sock = getSocketFactory().createDatagramSocket(Global.TUNNEL_UCAST_SOCK, bind_port, bind_addr); // loopback turned on is mandatory loopback = true; } public void destroy() { stubManager.destroyStubs(); super.destroy(); } private void disconnectStub(String group, Address addr) { stubManager.disconnectStubs(); } public Object handleDownEvent(Event evt) { Object retEvent = super.handleDownEvent(evt); switch (evt.getType()) { case Event.CONNECT: case Event.CONNECT_WITH_STATE_TRANSFER: case Event.CONNECT_USE_FLUSH: case Event.CONNECT_WITH_STATE_TRANSFER_USE_FLUSH: String group=(String)evt.getArg(); Address local= null; if(!isSingleton()) { local = local_addr; } else { // TODO [JGRP-1194] - Revisit implementation of TUNNEL and shared transport ProtocolAdapter adapter = ProtocolAdapter.thread_local.get(); local = adapter.local_addr; } if(stubManager != null){ stubManager.destroyStubs(); } stubManager = new TUNNELStubManager(this,group,local,getReconnectInterval()); for (InetSocketAddress gr : gossip_router_hosts) { RouterStub stub = stubManager.createAndRegisterStub(gr.getHostName(), gr.getPort(), bind_addr); stub.setTcpNoDelay(tcp_nodelay); } PhysicalAddress physical_addr=(PhysicalAddress)down(new Event(Event.GET_PHYSICAL_ADDRESS, local)); List physical_addrs=Arrays.asList(physical_addr); String logical_name=org.jgroups.util.UUID.get(local); List stubs = stubManager.getStubs(); tunnel_policy.connect(stubs, group, local, logical_name, physical_addrs); break; case Event.DISCONNECT: if(!isSingleton()) { local = local_addr; group = channel_name; } else { // TODO [JGRP-1194] - Revisit implementation of TUNNEL and shared transport ProtocolAdapter adapter = ProtocolAdapter.thread_local.get(); local = adapter.local_addr; group = adapter.cluster_name; } disconnectStub(group,local); break; } return retEvent; } private class TUNNELStubManager extends RouterStubManager { TUNNELStubManager(Protocol owner, String channelName, Address logicalAddress, long interval) { super(owner, channelName, logicalAddress, interval); } public void connectionStatusChange(RouterStub stub, RouterStub.ConnectionStatus newState) { super.connectionStatusChange(stub, newState); if (newState == RouterStub.ConnectionStatus.CONNECTED) { StubReceiver stubReceiver = new StubReceiver(stub); stub.setReceiver(stubReceiver); Thread t = global_thread_factory.newThread(stubReceiver, "TUNNEL receiver for " + stub.toString()); stubReceiver.setThread(t); t.setDaemon(true); t.start(); } } } public class StubReceiver implements Runnable { private Thread runner; private final RouterStub stub; public StubReceiver(RouterStub stub) { this.stub = stub; } public synchronized void setThread(Thread t) { runner = t; } public synchronized Thread getThread() { return runner; } public void run() { final DataInputStream input = stub.getInputStream(); mainloop: while (!Thread.currentThread().isInterrupted()) { try { GossipData msg = new GossipData(); msg.readFrom(input); switch (msg.getType()) { case GossipRouter.DISCONNECT_OK: break mainloop; case GossipRouter.MESSAGE: byte[] data = msg.getBuffer(); receive(null/* src will be read from data */, data, 0, data.length); break; case GossipRouter.SUSPECT: final Address suspect = Util.readAddress(input); log.debug("Firing suspect event " + suspect + " at " + local_addr); if(suspect != null) { // https://jira.jboss.org/jira/browse/JGRP-902 Thread thread = getThreadFactory().newThread(new Runnable() { public void run() { fireSuspectEvent(suspect); } }, "StubReceiver-suspect"); thread.start(); } break; } }catch (Exception ioe) { if(stub.isConnected()) continue mainloop; else break; } } } private void fireSuspectEvent(Address suspect) { up(new Event(Event.SUSPECT, suspect)); } } protected void send(Message msg, Address dest, boolean multicast) throws Exception { // we don't currently support message bundling in TUNNEL TpHeader hdr=(TpHeader)msg.getHeader(this.id); if(hdr == null) throw new Exception("message " + msg + " doesn't have a transport header, cannot route it"); String group=hdr.channel_name; ExposedByteArrayOutputStream out_stream=new ExposedByteArrayOutputStream((int)(msg.size() + 50)); ExposedDataOutputStream dos=new ExposedDataOutputStream(out_stream); writeMessage(msg, dos, multicast); Buffer buf=new Buffer(out_stream.getRawBuffer(), 0, out_stream.size()); if(stats) { num_msgs_sent++; num_bytes_sent+=buf.getLength(); } List stubs = stubManager.getStubs(); if(multicast) { tunnel_policy.sendToAllMembers(stubs, group, buf.getBuf(), buf.getOffset(), buf.getLength()); } else { tunnel_policy.sendToSingleMember(stubs, group, dest, buf.getBuf(), buf.getOffset(), buf.getLength()); } } public void sendMulticast(byte[] data, int offset, int length) throws Exception { throw new UnsupportedOperationException("sendMulticast() should not get called on TUNNEL"); } public void sendUnicast(PhysicalAddress dest, byte[] data, int offset, int length) throws Exception { throw new UnsupportedOperationException("sendUnicast() should not get called on TUNNEL"); } public String getInfo() { List stubs = stubManager.getStubs(); if (stubs.isEmpty()) return stubs.toString(); else return "RouterStubs not yet initialized"; } protected PhysicalAddress getPhysicalAddress() { return sock != null ? new IpAddress(bind_addr, sock.getLocalPort()) : null; } public interface TUNNELPolicy { public void connect(List stubs, String group, Address addr, String logical_name, List phys_addrs); public void sendToAllMembers(List stubs, String group, byte[] data, int offset, int length) throws Exception; public void sendToSingleMember(List stubs, String group, Address dest, byte[] data, int offset, int length) throws Exception; } private class DefaultTUNNELPolicy implements TUNNELPolicy { public void sendToAllMembers(List stubs, String group, byte[] data, int offset, int length) throws Exception { boolean sent = false; if(stubs.size() > 1) Collections.shuffle(stubs); // todo: why is this needed ? for (RouterStub stub : stubs) { try { if(!stub.isConnected()) continue; stub.sendToAllMembers(group, data, offset, length); if (log.isTraceEnabled()) log.trace("sent a message to all members, GR used " + stub.getGossipRouterAddress()); sent = true; break; } catch (Exception e) { if (log.isWarnEnabled()) log.warn("failed sending a message to all members, GR used " + stub.getGossipRouterAddress()); } } if (!sent) throw new Exception("None of the available stubs " + stubs + " accepted a multicast message"); } public void sendToSingleMember(List stubs, String group, Address dest, byte[] data, int offset, int length) throws Exception { boolean sent = false; if(stubs.size() > 1) Collections.shuffle(stubs); for (RouterStub stub : stubs) { try { if(!stub.isConnected()) continue; stub.sendToMember(group, dest, data, offset, length); if (log.isDebugEnabled()) log.debug("sent a message to " + dest + ", GR used " + stub.getGossipRouterAddress()); sent = true; break; } catch (Exception e) { if (log.isWarnEnabled()) { log.warn("failed sending a message to " + dest + ", GR used " + stub.getGossipRouterAddress()); } } } if (!sent) throw new Exception("None of the available stubs " + stubs + " accepted a message for dest " + dest); } public void connect(List stubs, String group, Address addr, String logical_name, List phys_addrs) { for (RouterStub stub : stubs) { try { stub.connect(group, addr, logical_name, phys_addrs); } catch (Exception e) { if (log.isWarnEnabled()) log.warn("Failed connecting to GossipRouter at " + stub.getGossipRouterAddress()); stubManager.startReconnecting(stub); } } } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/TpHeader.java0000644000175000017500000000171211647260573026212 0ustar moellermoellerpackage org.jgroups.protocols; import org.jgroups.Header; import java.io.*; /** * Generic transport header, used by TP. * @author Bela Ban */ public class TpHeader extends Header { 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 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() } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/TransportedVectorTime.java0000644000175000017500000000746611647260573031041 0ustar moellermoeller 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.8 $ */ 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(); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/UDP.java0000644000175000017500000006575011647260573025162 0ustar moellermoellerpackage org.jgroups.protocols; import org.jgroups.Global; import org.jgroups.PhysicalAddress; import org.jgroups.annotations.DeprecatedProperty; import org.jgroups.annotations.Property; import org.jgroups.stack.IpAddress; import org.jgroups.util.Util; import java.io.IOException; import java.net.*; import java.util.List; import java.util.Map; /** * 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 */ @DeprecatedProperty(names={"num_last_ports","null_src_addresses", "send_on_all_interfaces", "send_interfaces"}) public class UDP extends TP { /* ------------------------------------------ Properties ------------------------------------------ */ /** * 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
            • *
            */ @Property(description="Traffic class for sending unicast and multicast datagrams. Default is 8") protected int tos=8; // valid values: 2, 4, 8 (default), 16 @Property(name="mcast_addr", description="The multicast address used for sending and receiving packets. Default is 228.8.8.8", defaultValueIPv4="228.8.8.8", defaultValueIPv6="ff0e::8:8:8", systemProperty=Global.UDP_MCAST_ADDR,writable=false) protected InetAddress mcast_group_addr=null; @Property(description="The multicast port used for sending and receiving packets. Default is 7600", systemProperty=Global.UDP_MCAST_PORT, writable=false) protected int mcast_port=7600; @Property(description="Multicast toggle. If false multiple unicast datagrams are sent instead of one multicast. " + "Default is true", writable=false) protected boolean ip_mcast=true; @Property(description="The time-to-live (TTL) for multicast datagram packets. Default is 8",systemProperty=Global.UDP_IP_TTL) protected int ip_ttl=8; @Property(description="Send buffer size of the multicast datagram socket. Default is 100'000 bytes") protected int mcast_send_buf_size=100000; @Property(description="Receive buffer size of the multicast datagram socket. Default is 500'000 bytes") protected int mcast_recv_buf_size=500000; @Property(description="Send buffer size of the unicast datagram socket. Default is 100'000 bytes") protected int ucast_send_buf_size=100000; @Property(description="Receive buffer size of the unicast datagram socket. Default is 64'000 bytes") protected int ucast_recv_buf_size=64000; @Property protected boolean disable_loopback=false; /* --------------------------------------------- Fields ------------------------------------------------ */ /** The multicast address (mcast address and port) this member uses */ protected IpAddress mcast_addr=null; /** * Socket used for *
              *
            1. sending unicast and multicast packets and *
            2. receiving unicast packets *
            * The address of this socket will be our local address (local_addr) */ protected DatagramSocket sock=null; /** IP multicast socket for receiving multicast packets */ protected MulticastSocket mcast_sock=null; /** Runnable to receive multicast packets */ protected PacketReceiver mcast_receiver=null; /** Runnable to receive unicast packets */ protected PacketReceiver ucast_receiver=null; /** * 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() { } public boolean supportsMulticasting() { return ip_mcast; } public void setMulticastAddress(InetAddress addr) {this.mcast_group_addr=addr;} public InetAddress getMulticastAddress() {return mcast_group_addr;} public int getMulticastPort() {return mcast_port;} public void setMulticastPort(int mcast_port) {this.mcast_port=mcast_port;} public void setMcastPort(int mcast_port) {this.mcast_port=mcast_port;} /** * Set the ttl for multicast socket * @param ttl the time to live for the socket. * @throws IOException */ public void setMulticastTTL(int ttl) throws IOException { this.ip_ttl=ttl; mcast_sock.setTimeToLive((byte)ttl); } /** * Getter for current multicast TTL * @return */ public int getMulticastTTL() { return this.ip_ttl; } @Property(name="max_bundle_size", description="Maximum number of bytes for messages to be queued until they are sent") public void setMaxBundleSize(int size) { super.setMaxBundleSize(size); if(size > Global.MAX_DATAGRAM_PACKET_SIZE) throw new IllegalArgumentException("max_bundle_size (" + size + ") cannot exceed the max datagram " + "packet size of " + Global.MAX_DATAGRAM_PACKET_SIZE); } public String getInfo() { StringBuilder sb=new StringBuilder(); sb.append("group_addr=").append(mcast_group_addr.getHostName()).append(':').append(mcast_port).append("\n"); return sb.toString(); } public void sendMulticast(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 { sendToAllPhysicalAddresses(data, offset, length); } } public void sendUnicast(PhysicalAddress dest, byte[] data, int offset, int length) throws Exception { _send(((IpAddress)dest).getIpAddress(), ((IpAddress)dest).getPort(), false, data, offset, length); } protected 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_sock != null && !mcast_sock.isClosed()) { try { mcast_sock.send(packet); } // solve reconnection issue with Windows (https://jira.jboss.org/browse/JGRP-1254) catch(NoRouteToHostException e) { log.warn(e.getMessage() +", reset interface"); mcast_sock.setInterface(mcast_sock.getInterface()); } } } else { if(sock != null && !sock.isClosed()) sock.send(packet); } } catch(Exception ex) { throw new Exception("dest=" + dest + ":" + port + " (" + length + " bytes)", ex); } } /* ------------------------------------------------------------------------------- */ /*------------------------------ Protocol interface ------------------------------ */ /** * 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"); 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(); ucast_receiver=new PacketReceiver(sock, "unicast receiver", new Runnable() { public void run() { closeUnicastSocket(); } }); if(ip_mcast) mcast_receiver=new PacketReceiver(mcast_sock, "multicast receiver", new Runnable() { public void run() { closeMulticastSocket(); } }); } public void stop() { if(log.isDebugEnabled()) log.debug("closing sockets and stopping threads"); stopThreads(); // will close sockets, closeSockets() is not really needed anymore, but... super.stop(); } public void destroy() { super.destroy(); destroySockets(); } protected void handleConnect() throws Exception { if(isSingleton()) { if(connect_count == 0) { startThreads(); } super.handleConnect(); } else startThreads(); } protected void handleDisconnect() { if(isSingleton()) { 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). */ protected void createSockets() throws Exception { // 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]; // } // RA 14 Sep 09: These lines were never called as bind_addr always returned a non-null value // if(bind_addr == null && !use_local_host) { // bind_addr=Util.getFirstNonLoopbackAddress(); // } // if(bind_addr == null) // bind_addr=InetAddress.getLocalHost(); if(bind_addr == null) throw new IllegalArgumentException("bind_addr cannot be null") ; // RA: 16 Sep 09: need to resolve the use of use_local_host // if(bind_addr != null && !bind_addr.isLoopbackAddress() && use_local_host) { // throw new IllegalArgumentException("must use use localhost as a bind address") ; // } 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: " + e); } } if(sock == null) throw new Exception("socket is null"); // 3. Create socket for receiving IP multicast packets if(ip_mcast) { // 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(getSocketFactory(), Global.UDP_MCAST_SOCK, mcast_group_addr, mcast_port, log); else mcast_sock=getSocketFactory().createMulticastSocket(Global.UDP_MCAST_SOCK, mcast_port); if(disable_loopback) mcast_sock.setLoopbackMode(disable_loopback); mcast_sock.setTimeToLive(ip_ttl); mcast_addr=new IpAddress(mcast_group_addr, mcast_port); // check that we're not using the same mcast address and port as the diagnostics socket if(enable_diagnostics) { if(diagnostics_addr != null && diagnostics_addr.equals(mcast_group_addr) || diagnostics_port == mcast_port) throw new IllegalArgumentException("diagnostics_addr / diagnostics_port and mcast_addr / mcast_port " + "have to be different"); } 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(mcast_group_addr); } } setBufferSizes(); if(log.isDebugEnabled()) log.debug("socket information:\n" + dumpSocketInfo()); } protected void destroySockets() { closeMulticastSocket(); closeUnicastSocket(); } protected IpAddress createLocalAddress() { return sock != null && !sock.isClosed()? new IpAddress(sock.getLocalAddress(), sock.getLocalPort()) : null; } protected PhysicalAddress getPhysicalAddress() { return createLocalAddress(); } /** * * @param interfaces List. Guaranteed to have no duplicates * @param s * @param mcastAddr * @throws IOException */ protected void bindToInterfaces(List interfaces, MulticastSocket s, InetAddress mcastAddr) { SocketAddress tmp_mcast_addr=new InetSocketAddress(mcastAddr, mcast_port); for(NetworkInterface intf:interfaces) { //[ JGRP-680] - receive_on_all_interfaces requires every NIC to be configured try { s.joinGroup(tmp_mcast_addr, intf); if(log.isTraceEnabled()) log.trace("joined " + tmp_mcast_addr + " on " + intf.getName()); } catch(IOException e) { if(log.isWarnEnabled()) log.warn("Could not join " + tmp_mcast_addr + " on interface " + 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=getSocketFactory().createDatagramSocket(Global.UDP_UCAST_SOCK, localPort, bind_addr); } 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(); 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; while(rcv_port <= max_port) { try { tmp=getSocketFactory().createDatagramSocket(Global.UDP_UCAST_SOCK, 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; } protected String dumpSocketInfo() throws Exception { StringBuilder sb=new StringBuilder(128); 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()); } 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); } protected void setBufferSize(DatagramSocket sock, int send_buf_size, int recv_buf_size) { try { sock.setSendBufferSize(send_buf_size); int actual_size=sock.getSendBufferSize(); if(actual_size < send_buf_size && log.isWarnEnabled()) { log.warn("send buffer of socket " + sock + " was set to " + Util.printBytes(send_buf_size) + ", but the OS only allocated " + Util.printBytes(actual_size) + ". This might lead to performance problems. Please set your " + "max send buffer in the OS correctly (e.g. net.core.wmem_max on Linux)"); } } 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); int actual_size=sock.getReceiveBufferSize(); if(actual_size < recv_buf_size && log.isWarnEnabled()) { log.warn("receive buffer of socket " + sock + " was set to " + Util.printBytes(recv_buf_size) + ", but the OS only allocated " + Util.printBytes(actual_size) + ". This might lead to performance problems. Please set your " + "max receive buffer in the OS correctly (e.g. net.core.rmem_max on Linux)"); } } catch(Throwable ex) { if(log.isWarnEnabled()) log.warn("failed setting receive buffer size of " + recv_buf_size + " in " + sock + ": " + ex); } } void closeMulticastSocket() { if(mcast_sock != null) { try { if(mcast_addr != null) { mcast_sock.leaveGroup(mcast_addr.getIpAddress()); } getSocketFactory().close(mcast_sock); // 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; } } protected void closeUnicastSocket() { getSocketFactory().close(sock); } /** * Starts the unicast and multicast receiver threads */ void startThreads() throws Exception { ucast_receiver.start(); if(mcast_receiver != null) mcast_receiver.start(); } /** * Stops unicast and multicast receiver threads */ void stopThreads() { if(mcast_receiver != null) mcast_receiver.stop(); ucast_receiver.stop(); } protected void handleConfigEvent(Map map) { boolean set_buffers=false; 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 PacketReceiver implements Runnable { private Thread thread=null; private final DatagramSocket receiver_socket; private final String name; private final Runnable close_strategy; public PacketReceiver(DatagramSocket socket, String name, Runnable close_strategy) { this.receiver_socket=socket; this.name=name; this.close_strategy=close_strategy; } public synchronized void start() { if(thread == null || !thread.isAlive()) { thread=getThreadFactory().newThread(this, name); thread.start(); if(log.isDebugEnabled()) log.debug("created " + name + " thread "); } } public synchronized void stop() { try { close_strategy.run(); } catch(Exception e1) { } finally { Util.close(receiver_socket); // second line of defense } if(thread != null && thread.isAlive()) { Thread tmp=thread; thread=null; tmp.interrupt(); try { tmp.join(Global.THREAD_SHUTDOWN_WAIT_TIME); } catch(InterruptedException e) { Thread.currentThread().interrupt(); // set interrupt flag again } } thread=null; } public void run() { final byte receive_buf[]=new byte[66000]; // to be on the safe side (IPv6 == 65575 bytes, IPv4 = 65535) final DatagramPacket packet=new DatagramPacket(receive_buf, receive_buf.length); while(thread != null && Thread.currentThread().equals(thread)) { try { receiver_socket.receive(packet); int len=packet.getLength(); 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 FRAG2 protocol and make its frag_size lower than " + receive_buf.length); } receive(new IpAddress(packet.getAddress(), packet.getPort()), receive_buf, packet.getOffset(), len); } catch(SocketException sock_ex) { if(log.isDebugEnabled()) log.debug("receiver socket is closed, exception=" + sock_ex); break; } catch(Throwable ex) { if(log.isErrorEnabled()) log.error("failed receiving packet", ex); } } if(log.isDebugEnabled()) log.debug(name + " thread terminated"); } public String toString() { return receiver_socket != null? receiver_socket.getLocalSocketAddress().toString() : "null"; } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/UFC.java0000644000175000017500000001252211647260573025134 0ustar moellermoellerpackage org.jgroups.protocols; import org.jgroups.Address; import org.jgroups.Event; import org.jgroups.Message; import org.jgroups.annotations.MBean; import org.jgroups.annotations.ManagedAttribute; import org.jgroups.annotations.ManagedOperation; import org.jgroups.util.Util; import java.util.Iterator; import java.util.Map; import java.util.Vector; /** * 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 the actual number of bytes received *
                * @author Bela Ban */ @MBean(description="Simple flow control protocol based on a credit system") public class UFC extends FlowControl { /** * Map: keys are members, values are credits left. For each send, * the number of credits is decremented by the message size */ protected final Map sent=Util.createConcurrentMap(); @ManagedOperation(description="Print sender credits") public String printSenderCredits() { return printMap(sent); } @ManagedOperation(description="Print credits") public String printCredits() { StringBuilder sb=new StringBuilder(super.printCredits()); sb.append("\nsenders:\n").append(printMap(sent)); return sb.toString(); } public Map dumpStats() { Map retval=super.dumpStats(); retval.put("senders", printMap(sent)); return retval; } protected boolean handleMulticastMessage() { return false; } public void unblock() { super.unblock(); } @ManagedAttribute(description="Number of times flow control blocks sender") public int getNumberOfBlockings() { int retval=0; for(Credit cred: sent.values()) retval+=cred.getNumBlockings(); return retval; } @ManagedAttribute(description="Total time (ms) spent in flow control block") public long getTotalTimeBlocked() { long retval=0; for(Credit cred: sent.values()) retval+=cred.getTotalBlockingTime(); return retval; } public void stop() { super.stop(); for(Credit cred: sent.values()) cred.set(max_credits); } protected Object handleDownMessage(final Event evt, final Message msg, Address dest, int length) { if(dest == null || dest.isMulticastAddress()) { // 2nd line of defense, not really needed log.error(getClass().getSimpleName() + " doesn't handle multicast messages; passing message down"); return down_prot.down(evt); } Credit cred=sent.get(dest); if(cred == null) return down_prot.down(evt); long block_time=max_block_times != null? getMaxBlockTime(length) : max_block_time; while(running && sent.containsKey(dest)) { boolean rc=cred.decrementIfEnoughCredits(length, block_time); if(rc || !running || max_block_times != null) break; if(cred.needToSendCreditRequest()) sendCreditRequest(dest, Math.max(0, max_credits - cred.get())); } // send message - either after regular processing, or after blocking (when enough credits available again) return down_prot.down(evt); } protected void handleViewChange(Vector
                mbrs) { super.handleViewChange(mbrs); if(mbrs == null) return; // add members not in membership to received and sent hashmap (with full credits) for(Address addr: mbrs) { if(!sent.containsKey(addr)) sent.put(addr, new Credit(max_credits)); } // remove members that left for(Iterator
                it=sent.keySet().iterator(); it.hasNext();) { Address addr=it.next(); if(!mbrs.contains(addr)) it.remove(); // modified the underlying map } } protected void handleCredit(Address sender, long increase) { Credit cred; if(sender == null || (cred=sent.get(sender)) == null || increase <= 0) return; long new_credit=Math.min(max_credits, cred.get() + increase); if(log.isTraceEnabled()) { StringBuilder sb=new StringBuilder(); sb.append("received " + increase + " credits from ").append(sender).append(", old credits: ").append(cred) .append(", new credits: ").append(new_credit); log.trace(sb); } cred.increment(increase); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/UNICAST.java0000644000175000017500000010461211647260573025627 0ustar moellermoellerpackage org.jgroups.protocols; import org.jgroups.*; import org.jgroups.annotations.*; import org.jgroups.conf.PropertyConverters; 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.TimeScheduler; import org.jgroups.util.Tuple; import org.jgroups.util.Util; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.util.*; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * 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 */ @MBean(description="Reliable unicast layer") @DeprecatedProperty(names={"immediate_ack", "use_gms", "enabled_mbrs_timeout", "eager_lock_release"}) public class UNICAST extends Protocol implements AckSenderWindow.RetransmitCommand, AgeOutCache.Handler

                { public static final long DEFAULT_FIRST_SEQNO=Global.DEFAULT_FIRST_UNICAST_SEQNO; /* ------------------------------------------ Properties ------------------------------------------ */ @Property(description="Whether to loop back messages sent to self. Default is false", deprecatedMessage="might get removed soon as it can destroy ordering. " + "See https://jira.jboss.org/jira/browse/JGRP-1092 for details") @Deprecated private boolean loopback=false; private long[] timeout= { 400, 800, 1600, 3200 }; // for AckSenderWindow: max time to wait for missing acks @Property(description="Max number of messages to be removed from the AckReceiverWindow. This property might " + "get removed anytime, so don't use it !") private int max_msg_batch_size=50000; /* --------------------------------------------- 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 ConcurrentMap send_table=Util.createConcurrentMap(); private final ConcurrentMap recv_table=Util.createConcurrentMap(); protected final ReentrantLock recv_table_lock=new ReentrantLock(); private final Vector
                members=new Vector
                (11); 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; private short last_conn_id=0; protected long max_retransmit_time=60 * 1000L; private AgeOutCache
                cache=null; @Deprecated @ManagedAttribute public static int getUndeliveredMessages() { return 0; } public long[] getTimeout() {return timeout;} @Property(name="timeout",converter=PropertyConverters.LongArray.class) public void setTimeout(long[] val) { if(val != null) timeout=val; } public void setMaxMessageBatchSize(int size) { if(size >= 1) max_msg_batch_size=size; } @ManagedAttribute public String getLocalAddress() {return local_addr != null? local_addr.toString() : "null";} @ManagedAttribute public String getMembers() {return members != null? members.toString() : "[]";} @ManagedOperation public String printConnections() { StringBuilder sb=new StringBuilder(); if(!send_table.isEmpty()) { sb.append("\nsend connections:\n"); for(Map.Entry entry: send_table.entrySet()) { sb.append(entry.getKey()).append(": ").append(entry.getValue()).append("\n"); } } if(!recv_table.isEmpty()) { sb.append("\nreceive connections:\n"); for(Map.Entry entry: recv_table.entrySet()) { sb.append(entry.getKey()).append(": ").append(entry.getValue()).append("\n"); } } return sb.toString(); } @ManagedAttribute public long getNumMessagesSent() { return num_msgs_sent; } @ManagedAttribute public long getNumMessagesReceived() { return num_msgs_received; } @ManagedAttribute public long getNumBytesSent() { return num_bytes_sent; } @ManagedAttribute public long getNumBytesReceived() { return num_bytes_received; } @ManagedAttribute public long getNumAcksSent() { return num_acks_sent; } @ManagedAttribute public long getNumAcksReceived() { return num_acks_received; } @ManagedAttribute public long getNumberOfRetransmissions() { return num_xmits; } public long getMaxRetransmitTime() { return max_retransmit_time; } @Property(description="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 this") 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); } @ManagedAttribute public int getAgeOutCacheSize() { return cache != null? cache.size() : 0; } @ManagedOperation 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) */ @ManagedAttribute public int getNumberOfUnackedMessages() { int num=0; for(SenderEntry entry: send_table.values()) { if(entry.sent_msgs != null) num+=entry.sent_msgs.size(); } return num; } @ManagedOperation 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(); } @ManagedAttribute 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 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(); } 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() || msg.isFlagSet(Message.NO_RELIABILITY)) // 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(this.id); 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, evt); 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; } 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() || msg.isFlagSet(Message.NO_RELIABILITY)) break; if(!started) { if(log.isTraceEnabled()) log.trace("discarded message as start() has not yet been called, message: " + msg); 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; short 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=UnicastHeader.createDataHeader(seqno, send_conn_id, seqno == DEFAULT_FIRST_SEQNO); msg.putHeader(this.id, 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.addToMessages(seqno, msg); // add *including* UnicastHeader, adds to retransmitter entry.sent_msgs_seqno++; } finally { entry.unlock(); } long sleep_time=100; for(int i=1; i <= 10; i++) { try { entry.sent_msgs.addToRetransmitter(seqno, msg); // adds to retransmitter break; } catch(Throwable t) { log.error("failed adding message " + seqno + " to " + dst + " to the retransmitter, retrying... (attempt #" + i + ")"); Util.sleep(sleep_time); sleep_time*=2; } } 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 ! */ @ManagedOperation(description="Trashes all connections to other nodes. This is only used for testing") public 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. */ protected void handleDataReceived(Address sender, long seqno, long conn_id, boolean first, Message msg, Event evt) { 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; recv_table_lock.lock(); try { ReceiverEntry entry=recv_table.get(sender); win=entry != null? entry.received_msgs : null; if(first) { if(entry == null) { entry=getOrCreateReceiverEntry(sender, seqno, conn_id); win=entry.received_msgs; } 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"); ReceiverEntry entry2=recv_table.remove(sender); if(entry2 != null) entry2.received_msgs.reset(); entry=getOrCreateReceiverEntry(sender, seqno, conn_id); win=entry.received_msgs; } 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) { recv_table_lock.unlock(); sendRequestForFirstSeqno(sender); // drops the message and returns (see below) return; } } } finally { if(recv_table_lock.isHeldByCurrentThread()) recv_table_lock.unlock(); } 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(); // 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(sender, seqno); } // An OOB message is passed up immediately. Later, when remove() is called, we discard it. This affects ordering ! // http://jira.jboss.com/jira/browse/JGRP-377 if(msg.isFlagSet(Message.OOB) && added) { try { up_prot.up(evt); } catch(Throwable t) { log.error("couldn't deliver OOB message " + msg, t); } } 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 // 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 concurrent 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) { Tuple,Long> tuple=win.removeMany(max_msg_batch_size); if(tuple == null) return; List msgs=tuple.getVal1(); if(msgs.isEmpty()) return; long highest_removed=tuple.getVal2(); if(highest_removed > 0) sendAck(sender, 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; try { up_prot.up(new Event(Event.MSG, m)); } catch(Throwable t) { log.error("couldn't deliver message " + m, t); } } } } finally { processing.set(false); } } private ReceiverEntry getOrCreateReceiverEntry(Address sender, long seqno, long conn_id) { ReceiverEntry entry=new ReceiverEntry(new AckReceiverWindow(seqno), conn_id); ReceiverEntry entry2=recv_table.putIfAbsent(sender, entry); if(entry2 != null) return entry2; if(log.isTraceEnabled()) log.trace(local_addr + ": created receiver window for " + sender + " at seqno=#" + seqno + " for conn-id=" + conn_id); return entry; } /** 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(this.id); UnicastHeader newhdr=hdr.copy(); newhdr.first=true; copy.putHeader(this.id, newhdr); if(log.isTraceEnabled()) { StringBuilder sb=new StringBuilder(); sb.append(local_addr).append(" --> DATA(").append(copy.getDest()).append(": #").append(newhdr.seqno). append(", conn_id=").append(newhdr.conn_id); if(newhdr.first) sb.append(", first"); sb.append(')'); log.trace(sb); } 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 // https://jira.jboss.org/jira/browse/JGRP-1125; will be reverted in 2.10 // ack.setFlag(Message.OOB); ack.putHeader(this.id, UnicastHeader.createAckHeader(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 short getNewConnectionId() { synchronized(this) { short retval=last_conn_id; if(last_conn_id >= Short.MAX_VALUE || last_conn_id < 0) last_conn_id=0; else last_conn_id++; return retval; } } private void sendRequestForFirstSeqno(Address dest) { Message msg=new Message(dest); msg.setFlag(Message.OOB); UnicastHeader hdr=UnicastHeader.createSendFirstSeqnoHeader(); msg.putHeader(this.id, hdr); if(log.isTraceEnabled()) log.trace(local_addr + " --> SEND_FIRST_SEQNO(" + dest + ")"); down_prot.down(new Event(Event.MSG, msg)); } /** * The following types and fields are serialized: *
                     * | DATA | seqno | conn_id | first |
                     * | ACK  | seqno |
                     * | SEND_FIRST_SEQNO |
                     * 
                */ public static class UnicastHeader extends Header { public static final byte DATA = 0; public static final byte ACK = 1; public static final byte SEND_FIRST_SEQNO = 2; byte type; long seqno; // DATA and ACK short conn_id; // DATA boolean first; // DATA public UnicastHeader() {} // used for externalization public static UnicastHeader createDataHeader(long seqno, short conn_id, boolean first) { return new UnicastHeader(DATA, seqno, conn_id, first); } public static UnicastHeader createAckHeader(long seqno) { return new UnicastHeader(ACK, seqno); } public static UnicastHeader createSendFirstSeqnoHeader() { return new UnicastHeader(SEND_FIRST_SEQNO, 0L); } private UnicastHeader(byte type, long seqno) { this.type=type; this.seqno=seqno; } private UnicastHeader(byte type, long seqno, short conn_id, boolean first) { this.type=type; this.seqno=seqno; this.conn_id=conn_id; this.first=first; } public long getSeqno() { return seqno; } public String toString() { StringBuilder sb=new StringBuilder(); sb.append(type2Str(type)).append(", seqno=").append(seqno); if(conn_id != 0) sb.append(", conn_id=").append(conn_id); if(first) sb.append(", first"); 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() { switch(type) { case DATA: return Global.BYTE_SIZE *2 + Global.LONG_SIZE + Global.SHORT_SIZE; case ACK: return Global.BYTE_SIZE + Global.LONG_SIZE; case SEND_FIRST_SEQNO: return Global.BYTE_SIZE; } return 0; } public UnicastHeader copy() { return new UnicastHeader(type, seqno, conn_id, first); } public void writeTo(DataOutputStream out) throws IOException { out.writeByte(type); switch(type) { case DATA: out.writeLong(seqno); out.writeShort(conn_id); out.writeBoolean(first); break; case ACK: out.writeLong(seqno); break; case SEND_FIRST_SEQNO: break; } } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { type=in.readByte(); switch(type) { case DATA: seqno=in.readLong(); conn_id=in.readShort(); first=in.readBoolean(); break; case ACK: seqno=in.readLong(); break; case SEND_FIRST_SEQNO: break; } } } 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 short send_conn_id; final Lock lock=new ReentrantLock(); public SenderEntry(short 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(", "); sb.append("send_conn_id=" + send_conn_id); return sb.toString(); } } private static final class ReceiverEntry { private final AckReceiverWindow received_msgs; // stores all msgs rcvd by a certain peer in seqno-order private final long recv_conn_id; public ReceiverEntry(AckReceiverWindow received_msgs, long recv_conn_id) { this.received_msgs=received_msgs; this.recv_conn_id=recv_conn_id; } 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(", "); sb.append("recv_conn_id=" + recv_conn_id); return sb.toString(); } } }libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/UNICAST2.java0000644000175000017500000011720011647260573025706 0ustar moellermoellerpackage org.jgroups.protocols; import org.jgroups.*; import org.jgroups.annotations.*; import org.jgroups.conf.PropertyConverters; import org.jgroups.stack.AckSenderWindow; import org.jgroups.stack.NakReceiverWindow; import org.jgroups.stack.Protocol; import org.jgroups.stack.Retransmitter; import org.jgroups.util.AgeOutCache; import org.jgroups.util.TimeScheduler; import org.jgroups.util.Util; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.util.*; 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.atomic.AtomicInteger; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * Reliable unicast layer. Implemented with negative acks. Every sender keeps its messages in an AckSenderWindow. A * receiver stores incoming messages in a NakReceiverWindow, and asks the sender for retransmission if a gap is * detected. Every now and then (stable_interval), a timer task sends a STABLE message to all senders, including the * highest received and delivered seqnos. A sender purges messages lower than highest delivered and asks the STABLE * sender for messages it might have missed (smaller than highest received). A STABLE message can also be sent when * a receiver has received more than max_bytes from a given sender.

                * The advantage of this protocol over {@link org.jgroups.protocols.UNICAST} is that it doesn't send acks for every * message. Instead, it sends 'acks' after receiving max_bytes and/ or periodically (stable_interval). * @author Bela Ban */ @Experimental @MBean(description="Reliable unicast layer") public class UNICAST2 extends Protocol implements Retransmitter.RetransmitCommand, AgeOutCache.Handler

                { public static final long DEFAULT_FIRST_SEQNO=Global.DEFAULT_FIRST_UNICAST_SEQNO; /* ------------------------------------------ Properties ------------------------------------------ */ private long[] timeout= { 400, 800, 1600, 3200 }; // for NakSenderWindow: max time to wait for missing acks @Property(description="Max number of messages to be removed from a NakReceiverWindow. This property might " + "get removed anytime, so don't use it !") private int max_msg_batch_size=50000; @Property(description="Max number of bytes before a stability message is sent to the sender") protected long max_bytes=10000000; @Property(description="Max number of milliseconds before a stability message is sent to the sender(s)") protected long stable_interval=60000L; @Property(description="Max number of STABLE messages sent for the same highest_received seqno. A value < 1 is invalid") protected int max_stable_msgs=5; @Property(description="Number of rows of the matrix in the retransmission table (only for experts)",writable=false) int xmit_table_num_rows=5; @Property(description="Number of elements of a row of the matrix in the retransmission table (only for experts). " + "The capacity of the matrix is xmit_table_num_rows * xmit_table_msgs_per_row",writable=false) int xmit_table_msgs_per_row=10000; @Property(description="Resize factor of the matrix in the retransmission table (only for experts)",writable=false) double xmit_table_resize_factor=1.2; @Property(description="Number of milliseconds after which the matrix in the retransmission table " + "is compacted (only for experts)",writable=false) long xmit_table_max_compaction_time=10 * 60 * 1000; @Property(description="If enabled, the removal of a message from the retransmission table causes an " + "automatic purge (only for experts)",writable=false) boolean xmit_table_automatic_purging=true; @Property(description="Whether to use the old retransmitter which retransmits individual messages or the new one " + "which uses ranges of retransmitted messages. Default is true. Note that this property will be removed in 3.0; " + "it is only used to switch back to the old (and proven) retransmitter mechanism if issues occur") private boolean use_range_based_retransmitter=true; /* --------------------------------------------- JMX ---------------------------------------------- */ private long num_msgs_sent=0, num_msgs_received=0, num_bytes_sent=0, num_bytes_received=0, num_xmits=0; /* --------------------------------------------- Fields ------------------------------------------------ */ private final ConcurrentMap send_table=Util.createConcurrentMap(); private final ConcurrentMap recv_table=Util.createConcurrentMap(); protected final ReentrantLock recv_table_lock=new ReentrantLock(); private final Vector
                members=new Vector
                (11); private Address local_addr=null; private TimeScheduler timer=null; // used for retransmissions (passed to AckSenderWindow) private boolean started=false; private short last_conn_id=0; protected long max_retransmit_time=60 * 1000L; private AgeOutCache
                cache=null; private Future stable_task_future=null; // bcasts periodic STABLE message (added to timer below) public long[] getTimeout() {return timeout;} @Property(name="timeout",converter=PropertyConverters.LongArray.class) public void setTimeout(long[] val) { if(val != null) timeout=val; } public void setMaxMessageBatchSize(int size) { if(size >= 1) max_msg_batch_size=size; } @ManagedAttribute public String getLocalAddress() {return local_addr != null? local_addr.toString() : "null";} @ManagedAttribute public String getMembers() {return members != null? members.toString() : "[]";} @ManagedOperation public String printConnections() { StringBuilder sb=new StringBuilder(); if(!send_table.isEmpty()) { sb.append("send connections:\n"); for(Map.Entry entry: send_table.entrySet()) { sb.append(entry.getKey()).append(": ").append(entry.getValue()).append("\n"); } } if(!recv_table.isEmpty()) { sb.append("\nreceive connections:\n"); for(Map.Entry entry: recv_table.entrySet()) { sb.append(entry.getKey()).append(": ").append(entry.getValue()).append("\n"); } } return sb.toString(); } @ManagedAttribute public long getNumMessagesSent() { return num_msgs_sent; } @ManagedAttribute public long getNumMessagesReceived() { return num_msgs_received; } @ManagedAttribute public long getNumBytesSent() { return num_bytes_sent; } @ManagedAttribute public long getNumBytesReceived() { return num_bytes_received; } @ManagedAttribute public long getNumberOfRetransmissions() { return num_xmits; } public long getMaxRetransmitTime() { return max_retransmit_time; } @Property(description="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 this") 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); } @ManagedAttribute public int getAgeOutCacheSize() { return cache != null? cache.size() : 0; } @ManagedOperation public String printAgeOutCache() { return cache != null? cache.toString() : "n/a"; } public AgeOutCache
                getAgeOutCache() { return cache; } @ManagedAttribute public int getNumberOfMessagesInReceiveWindows() { int num=0; for(ReceiverEntry entry: recv_table.values()) { if(entry.received_msgs != null) num+=entry.received_msgs.size(); } return num; } @ManagedOperation(description="Returns the sizes of all NakReceiverWindow.RetransmitTables") public String printRetransmitTableSizes() { StringBuilder sb=new StringBuilder(); for(Map.Entry entry: recv_table.entrySet()) { NakReceiverWindow win=entry.getValue().received_msgs; sb.append(entry.getKey() + ": ").append(win.getRetransmiTableSize()) .append(" (capacity=" + win.getRetransmitTableCapacity()) .append(", fill factor=" + win.getRetransmitTableFillFactor() + "%)\n"); } return sb.toString(); } @ManagedOperation(description="Compacts the retransmission tables") public void compact() { for(Map.Entry entry: recv_table.entrySet()) { NakReceiverWindow win=entry.getValue().received_msgs; win.compact(); } } @ManagedOperation(description="Purges highes delivered messages and compacts the retransmission tables") public void purgeAndCompact() { for(Map.Entry entry: recv_table.entrySet()) { NakReceiverWindow win=entry.getValue().received_msgs; win.stable(win.getHighestDelivered()); win.compact(); } } public void resetStats() { num_msgs_sent=num_msgs_received=num_bytes_sent=num_bytes_received=num_xmits=0; } public Map dumpStats() { Map m=super.dumpStats(); 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_xmits", num_xmits); m.put("num_msgs_in_recv_windows", getNumberOfMessagesInReceiveWindows()); return m; } public TimeScheduler getTimer() { return timer; } /** * Only used for unit tests, don't use ! * @param timer */ public void setTimer(TimeScheduler timer) { this.timer=timer; } public void init() throws Exception { super.init(); if(max_stable_msgs < 1) throw new IllegalArgumentException("max_stable_msgs ( " + max_stable_msgs + ") must be > 0"); if(max_bytes <= 0) throw new IllegalArgumentException("max_bytes has to be > 0"); } 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; if(stable_interval > 0) { startStableTask(); } } public void stop() { started=false; stopStableTask(); removeAllConnections(); } public Object up(Event evt) { Message msg; Address dst, src; Unicast2Header hdr; switch(evt.getType()) { case Event.MSG: msg=(Message)evt.getArg(); dst=msg.getDest(); if(dst == null || dst.isMulticastAddress() || msg.isFlagSet(Message.NO_RELIABILITY)) // 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=(Unicast2Header)msg.getHeader(this.id); if(hdr == null) break; src=msg.getSrc(); switch(hdr.type) { case Unicast2Header.DATA: // received regular message handleDataReceived(src, hdr.seqno, hdr.conn_id, hdr.first, msg, evt); return null; // we pass the deliverable message up in handleDataReceived() case Unicast2Header.XMIT_REQ: // received ACK for previously sent message handleXmitRequest(src, hdr.seqno, hdr.high_seqno); break; case Unicast2Header.SEND_FIRST_SEQNO: handleResendingOfFirstMessage(src, hdr.seqno); break; case Unicast2Header.STABLE: stable(msg.getSrc(), hdr.seqno, hdr.high_seqno); break; default: log.error("UnicastHeader type " + hdr.type + " not known !"); 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.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() || msg.isFlagSet(Message.NO_RELIABILITY)) break; if(!started) { if(log.isTraceEnabled()) log.trace("discarded message as start() has not yet been called, message: " + msg); return null; } SenderEntry entry=send_table.get(dst); if(entry == null) { entry=new SenderEntry(getNewConnectionId()); 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; short send_conn_id=-1; Unicast2Header 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=Unicast2Header.createDataHeader(seqno, send_conn_id, seqno == DEFAULT_FIRST_SEQNO); msg.putHeader(this.id, hdr); entry.sent_msgs.addToMessages(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); } try { down_prot.down(evt); num_msgs_sent++; num_bytes_sent+=msg.getLength(); } 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; } return down_prot.down(evt); // Pass on to the layer below us } /** * Purge all messages in window for local_addr, which are <= low. Check if the window's highest received message is * > high: if true, retransmit all messages from high - win.high to sender * @param sender * @param highest_delivered * @param highest_seen */ protected void stable(Address sender, long highest_delivered, long highest_seen) { SenderEntry entry=send_table.get(sender); AckSenderWindow win=entry != null? entry.sent_msgs : null; if(win == null) return; if(log.isTraceEnabled()) log.trace(new StringBuilder().append(local_addr).append(" <-- STABLE(").append(sender). append(": ").append(highest_delivered).append("-").append(highest_seen).append(')')); win.ack(highest_delivered); long win_high=win.getHighest(); if(win_high > highest_seen) { for(long seqno=highest_seen; seqno <= win_high; seqno++) { Message msg=win.get(seqno); // destination is still the same (the member which sent the STABLE message) if(msg != null) down_prot.down(new Event(Event.MSG, msg)); } } } @ManagedOperation(description="Sends a STABLE message to all senders. This causes message purging and potential" + " retransmissions from senders") public void sendStableMessages() { for(Map.Entry entry: recv_table.entrySet()) { Address dest=entry.getKey(); ReceiverEntry val=entry.getValue(); NakReceiverWindow win=val != null? val.received_msgs : null; if(win != null) { long low=win.getHighestDelivered(); long high=win.getHighestReceived(); if(val.last_highest == high) { if(val.num_stable_msgs >= val.max_stable_msgs) { continue; } else val.num_stable_msgs++; } else { val.last_highest=high; val.num_stable_msgs=1; } sendStableMessage(dest, low, high); } } } protected void sendStableMessage(Address dest, long low, long high) { Message stable_msg=new Message(dest, null, null); Unicast2Header hdr=Unicast2Header.createStableHeader(low, high); stable_msg.putHeader(this.id, hdr); if(log.isTraceEnabled()) { StringBuilder sb=new StringBuilder(); sb.append(local_addr).append(" --> STABLE(").append(dest).append(": ").append(low).append("-").append(high).append(")"); log.trace(sb.toString()); } down_prot.down(new Event(Event.MSG, stable_msg)); ReceiverEntry entry=recv_table.get(dest); NakReceiverWindow win=entry != null? entry.received_msgs : null; if(win != null) { //System.out.println("[" + local_addr + "] stable(" + dest + ", hd=" + win.getHighestDelivered() + "): " + // "win: " + win); win.stable(win.getHighestDelivered()); } } private void startStableTask() { if(stable_task_future == null || stable_task_future.isDone()) { final Runnable stable_task=new Runnable() { public void run() { try { sendStableMessages(); } catch(Throwable t) { log.error("sending of STABLE messages failed", t); } } }; stable_task_future=timer.scheduleWithFixedDelay(stable_task, stable_interval, stable_interval, TimeUnit.MILLISECONDS); if(log.isTraceEnabled()) log.trace("stable task started"); } } private void stopStableTask() { if(stable_task_future != null) { stable_task_future.cancel(false); stable_task_future=null; } } /** * 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) { NakReceiverWindow win=entry2.received_msgs; if(win != null) sendStableMessage(mbr, win.getHighestDelivered(), win.getHighestReceived()); entry2.reset(); } } /** * This method is public only so it can be invoked by unit testing, but should not otherwise be used ! */ @ManagedOperation(description="Trashes all connections to other nodes. This is only used for testing") public void removeAllConnections() { for(SenderEntry entry: send_table.values()) entry.reset(); send_table.clear(); sendStableMessages(); for(ReceiverEntry entry2: recv_table.values()) entry2.reset(); recv_table.clear(); } public void retransmit(long first_seqno, long last_seqno, Address sender) { Unicast2Header hdr=Unicast2Header.createXmitReqHeader(first_seqno, last_seqno); Message xmit_req=new Message(sender, null, null); xmit_req.putHeader(this.id, hdr); down_prot.down(new Event(Event.MSG, xmit_req)); } /** * 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. */ protected void handleDataReceived(Address sender, long seqno, long conn_id, boolean first, Message msg, Event evt) { 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); } ReceiverEntry entry; NakReceiverWindow win; recv_table_lock.lock(); try { entry=recv_table.get(sender); win=entry != null? entry.received_msgs : null; if(first) { if(entry == null) { entry=getOrCreateReceiverEntry(sender, seqno, conn_id); win=entry.received_msgs; } 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"); ReceiverEntry entry2=recv_table.remove(sender); if(entry2 != null) entry2.received_msgs.destroy(); entry=getOrCreateReceiverEntry(sender, seqno, conn_id); win=entry.received_msgs; } 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) { recv_table_lock.unlock(); sendRequestForFirstSeqno(sender, seqno); // drops the message and returns (see below) return; } } } finally { if(recv_table_lock.isHeldByCurrentThread()) recv_table_lock.unlock(); } boolean added=win.add(seqno, msg); // win is guaranteed to be non-null if we get here num_msgs_received++; num_bytes_received+=msg.getLength(); if(added) { int bytes=entry.received_bytes.addAndGet(msg.getLength()); if(bytes >= max_bytes) { entry.received_bytes_lock.lock(); try { entry.received_bytes.set(0); } finally { entry.received_bytes_lock.unlock(); } sendStableMessage(sender, win.getHighestDelivered(), win.getHighestReceived()); } } // An OOB message is passed up immediately. Later, when remove() is called, we discard it. This affects ordering ! // http://jira.jboss.com/jira/browse/JGRP-377 if(msg.isFlagSet(Message.OOB) && added) { try { up_prot.up(evt); } catch(Throwable t) { log.error("couldn't deliver OOB message " + msg, t); } } final AtomicBoolean processing=win.getProcessing(); if(!processing.compareAndSet(false, true)) { return; } // try to remove (from the AckReceiverWindow) as many messages as possible and pass them up // 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 concurrent 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 boolean released_processing=false; try { while(true) { List msgs=win.removeMany(processing, true, max_msg_batch_size); // remove my own messages if(msgs == null || msgs.isEmpty()) { released_processing=true; return; } 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; try { up_prot.up(new Event(Event.MSG, m)); } catch(Throwable t) { log.error("couldn't deliver message " + m, t); } } } } finally { // 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); } } private ReceiverEntry getOrCreateReceiverEntry(Address sender, long seqno, long conn_id) { NakReceiverWindow win=new NakReceiverWindow(sender, this, seqno-1, seqno-1, timer, use_range_based_retransmitter, xmit_table_num_rows, xmit_table_msgs_per_row, xmit_table_resize_factor, xmit_table_max_compaction_time, xmit_table_automatic_purging); ReceiverEntry entry=new ReceiverEntry(win, conn_id, max_stable_msgs); ReceiverEntry entry2=recv_table.putIfAbsent(sender, entry); if(entry2 != null) return entry2; if(log.isTraceEnabled()) log.trace(local_addr + ": created receiver window for " + sender + " at seqno=#" + seqno + " for conn-id=" + conn_id); return entry; } private void handleXmitRequest(Address sender, long low, long high) { if(log.isTraceEnabled()) log.trace(new StringBuilder().append(local_addr).append(" <-- XMIT(").append(sender). append(": #").append(low).append( "-").append(high).append(')')); SenderEntry entry=send_table.get(sender); AckSenderWindow win=entry != null? entry.sent_msgs : null; if(win != null) { for(long i=low; i <= high; i++) { Message msg=win.get(i); if(msg == null) { if(log.isWarnEnabled() && !local_addr.equals(sender)) { StringBuilder sb=new StringBuilder(); sb.append("(requester=").append(sender).append(", local_addr=").append(this.local_addr); sb.append(") message ").append(sender).append("::").append(i); sb.append(" not found in retransmission table of ").append(sender).append(":\n").append(win); log.warn(sb.toString()); } continue; } down_prot.down(new Event(Event.MSG, msg)); num_xmits++; } } } /** * We need to resend our first message with our conn_id * @param sender * @param seqno Resend messages in the range [lowest .. seqno] */ private void handleResendingOfFirstMessage(Address sender, long seqno) { 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; } long lowest=win.getLowest(); Message rsp=win.get(lowest); 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(); Unicast2Header hdr=(Unicast2Header)copy.getHeader(this.id); Unicast2Header newhdr=hdr.copy(); newhdr.first=true; copy.putHeader(this.id, newhdr); if(log.isTraceEnabled()) { StringBuilder sb=new StringBuilder(); sb.append(local_addr).append(" --> DATA(").append(copy.getDest()).append(": #").append(newhdr.seqno). append(", conn_id=").append(newhdr.conn_id); if(newhdr.first) sb.append(", first"); sb.append(')'); log.trace(sb); } down_prot.down(new Event(Event.MSG, copy)); if(++lowest > seqno) return; for(long i=lowest; i <= seqno; i++) { rsp=win.get(i); if(rsp != null) down_prot.down(new Event(Event.MSG, rsp)); } } private short getNewConnectionId() { synchronized(this) { short retval=last_conn_id; if(last_conn_id >= Short.MAX_VALUE || last_conn_id < 0) last_conn_id=0; else last_conn_id++; return retval; } } private void sendRequestForFirstSeqno(Address dest, long seqno_received) { Message msg=new Message(dest); msg.setFlag(Message.OOB); Unicast2Header hdr=Unicast2Header.createSendFirstSeqnoHeader(seqno_received); msg.putHeader(this.id, hdr); if(log.isTraceEnabled()) log.trace(local_addr + " --> SEND_FIRST_SEQNO(" + dest + ")"); down_prot.down(new Event(Event.MSG, msg)); } /** * The following types and fields are serialized: *
                     * | DATA | seqno | conn_id | first |
                     * | ACK  | seqno |
                     * | SEND_FIRST_SEQNO | seqno |
                     * 
                */ public static class Unicast2Header extends Header { public static final byte DATA = 0; public static final byte XMIT_REQ = 1; public static final byte SEND_FIRST_SEQNO = 2; public static final byte STABLE = 3; byte type; long seqno; // DATA, XMIT_REQ and STABLE long high_seqno; // XMIT_REQ and STABLE short conn_id; // DATA boolean first; // DATA public Unicast2Header() {} // used for externalization public static Unicast2Header createDataHeader(long seqno, short conn_id, boolean first) { return new Unicast2Header(DATA, seqno, 0L, conn_id, first); } public static Unicast2Header createXmitReqHeader(long low, long high) { Unicast2Header retval=new Unicast2Header(XMIT_REQ, low); retval.high_seqno=high; return retval; } public static Unicast2Header createStableHeader(long low, long high) { Unicast2Header retval=new Unicast2Header(STABLE, low); retval.high_seqno=high; return retval; } public static Unicast2Header createSendFirstSeqnoHeader(long seqno_received) { return new Unicast2Header(SEND_FIRST_SEQNO, seqno_received); } private Unicast2Header(byte type, long seqno) { this.type=type; this.seqno=seqno; } private Unicast2Header(byte type, long seqno, long high, short conn_id, boolean first) { this.type=type; this.seqno=seqno; this.high_seqno=high; this.conn_id=conn_id; this.first=first; } public long getSeqno() { return seqno; } public String toString() { StringBuilder sb=new StringBuilder(); sb.append(type2Str(type)).append(", seqno=").append(seqno); if(conn_id != 0) sb.append(", conn_id=").append(conn_id); if(first) sb.append(", first"); return sb.toString(); } public static String type2Str(byte t) { switch(t) { case DATA: return "DATA"; case XMIT_REQ: return "XMIT_REQ"; case SEND_FIRST_SEQNO: return "SEND_FIRST_SEQNO"; case STABLE: return "STABLE"; default: return ""; } } public final int size() { switch(type) { case DATA: return Global.BYTE_SIZE *2 + Global.LONG_SIZE + Global.SHORT_SIZE; case XMIT_REQ: case STABLE: return Global.BYTE_SIZE + Global.LONG_SIZE *2; case SEND_FIRST_SEQNO: return Global.BYTE_SIZE; } return 0; } public Unicast2Header copy() { return new Unicast2Header(type, seqno, high_seqno, conn_id, first); } public void writeTo(DataOutputStream out) throws IOException { out.writeByte(type); switch(type) { case DATA: out.writeLong(seqno); out.writeShort(conn_id); out.writeBoolean(first); break; case XMIT_REQ: case STABLE: out.writeLong(seqno); out.writeLong(high_seqno); break; case SEND_FIRST_SEQNO: out.writeLong(seqno); break; } } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { type=in.readByte(); switch(type) { case DATA: seqno=in.readLong(); conn_id=in.readShort(); first=in.readBoolean(); break; case XMIT_REQ: case STABLE: seqno=in.readLong(); high_seqno=in.readLong(); break; case SEND_FIRST_SEQNO: seqno=in.readLong(); break; } } } 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 short send_conn_id; final Lock lock=new ReentrantLock(); public SenderEntry(short send_conn_id) { this.send_conn_id=send_conn_id; sent_msgs=new AckSenderWindow(); } 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(", "); sb.append("send_conn_id=" + send_conn_id); return sb.toString(); } } private static final class ReceiverEntry { private final NakReceiverWindow received_msgs; // stores all msgs rcvd by a certain peer in seqno-order private final long recv_conn_id; final AtomicInteger received_bytes=new AtomicInteger(0); final Lock received_bytes_lock=new ReentrantLock(); private long last_highest=-1; private int num_stable_msgs=0; public final int max_stable_msgs; public ReceiverEntry(NakReceiverWindow received_msgs, long recv_conn_id, final int max_stable_msgs) { this.received_msgs=received_msgs; this.recv_conn_id=recv_conn_id; this.max_stable_msgs=max_stable_msgs; } void reset() { if(received_msgs != null) received_msgs.destroy(); received_bytes.set(0); last_highest=-1; num_stable_msgs=0; } public String toString() { StringBuilder sb=new StringBuilder(); if(received_msgs != null) sb.append(received_msgs).append(", "); sb.append("recv_conn_id=" + recv_conn_id); return sb.toString(); } } }libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/VERIFY_SUSPECT.java0000644000175000017500000003013011647260573026724 0ustar moellermoeller package org.jgroups.protocols; import org.jgroups.*; import org.jgroups.annotations.LocalAddress; import org.jgroups.annotations.MBean; import org.jgroups.annotations.Property; import org.jgroups.conf.PropertyConverters; import org.jgroups.stack.IpAddress; import org.jgroups.stack.Protocol; import org.jgroups.util.Util; import java.io.*; import java.net.InetAddress; import java.net.NetworkInterface; 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 */ @MBean(description="Double-checks suspicions reports") public class VERIFY_SUSPECT extends Protocol implements Runnable { /* ------------------------------------------ Properties ------------------------------------------ */ @Property(description="Number of millisecs to wait for a response from a suspected member") private long timeout=2000; @Property(description="Number of verify heartbeats sent to a suspected member") private int num_msgs=1; @Property(description="Use InetAddress.isReachable() to verify suspected member instead of regular messages") private boolean use_icmp=false; @LocalAddress @Property(description="Interface for ICMP pings. Used if use_icmp is true " + "The following special values are also recognized: GLOBAL, SITE_LOCAL, LINK_LOCAL and NON_LOOPBACK", systemProperty={Global.BIND_ADDR, Global.BIND_ADDR_OLD}, defaultValueIPv4=Global.NON_LOOPBACK_ADDRESS, defaultValueIPv6=Global.NON_LOOPBACK_ADDRESS) private InetAddress bind_addr; // interface for ICMP pings @Property(name="bind_interface", converter=PropertyConverters.BindInterface.class, description="The interface (NIC) which should be used by this transport", dependsUpon="bind_addr") protected String bind_interface_str=null; /* --------------------------------------------- Fields ------------------------------------------------ */ /** network interface to be used to send the ICMP packets */ private NetworkInterface intf=null; private Address local_addr=null; /**keys=Addresses, vals=time in mcses since added **/ private final Hashtable suspects=new Hashtable(); private Thread timer=null; public VERIFY_SUSPECT() { } public Object down(Event evt) { if(evt.getType() == Event.SET_LOCAL_ADDRESS) { local_addr=(Address)evt.getArg(); } return down_prot.down(evt); } public Object up(Event evt) { switch(evt.getType()) { case Event.SUSPECT: // it all starts here ... 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(this.id); if(hdr == null) break; 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(this.id, 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(this.id, 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 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 { 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 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); } public int size() { return Global.SHORT_SIZE + Util.size(from); } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/VIEW_SYNC.java0000644000175000017500000002703011647260573026125 0ustar moellermoellerpackage org.jgroups.protocols; import org.jgroups.*; import org.jgroups.annotations.*; import org.jgroups.conf.ClassConfigurator; import org.jgroups.protocols.pbcast.GMS; import org.jgroups.stack.Protocol; import org.jgroups.util.TimeScheduler; import org.jgroups.util.Util; import java.io.*; 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 */ @MBean(description="Periodically sends the view to the group") public class VIEW_SYNC extends Protocol { /* ----------------------------------------- Properties --------------------------------------- */ @Property(description="View synchronization interval. Default is 60 sec") private long avg_send_interval=60000; /* --------------------------------------------- JMX ---------------------------------------------- */ private int num_views_sent=0; @ManagedAttribute private int num_view_requests_sent=0; @ManagedAttribute private int num_view_responses_seen=0; @ManagedAttribute private int num_views_non_local=0; @ManagedAttribute private int num_views_equal=0; @ManagedAttribute private int num_views_less=0; @ManagedAttribute private int num_views_adjusted=0; @ManagedAttribute private long last_view_request_sent=0; /* --------------------------------------------- Fields ------------------------------------------------------ */ private Address local_addr=null; private final Vector
                mbrs=new Vector
                (); private View my_view=null; private ViewId my_vid=null; @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(); private TimeScheduler timer=null; private static final short gms_id=ClassConfigurator.getProtocolId(GMS.class); public long getAverageSendInterval() { return avg_send_interval; } public void setAverageSendInterval(long gossip_interval) { avg_send_interval=gossip_interval; } @ManagedAttribute public int getNumViewsSent() { return num_views_sent; } public int getNumViewsAdjusted() { return num_views_adjusted; } @ManagedOperation public void resetStats() { super.resetStats(); num_views_adjusted=num_views_sent=0; } 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 */ @ManagedOperation(description="Sends a VIEW_SYNC_REQ to all members") public void sendViewRequest() { Message msg=new Message(null); msg.setFlag(Message.OOB); ViewSyncHeader hdr=new ViewSyncHeader(ViewSyncHeader.VIEW_SYNC_REQ, null); msg.putHeader(this.id, 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(this.id); 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; } 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; case Event.SET_LOCAL_ADDRESS: local_addr=(Address)evt.getArg(); 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_id, 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(this.id, 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 { 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 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); } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/obsolete/0000755000175000017500000000000011647260573025466 5ustar moellermoellerlibjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/obsolete/ENCRYPT.java.txt0000644000175000017500000002653411647260573030305 0ustar moellermoeller 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)((PingData)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; } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/obsolete/ENCRYPT1_4.java.txt0000644000175000017500000004710611647260573030607 0ustar moellermoeller// changes made by mandar // Added .* imports // replacing SecretKey with SecretKey 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.PingData; 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=((PingData) 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; } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/obsolete/FC.java.txt0000644000175000017500000005133011647260573027441 0ustar moellermoeller 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.2 $ */ 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 ""; } } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/obsolete/FD_PROB.java.txt0000644000175000017500000005031011647260573030261 0ustar moellermoeller 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.2 $ */ 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; } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/obsolete/FD_RAND.java.txt0000644000175000017500000001543411647260573030253 0ustar moellermoeller 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; } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/obsolete/FD_SHUN.java.txt0000644000175000017500000002166011647260573030302 0ustar moellermoeller 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; } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/obsolete/PERF.java.txt0000644000175000017500000000422311647260573027704 0ustar moellermoeller 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; } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/obsolete/PerfHeader.java.txt0000644000175000017500000002712611647260573031164 0ustar moellermoeller package org.jgroups.protocols; import org.jgroups.Header; import org.jgroups.Message; import org.jgroups.stack.Protocol; import org.jgroups.util.Util; import org.jgroups.logging.Log; import org.jgroups.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(); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/obsolete/UDP.java.txt0000644000175000017500000021235311647260573027605 0ustar moellermoeller 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(); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/obsolete/UDP_NIO.java.txt0000644000175000017500000015140611647260573030313 0ustar moellermoellerpackage 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.jgroups.logging.Log; import org.jgroups.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 * @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); } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/obsolete/UNIFORM.java.txt0000644000175000017500000002253711647260573030277 0ustar moellermoeller 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); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/package.html0000644000175000017500000000024311647260573026132 0ustar moellermoeller Provides implementations of transport protocols which are responsible for sending and receiving messages to/from the network. libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/pbcast/0000755000175000017500000000000011647260573025126 5ustar moellermoellerlibjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/pbcast/ClientGmsImpl.java0000644000175000017500000003520011647260573030500 0ustar moellermoellerpackage org.jgroups.protocols.pbcast; import org.jgroups.*; import org.jgroups.protocols.PingData; 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 $Revision: 1.78 $ */ 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,boolean useFlushIfPresent) { joinInternal(address, false,useFlushIfPresent); } public void joinWithStateTransfer(Address local_addr, boolean useFlushIfPresent) { joinInternal(local_addr,true,useFlushIfPresent); } /** * 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 joinInternal(Address mbr, boolean joinWithStateTransfer,boolean useFlushIfPresent) { 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 (responses == null) { // gray: we've seen this NPE here. not sure of the cases but wanted to add more debugging info throw new NullPointerException("responses returned by findInitialMembers for " + join_promise + " is null"); } /*// Sept 2008 (bela): break if we got a belated JoinRsp (https://jira.jboss.org/jira/browse/JGRP-687) if(join_promise.hasResult()) { rsp=join_promise.getResult(gms.join_timeout); // clears the result continue; }*/ 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(PingData 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,useFlushIfPresent); } 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"); continue; } // 1. check whether JOIN was rejected String failure=rsp.getFailReason(); if(failure != null) throw new SecurityException(failure); // 2. Install digest 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 doc/design/varia2.txt 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.getId(), tmphdr); 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", e); rsp=null; } } } @SuppressWarnings("unchecked") 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();) { Address address=iter.next().getAddress(); if(address != null && address.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 } /** * 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; } /* --------------------------- Private Methods ------------------------------------ */ void sendJoinMessage(Address coord, Address mbr,boolean joinWithTransfer, boolean useFlushIfPresent) { 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,useFlushIfPresent); else hdr=new GMS.GmsHeader(GMS.GmsHeader.JOIN_REQ, mbr,useFlushIfPresent); msg.putHeader(gms.getId(), hdr); 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(PingData mbr:mbrs) { if(mbr.hasCoord()) { if(!votes.containsKey(mbr.getCoordAddress())) votes.put(mbr.getCoordAddress(), 1); else { count=votes.get(mbr.getCoordAddress()); votes.put(mbr.getCoordAddress(), count + 1); } } } // we have seen members say someone else is coordinator but they disagree for(PingData mbr:mbrs) { // remove members who don't agree with the election (Florida) if (votes.containsKey(mbr.getAddress()) && (!mbr.isCoord())) { votes.remove(mbr.getAddress()); } } 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); View new_view=new View(view_id, mbrs); gms.up(new Event(Event.PREPARE_VIEW,new_view)); gms.down(new Event(Event.PREPARE_VIEW,new_view)); gms.installView(new_view); 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()); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/pbcast/CoordGmsImpl.java0000644000175000017500000002442711647260573030341 0ustar moellermoeller package org.jgroups.protocols.pbcast; import org.jgroups.Address; import org.jgroups.Event; import org.jgroups.Message; import org.jgroups.View; import org.jgroups.util.Digest; import org.jgroups.util.MergeId; import org.jgroups.util.MutableDigest; import java.util.*; /** * 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 ServerGmsImpl { private final Long MAX_SUSPEND_TIMEOUT=new Long(30000); public CoordGmsImpl(GMS g) { super(g); } public MergeId getMergeId() { return merger.getMergeId(); } public void init() throws Exception { super.init(); merger.cancelMerge(null); } public void join(Address mbr,boolean useFlushIfPresent) { wrongMethod("join"); } public void joinWithStateTransfer(Address mbr,boolean useFlushIfPresent) { 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)); 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 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)); handleMembershipChange(suspected); } /** * Invoked upon receiving a MERGE event from the MERGE layer. Starts the merge protocol. * See description of protocol in DESIGN. * @param views A List of different views detected by the merge protocol */ public void merge(Map views) { merger.merge(views); } public void handleMergeResponse(MergeData data, MergeId merge_id) { merger.handleMergeResponse(data, merge_id); } public void handleMergeCancelled(MergeId merge_id) { merger.handleMergeCancelled(merge_id); } /** * Fetches the digests from all members and installs them again. Used only for diagnosis and support; don't * use this otherwise ! */ void fixDigests() { merger.fixDigests(); } public void handleMembershipChange(Collection requests) { boolean joinAndStateTransferInitiated=false; boolean useFlushIfPresent=gms.use_flush_if_present; 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); useFlushIfPresent=req.useFlushIfPresent; break; case Request.JOIN_WITH_STATE_TRANSFER: new_mbrs.add(req.mbr); joinAndStateTransferInitiated=true; useFlushIfPresent=req.useFlushIfPresent; 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=" + String.valueOf(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 if(log.isWarnEnabled()) log.warn(mbr + " already present; returning existing view " + gms.view); JoinRsp 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); if(new_view.size() == 0 && gms.local_addr != null && gms.local_addr.equals(new_view.getCreator())) { if(log.isTraceEnabled()) log.trace("view " + new_view + " is empty: will not multicast it (last view)"); if(leaving) { gms.initState(); // in case connect() is called again } return; } 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 =!useFlushIfPresent || gms.startFlush(new_view); if(!successfulFlush && hasJoiningMembers){ // Don't send a join response if the flush fails (http://jira.jboss.org/jira/browse/JGRP-759) // The joiner should block until the previous FLUSH completed sendLeaveResponses(leaving_mbrs); // we still have to send potential leave responses // 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, join_rsp != null? join_rsp.getDigest() : null, join_rsp,new_mbrs); } finally { if(hasJoiningMembers) gms.getDownProtocol().down(new Event(Event.RESUME_STABLE)); if(!joinAndStateTransferInitiated && useFlushIfPresent) 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(leaving && !mbrs.contains(gms.local_addr)) return; gms.installView(new_view, digest); } public void stop() { super.stop(); // sets leaving=false merger.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.getId(), hdr); gms.getDownProtocol().down(new Event(Event.MSG, msg)); } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/pbcast/FLUSH.java0000644000175000017500000011372011647260573026656 0ustar moellermoellerpackage org.jgroups.protocols.pbcast; import org.jgroups.*; import org.jgroups.annotations.*; import org.jgroups.stack.Protocol; import org.jgroups.util.Digest; import org.jgroups.util.Promise; import org.jgroups.util.Util; import java.io.*; import java.util.*; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.ReentrantLock; /** * 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 * @since 2.4 */ @MBean(description = "Flushes the cluster") @DeprecatedProperty(names = { "auto_flush_conf" }) public class FLUSH extends Protocol { /* * ------------------------------------------ Properties------------------------------------------ */ @Property(description = "Max time to keep channel blocked in flush. Default is 8000 msec") private long timeout = 8000; @Property(description = "Timeout (per atttempt) to quiet the cluster during the first flush phase. Default is 2000 msec") private long start_flush_timeout = 2000; @Property(description = "Timeout to wait for UNBLOCK after STOP_FLUSH is issued. Default is 2000 msec") private long end_flush_timeout = 2000; @Property(description = "Retry timeout after an unsuccessful attempt to quiet the cluster (first flush phase). Default is 3000 msec") private long retry_timeout = 2000; @Property(description = "Reconciliation phase toggle. Default is true") private boolean enable_reconciliation = true; /* * --------------------------------------------- JMX ---------------------------------------------- */ private long startFlushTime; private long totalTimeInFlush; private int numberOfFlushes; private double averageFlushDuration; /* * --------------------------------------------- Fields------------------------------------------------------ */ @GuardedBy("sharedLock") private View currentView=new View(new ViewId(), new Vector

                ()); 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=new ArrayList
                (); private final AtomicInteger viewCounter = new AtomicInteger(0); @GuardedBy("sharedLock") private final Map flushCompletedMap=new HashMap(); @GuardedBy("sharedLock") private final List
                flushNotCompletedMap=new ArrayList
                (); @GuardedBy("sharedLock") private final Set
                suspected=new TreeSet
                (); @GuardedBy("sharedLock") private final List
                reconcileOks=new ArrayList
                (); private final Object sharedLock = new Object(); private final ReentrantLock blockMutex = new ReentrantLock(); private final Condition notBlockedDown = blockMutex.newCondition(); /** * 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 Promise flush_unblock_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() { } public long getStartFlushTimeout() { return start_flush_timeout; } public void setStartFlushTimeout(long start_flush_timeout) { this.start_flush_timeout = start_flush_timeout; } public long getRetryTimeout() { return retry_timeout; } public void setRetryTimeout(long retry_timeout) { this.retry_timeout = retry_timeout; } 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); blockMutex.lock(); try { isBlockingFlushDown = true; } finally { blockMutex.unlock(); } } 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 --------------------- */ @ManagedAttribute public double getAverageFlushDuration() { return averageFlushDuration; } @ManagedAttribute public long getTotalTimeInFlush() { return totalTimeInFlush; } @ManagedAttribute public int getNumberOfFlushes() { return numberOfFlushes; } @ManagedOperation(description = "Request cluster flush") public boolean startFlush() { return startFlush(new Event(Event.SUSPEND)); } @SuppressWarnings("unchecked") private boolean startFlush(Event evt) { 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(localAddress + ": timed out waiting for flush responses after " + start_flush_timeout + " ms. Rejecting flush to participants " + flushParticipants); rejectFlush(flushParticipants, currentViewId()); } } return successfulFlush; } @ManagedOperation(description = "Request end of flush in a cluster") 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(this.id); 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_USE_FLUSH: return handleConnect(evt,true); case Event.CONNECT_WITH_STATE_TRANSFER: case Event.CONNECT_WITH_STATE_TRANSFER_USE_FLUSH: return handleConnect(evt, false); case Event.SUSPEND: return startFlush(evt); // only for testing, see FLUSH#testFlushWithCrashedFlushCoordinator case Event.SUSPEND_BUT_FAIL: if (!flushInProgress.get()) { flush_promise.reset(); ArrayList
                flushParticipants = null; synchronized (sharedLock) { flushParticipants = new ArrayList
                (currentView.getMembers()); } onSuspend(flushParticipants); } break; case Event.RESUME: onResume(evt); return null; case Event.SET_LOCAL_ADDRESS: localAddress = (Address) evt.getArg(); break; } return down_prot.down(evt); } private Object handleConnect(Event evt, boolean waitForUnblock) { if (sentBlock.compareAndSet(false, true)) { sendBlockUpToChannel(); } Object result = down_prot.down(evt); if (result instanceof Throwable) { // set the var back to its original state if we cannot // connect successfully sentBlock.set(false); } if(waitForUnblock) waitForUnblock(); return result; } private void blockMessageDuringFlush() { boolean shouldSuspendByItself = false; blockMutex.lock(); try { while (isBlockingFlushDown) { if (log.isDebugEnabled()) log.debug(localAddress + ": blocking for " + (timeout <= 0 ? "ever" : timeout + "ms")); shouldSuspendByItself = !notBlockedDown.await(timeout, TimeUnit.MILLISECONDS); } if (shouldSuspendByItself) { isBlockingFlushDown = false; log.warn(localAddress + ": unblocking after " + timeout + "ms"); flush_promise.setResult(Boolean.TRUE); notBlockedDown.signalAll(); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { blockMutex.unlock(); } } public Object up(Event evt) { switch (evt.getType()) { case Event.MSG: Message msg = (Message) evt.getArg(); final FlushHeader fh = (FlushHeader) msg.getHeader(this.id); 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(localAddress + ": received START_FLUSH 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; boolean participant = flushParticipants != null && flushParticipants.contains(localAddress); if (log.isDebugEnabled()) { log.debug(localAddress + ": received ABORT_FLUSH from flush coordinator " + msg.getSrc() + ", am I flush participant=" + participant); } if (participant) { resetForNextFlush(); } break; case FlushHeader.FLUSH_NOT_COMPLETED: if (log.isDebugEnabled()) { log.debug(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(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() { 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: View tmpView = (View) evt.getArg(); if (!tmpView.containsMember(localAddress)) { onViewChange(tmpView); } break; case Event.SUSPECT: onSuspect((Address) evt.getArg()); break; case Event.SUSPEND: return startFlush(evt); case Event.RESUME: onResume(evt); return null; case Event.UNBLOCK: flush_unblock_promise.setResult(Boolean.TRUE); break; } return up_prot.up(evt); } private void waitForUnblock() { try { flush_unblock_promise.getResultWithTimeout(end_flush_timeout); } catch (TimeoutException t) { if (log.isWarnEnabled()) log.warn(localAddress + ": waiting for UNBLOCK timed out after " + end_flush_timeout + " ms"); } finally { flush_unblock_promise.reset(); } } 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(localAddress + ": all FLUSH_RECONCILE_OK received"); } } } private void handleFlushReconcile(Message msg, FlushHeader fh) { Address requester = msg.getSrc(); Digest reconcileDigest = fh.digest; if (log.isDebugEnabled()) log.debug(localAddress + ": received FLUSH_RECONCILE, passing digest to NAKACK " + reconcileDigest); // Let NAKACK reconcile missing messages down_prot.down(new Event(Event.REBROADCAST, reconcileDigest)); if (log.isDebugEnabled()) log.debug(localAddress + ": returned from FLUSH_RECONCILE, " + " sending RECONCILE_OK to " + requester); Message reconcileOk = new Message(requester); reconcileOk.setFlag(Message.OOB); reconcileOk.putHeader(this.id, 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(this.id, fhr); down_prot.down(new Event(Event.MSG, response)); if (log.isDebugEnabled()) log.debug(localAddress + ": received START_FLUSH, 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.setFlag(Message.OOB); reject.putHeader(this.id, 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() { this.up(new Event(Event.BLOCK)); sentUnblock.set(false); } private void sendUnBlockUpToChannel() { sentBlock.set(false); this.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; View oldView; synchronized (sharedLock) { suspected.retainAll(view.getMembers()); oldView = currentView; currentView = view; coordinatorLeft = !oldView.getMembers().isEmpty() && !view.getMembers().isEmpty() && !view.containsMember(oldView.getCreator()); } if (log.isDebugEnabled()) log.debug(localAddress + ": installing view " + view); return coordinatorLeft; } private void onStopFlush() { if (stats && startFlushTime > 0) { long stopFlushTime = System.currentTimeMillis(); totalTimeInFlush += (stopFlushTime - startFlushTime); if (numberOfFlushes > 0) { averageFlushDuration = totalTimeInFlush / (double) numberOfFlushes; } startFlushTime = 0; } if (log.isDebugEnabled()) log.debug(localAddress + ": received STOP_FLUSH, unblocking FLUSH.down() and sending UNBLOCK up"); resetForNextFlush(); if (sentUnblock.compareAndSet(false, true)) { // ensures that we do not repeat unblock event sendUnBlockUpToChannel(); } } private void resetForNextFlush() { synchronized (sharedLock) { flushCompletedMap.clear(); flushNotCompletedMap.clear(); flushMembers.clear(); suspected.clear(); flushCoordinator = null; flushCompleted = false; } blockMutex.lock(); try { isBlockingFlushDown = false; notBlockedDown.signalAll(); } finally { blockMutex.unlock(); } flushInProgress.set(false); } /** * Starts the flush protocol * @param members List of participants in the flush protocol. Guaranteed to be non-null */ private void onSuspend(final List
                members) { Message msg = null; Collection
                participantsInFlush = null; synchronized (sharedLock) { flushCoordinator = localAddress; // start FLUSH only on group members that we need to flush participantsInFlush = members; participantsInFlush.retainAll(currentView.getMembers()); flushMembers.clear(); flushMembers.addAll(participantsInFlush); flushMembers.removeAll(suspected); msg = new Message(null, localAddress, null); msg.putHeader(this.id, 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(localAddress + ": flush coordinator " + " is starting FLUSH with participants " + participantsInFlush); } } @SuppressWarnings("unchecked") private void onResume(Event evt) { List
                members = (List
                ) evt.getArg(); long viewID = currentViewId(); boolean isParticipant = false; synchronized(sharedLock) { isParticipant = flushMembers.contains(localAddress) || (members != null && members.contains(localAddress)); } 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 if (log.isDebugEnabled()) log.debug(localAddress + ": received RESUME, sending STOP_FLUSH to all"); msg.putHeader(this.id, new FlushHeader(FlushHeader.STOP_FLUSH, viewID)); down_prot.down(new Event(Event.MSG, msg)); } 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 if (log.isDebugEnabled()) log.debug(localAddress + ": received RESUME, sending STOP_FLUSH to " + address); msg.putHeader(this.id, new FlushHeader(FlushHeader.STOP_FLUSH, viewID)); down_prot.down(new Event(Event.MSG, msg)); } } if(isParticipant) waitForUnblock(); } private void onStartFlush(Address flushStarter, FlushHeader fh) { if (stats) { startFlushTime = System.currentTimeMillis(); numberOfFlushes += 1; } boolean proceed = false; boolean amIFlushInitiator = false; synchronized (sharedLock) { amIFlushInitiator = flushStarter.equals(localAddress); if(!amIFlushInitiator){ flushCoordinator = flushStarter; flushMembers.clear(); if (fh.flushParticipants != null) { flushMembers.addAll(fh.flushParticipants); } flushMembers.removeAll(suspected); } proceed = flushMembers.contains(localAddress); } 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(); blockMutex.lock(); try { isBlockingFlushDown = true; } finally { blockMutex.unlock(); } } else { if (log.isDebugEnabled()) log.debug(localAddress + ": received START_FLUSH, 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(this.id, fhr); down_prot.down(new Event(Event.MSG, msg)); if (log.isDebugEnabled()) log.debug(localAddress + ": received START_FLUSH, 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(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(this.id, fh); if (log.isDebugEnabled()) log.debug(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(localAddress + ": all FLUSH_COMPLETED received"); } else if (collision) { // reject flush if we have at least one OK and at least one FAIL Runnable r = new Runnable() { public void run() { 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 != null && address.equals(flushCoordinator); if (flushCoordinatorSuspected) { 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(localAddress + ": flush coordinator " + flushCoordinator + " suspected," + " I am the neighbor, completing the 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(localAddress + ": 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(this.id, 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 { 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; 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 writeTo(DataOutputStream out) throws IOException { out.writeByte(type); out.writeLong(viewID); Util.writeAddresses(flushParticipants, out); Util.writeStreamable(digest, out); } @SuppressWarnings("unchecked") public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { type = in.readByte(); viewID = in.readLong(); flushParticipants =(Collection
                )Util.readAddresses(in, ArrayList.class); digest = (Digest) Util.readStreamable(Digest.class, in); } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/pbcast/GMS.java0000644000175000017500000015306411647260573026430 0ustar moellermoellerpackage org.jgroups.protocols.pbcast; import org.jgroups.*; import org.jgroups.annotations.*; import org.jgroups.conf.PropertyConverters; import org.jgroups.logging.Log; import org.jgroups.protocols.TP; import org.jgroups.protocols.pbcast.GmsImpl.Request; import org.jgroups.stack.Protocol; import org.jgroups.util.*; import org.jgroups.util.Queue; import org.jgroups.util.UUID; import java.io.*; import java.util.*; import java.util.concurrent.Callable; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; /** * 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 */ @MBean(description="Group membership protocol") @DeprecatedProperty(names={"join_retry_timeout","digest_timeout","use_flush","flush_timeout", "merge_leader", "reject_join_from_existing_member", "shun"}) public class GMS extends Protocol implements TP.ProbeHandler { private static final String CLIENT="Client"; private static final String COORD="Coordinator"; private static final String PART="Participant"; /* ------------------------------------------ Properties ------------------------------------------ */ @Property(description="Join timeout") long join_timeout=5000; @Property(description="Leave timeout") long leave_timeout=5000; @Property(description="Timeout to complete merge") long merge_timeout=5000; // time to wait for all MERGE_RSPS @Property(description="Print local address of this member after connect. Default is true") private boolean print_local_addr=true; @Property(description="Print physical address(es) on startup") private boolean print_physical_addrs=true; @Property(description="If true this member can never become coordinator. Default is false", deprecatedMessage="This method will be deprecated in 3.0") 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 ! */ @Property(description="Temporary switch. Default is true and should not be changed") 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 */ @Property(description="View bundling toggle") private boolean view_bundling=true; @Property(description="Max view bundling timeout if view bundling is turned on. Default is 50 msec") private long max_bundling_time=50; // 50ms max to wait for other JOIN, LEAVE or SUSPECT requests @Property(description="Max number of old members to keep in history. Default is 50") protected int num_prev_mbrs=50; @Property(description="Time in ms to wait for all VIEW acks (0 == wait forever. Default is 2000 msec" ) long view_ack_collection_timeout=2000; @Property(description="Timeout to resume ViewHandler. Default is 10000 msec") long resume_task_timeout=10000; @Property(description="Use flush for view changes. Default is true") boolean use_flush_if_present=true; @Property(description="Logs failures for collecting all view acks if true") boolean log_collect_msgs=true; /* --------------------------------------------- JMX ---------------------------------------------- */ private int num_views=0; /** Stores the last 20 views */ private final BoundedList prev_views=new BoundedList(20); /* --------------------------------------------- Fields ------------------------------------------------ */ @Property(converter=PropertyConverters.FlushInvoker.class,name="flush_invoker_class") protected Class> flushInvokerClass; private GmsImpl impl=null; private final Object impl_mutex=new Object(); // synchronizes event entry into impl private final Hashtable impls=new Hashtable(3); // Handles merge related tasks final Merger merger=new Merger(this, log); protected Address local_addr=null; protected 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); /** Keeps track of old members (up to num_prev_mbrs) */ private BoundedList
                prev_members=null; protected View view=null; protected ViewId view_id=null; protected long ltime=0; protected TimeScheduler timer=null; /** Class to process JOIN, LEAVE and MERGE requests */ private final ViewHandler view_handler=new ViewHandler(); /** To collect VIEW_ACKs from all members */ protected final AckCollector ack_collector=new AckCollector(); //[JGRP-700] - FLUSH: flushing should span merge protected final AckCollector merge_ack_collector=new AckCollector(); boolean flushProtocolInStack=false; public GMS() { initState(); } @ManagedAttribute public String getView() {return view_id != null? view_id.toString() : "null";} @ManagedAttribute public int getNumberOfViews() {return num_views;} @ManagedAttribute public String getLocalAddress() {return local_addr != null? local_addr.toString() : "null";} @ManagedAttribute public String getMembers() {return members.toString();} @ManagedAttribute public int getNumMembers() {return members.size();} public long getJoinTimeout() {return join_timeout;} public void setJoinTimeout(long t) {join_timeout=t;} public long getMergeTimeout() { return merge_timeout; } public void setMergeTimeout(long timeout) {merge_timeout=timeout;} /** @deprecated */ public static long getJoinRetryTimeout() {return -1;} /** @deprecated */ public void setJoinRetryTimeout(long t) {} @Deprecated public static boolean isShun() {return false;} @Deprecated public void setShun(boolean s) {} @ManagedOperation 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 void setPrintLocalAddress(boolean flag) {print_local_addr=flag;} public void setPrintLocalAddr(boolean flag) {setPrintLocalAddress(flag);} public long getViewAckCollectionTimeout() { return view_ack_collection_timeout; } public void setViewAckCollectionTimeout(long view_ack_collection_timeout) { if(view_ack_collection_timeout <= 0) throw new IllegalArgumentException("view_ack_collection_timeout has to be greater than 0"); 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; } @ManagedAttribute public int getViewHandlerSize() {return view_handler.size();} @ManagedAttribute public boolean isViewHandlerSuspended() {return view_handler.suspended();} @ManagedOperation public String dumpViewHandlerQueue() { return view_handler.dumpQueue(); } @ManagedOperation public String dumpViewHandlerHistory() { return view_handler.dumpHistory(); } @ManagedOperation public void suspendViewHandler() { view_handler.suspend(null); } @ManagedOperation public void resumeViewHandler() { view_handler.resumeForce(); } Log getLog() {return log;} ViewHandler getViewHandler() {return view_handler;} @ManagedOperation public String printPreviousViews() { StringBuilder sb=new StringBuilder(); for(View view: prev_views) { sb.append(view).append("\n"); } return sb.toString(); } @ManagedOperation public void suspect(String suspected_member) { if(suspected_member == null) return; Map contents= UUID.getContents(); for(Map.Entry entry: contents.entrySet()) { String logical_name=entry.getValue(); if(logical_name != null && logical_name.equals(suspected_member)) { Address suspect=entry.getKey(); if(suspect != null) up(new Event(Event.SUSPECT, suspect)); } } } public boolean isCoordinator() { Address coord=determineCoordinator(); return coord != null && local_addr != null && local_addr.equals(coord); } public MergeId getMergeId() { return impl instanceof CoordGmsImpl? ((CoordGmsImpl)impl).getMergeId() : null; } public void setLogCollectMessages(boolean flag) { log_collect_msgs=flag; } public boolean getLogCollectMessages() { return log_collect_msgs; } 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) // unnecessary ? return; impl=new_impl; if(log.isDebugEnabled()) log.debug(local_addr != null? local_addr + ": " : "" + "changed role to " + new_impl.getClass().getName()); } } public GmsImpl getImpl() { return impl; } public void init() throws Exception { if(view_ack_collection_timeout <= 0) throw new IllegalArgumentException("view_ack_collection_timeout has to be greater than 0"); prev_members=new BoundedList
                (num_prev_mbrs); TP transport=getTransport(); timer=transport.getTimer(); if(timer == null) throw new Exception("timer is null"); if(impl != null) impl.init(); transport.registerProbeHandler(this); } 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; } @ManagedOperation(description="Fetches digests from all members and installs them, unblocking blocked members") public void fixDigests() { if(impl instanceof CoordGmsImpl) ((CoordGmsImpl)impl).fixDigests(); } /** * 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(); Address new_coord=local_addr; if(!mbrs.isEmpty()) new_coord=mbrs.firstElement(); v=new View(new_coord, vid, mbrs); // Update membership (see DESIGN for explanation): tmp_members.set(mbrs); // Update joining list (see DESIGN for explanation) if(new_mbrs != null) { for(Address tmp_mbr: new_mbrs) { if(!joining.contains(tmp_mbr)) joining.addElement(tmp_mbr); } } // Update leaving list (see DESIGN for explanations) if(old_mbrs != null) { for(Address addr: old_mbrs) { if(!leaving.contains(addr)) leaving.add(addr); } } if(suspected_mbrs != null) { for(Address addr:suspected_mbrs) { 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(local_addr + ": 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(this.id, hdr); List
                ackMembers = new ArrayList
                (new_view.getMembers()); if(newMembers != null && !newMembers.isEmpty()) { ackMembers.removeAll(newMembers); } if(!ackMembers.isEmpty()) ack_collector.reset(ackMembers); else ack_collector.reset(null); // 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 { if(!ackMembers.isEmpty()) { ack_collector.waitForAllAcks(view_ack_collection_timeout); if(log.isTraceEnabled()) log.trace(local_addr + ": received all ACKs (" + ack_collector.expectedAcks() + ") from existing members for view " + new_view.getVid()); } } catch(TimeoutException e) { if(log_collect_msgs && log.isWarnEnabled()) { log.warn(local_addr + ": failed to collect all ACKs (expected=" + ack_collector.expectedAcks() + ") for view " + new_view + " after " + view_ack_collection_timeout + "ms, missing ACKs from " + ack_collector.printMissing()); } } 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(local_addr + ": received all ACKs (" + ack_collector.expectedAcks() + ") from joiners for view " + new_view.getVid()); } catch(TimeoutException e) { if(log_collect_msgs && log.isWarnEnabled()) { log.warn(local_addr + ": failed to collect all ACKs (expected=" + ack_collector.expectedAcks() + ") for unicast view " + new_view + " after " + view_ack_collection_timeout + "ms, missing ACKs from " + ack_collector.printMissing()); } } } } 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(this.id, 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) { if(new_view instanceof MergeView) mergeDigest(digest); else setDigest(digest); } if(log.isDebugEnabled()) log.debug(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) { if(log.isWarnEnabled()) log.warn(local_addr + ": not member of view " + new_view + "; discarding it"); 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(Address addr: mbrs) { 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); // we don't need this one anymore } } } } protected Address determineCoordinator() { synchronized(members) { return 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(View view) { return _startFlush(view, 4, 1000L, 5000L); } boolean startFlush(View view, int maxAttempts, long floor, long ceiling) { return _startFlush(view, maxAttempts, floor, ceiling); } protected boolean _startFlush(final View new_view, int maxAttempts, long randomFloor, long randomCeiling) { if(flushInvokerClass != null) { try { Callable invoker = flushInvokerClass.getDeclaredConstructor(View.class).newInstance(new_view); return invoker.call(); } catch (Throwable e) { return false; } } try { 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(local_addr + ": successful GMS flush by coordinator"); } else { if(log.isWarnEnabled()) log.warn(local_addr + ": GMS flush by coordinator failed"); } } return successfulFlush; } catch (Exception e) { return false; } } void stopFlush() { if(flushProtocolInStack) { if(log.isDebugEnabled()) { log.debug(local_addr + ": sending RESUME event"); } up_prot.up(new Event(Event.RESUME)); } } void stopFlush(List
                members) { if(log.isDebugEnabled()){ log.debug(local_addr + ": sending RESUME event"); } up_prot.up(new Event(Event.RESUME,members)); } @SuppressWarnings("unchecked") public Object up(Event evt) { switch(evt.getType()) { case Event.MSG: Message msg=(Message)evt.getArg(); GmsHeader hdr=(GmsHeader)msg.getHeader(this.id); if(hdr == null) break; switch(hdr.type) { case GmsHeader.JOIN_REQ: view_handler.add(new Request(Request.JOIN, hdr.mbr, false, null, hdr.useFlushIfPresent)); break; case GmsHeader.JOIN_REQ_WITH_STATE_TRANSFER: view_handler.add(new Request(Request.JOIN_WITH_STATE_TRANSFER, hdr.mbr, false, null, hdr.useFlushIfPresent)); 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) { return null; } view_handler.add(new Request(Request.LEAVE, hdr.mbr, false)); break; case GmsHeader.LEAVE_RSP: impl.handleLeaveResponse(); break; case GmsHeader.VIEW: View new_view=hdr.view; if(new_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)); impl.handleMergeRequest(msg.getSrc(), hdr.merge_id, hdr.mbrs); 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(local_addr + ": got merge response from " + msg.getSrc() + ", merge_id=" + hdr.merge_id + ", 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_DIGEST: Digest tmp=hdr.my_digest; down_prot.down(new Event(Event.MERGE_DIGEST, tmp)); 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; case GmsHeader.GET_DIGEST_REQ: Digest digest=(Digest)down_prot.down(Event.GET_DIGEST_EVT); if(digest != null) { Digest.Entry entry=digest.get(local_addr); if(entry != null) { // only return my own digest information, but nobody else's ! // https://jira.jboss.org/jira/browse/JGRP-948 Digest retval=new Digest(local_addr, entry.getLow(), entry.getHighestDeliveredSeqno(), entry.getHighestReceivedSeqno()); GmsHeader rsp_hdr=new GmsHeader(GmsHeader.GET_DIGEST_RSP); rsp_hdr.my_digest=retval; Message get_digest_rsp=new Message(msg.getSrc(), null, null); get_digest_rsp.setFlag(Message.OOB); get_digest_rsp.putHeader(this.id, rsp_hdr); down_prot.down(new Event(Event.MSG, get_digest_rsp)); } } break; case GmsHeader.GET_DIGEST_RSP: Digest digest_rsp=hdr.my_digest; impl.handleDigestResponse(msg.getSrc(), digest_rsp); break; default: if(log.isErrorEnabled()) log.error("GmsHeader with type=" + hdr.type + " not known"); } return null; // don't 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)); 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, (Map)evt.getArg())); return null; // don't pass up } return up_prot.up(evt); } @SuppressWarnings("unchecked") public Object down(Event evt) { int type=evt.getType(); switch(type) { case Event.CONNECT: case Event.CONNECT_USE_FLUSH: case Event.CONNECT_WITH_STATE_TRANSFER: case Event.CONNECT_WITH_STATE_TRANSFER_USE_FLUSH: boolean use_flush=type == Event.CONNECT_USE_FLUSH || type == Event.CONNECT_WITH_STATE_TRANSFER_USE_FLUSH; boolean state_transfer=type == Event.CONNECT_WITH_STATE_TRANSFER || type == Event.CONNECT_WITH_STATE_TRANSFER_USE_FLUSH; if(print_local_addr) { PhysicalAddress physical_addr=print_physical_addrs? (PhysicalAddress)down(new Event(Event.GET_PHYSICAL_ADDRESS, local_addr)) : null; System.out.println("\n-------------------------------------------------------------------\n" + "GMS: address=" + local_addr + ", cluster=" + evt.getArg() + (physical_addr != null? ", physical address=" + physical_addr : "") + "\n-------------------------------------------------------------------"); } else { if(log.isDebugEnabled()) { PhysicalAddress physical_addr=print_physical_addrs? (PhysicalAddress)down(new Event(Event.GET_PHYSICAL_ADDRESS, local_addr)) : null; log.debug("address=" + local_addr + ", cluster=" + evt.getArg() + (physical_addr != null? ", physical address=" + physical_addr : "")); } } down_prot.down(evt); if(local_addr == null) if(log.isFatalEnabled()) log.fatal("[CONNECT] local_addr is null"); try { if(state_transfer) impl.joinWithStateTransfer(local_addr, use_flush); else impl.join(local_addr, use_flush); } catch(Throwable e) { return e; } return null; // don't pass down: event has already been 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; case Event.SET_LOCAL_ADDRESS: local_addr=(Address)evt.getArg(); break; } return down_prot.down(evt); } public Map handleProbe(String... keys) { for(String key: keys) { if(key.equals("fix-digests")) { fixDigests(); } } return null; } public String[] supportedKeys() { return new String[]{"fix-digests"}; } /* ------------------------------- 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(this.id, tmphdr); down_prot.down(new Event(Event.MSG, view_ack)); } /* --------------------------- End of Private Methods ------------------------------- */ public static class GmsHeader extends Header { 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; public static final byte GET_DIGEST_REQ=13; public static final byte GET_DIGEST_RSP=14; public static final byte INSTALL_DIGEST=15; 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 Collection mbrs=null; // used with MERGE_REQ boolean useFlushIfPresent; // used when type=JOIN_REQ JoinRsp join_rsp=null; // used when type=JOIN_RSP Digest my_digest=null; // used when type=MERGE_RSP or INSTALL_MERGE_VIEW MergeId 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 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,boolean useFlushIfPresent) { this.type=type; this.mbr=mbr; this.useFlushIfPresent = useFlushIfPresent; } public GmsHeader(byte type, Address mbr) { this(type,mbr,true); } public GmsHeader(byte type, Collection
                mbrs) { this(type); this.mbrs=mbrs; } /** 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 MergeId getMergeId() { return merge_id; } public void setMergeId(MergeId merge_id) { this.merge_id=merge_id; } public boolean isMergeRejected() { return merge_rejected; } public void setMergeRejected(boolean merge_rejected) { this.merge_rejected=merge_rejected; } public String toString() { StringBuilder sb=new StringBuilder("GmsHeader"); sb.append('[' + type2String(type) + ']'); switch(type) { case JOIN_REQ: case LEAVE_REQ: case GET_DIGEST_REQ: sb.append(": mbr=" + mbr); break; case JOIN_RSP: sb.append(": join_rsp=" + join_rsp); break; case VIEW: case VIEW_ACK: sb.append(": view=" + view); break; case MERGE_REQ: sb.append(": merge_id=" + merge_id).append(", mbrs=" + mbrs); break; case MERGE_RSP: sb.append(": view=" + view + ", digest=" + my_digest + ", merge_id=" + merge_id); if(merge_rejected) sb.append(", merge_rejected=" + merge_rejected); break; case INSTALL_MERGE_VIEW: case GET_DIGEST_RSP: case INSTALL_DIGEST: 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"; case INSTALL_MERGE_VIEW_OK: return "INSTALL_MERGE_VIEW_OK"; case GET_DIGEST_REQ: return "GET_DIGEST_REQ"; case GET_DIGEST_RSP: return "GET_DIGEST_RSP"; case INSTALL_DIGEST: return "INSTALL_DIGEST"; default: return ""; } } 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.writeAddresses(mbrs, out); Util.writeStreamable(join_rsp, out); Util.writeStreamable(my_digest, out); Util.writeStreamable(merge_id, out); out.writeBoolean(merge_rejected); out.writeBoolean(useFlushIfPresent); } 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); mbrs=Util.readAddresses(in, ArrayList.class); join_rsp=(JoinRsp)Util.readStreamable(JoinRsp.class, in); my_digest=(Digest)Util.readStreamable(Digest.class, in); merge_id=(MergeId)Util.readStreamable(MergeId.class, in); merge_rejected=in.readBoolean(); useFlushIfPresent=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+=Util.size(mbrs); 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.size(); retval+=Global.BYTE_SIZE; // boolean useFlushIfPresent return retval; } } /** * Class which processes JOIN, LEAVE and MERGE requests. Requests are queued and processed in FIFO order * @author Bela Ban */ class ViewHandler implements Runnable { volatile Thread thread; final Queue queue=new Queue(); // Queue volatile 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(); synchronized void add(Request req) { if(suspended) { if(log.isTraceEnabled()) log.trace("queue is suspended; request " + req + " is discarded"); return; } start(); try { queue.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(); } synchronized void start() { if(queue.closed()) queue.reset(); 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) { queue.close(flush); synchronized(resume_tasks) { for(Future future: resume_tasks.values()) { future.cancel(true); } resume_tasks.clear(); } } /** * Waits until the current request has been processed, then clears the queue and discards new * requests from now on */ public synchronized void suspend(MergeId merge_id) { if(!suspended) { suspended=true; queue.clear(); waitUntilCompleted(MAX_COMPLETION_TIME); queue.close(true); 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); if(log.isTraceEnabled()) log.trace(local_addr + ": view handler for merge_id " + merge_id + " was suspended"); } } public synchronized void resume(MergeId merge_id) { if(!suspended) return; Future future; synchronized(resume_tasks) { future=resume_tasks.remove(merge_id); } if(future != null) future.cancel(true); resumeForce(); } public synchronized void resumeForce() { if(queue.closed()) queue.reset(); suspended=false; if(log.isTraceEnabled()) log.trace("view handler was resumed"); } 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)queue.remove(INTERVAL); // throws a TimeoutException if it runs into timeout requests.add(firstRequest); if(!view_bundling) break; if(queue.size() > 0) { Request nextReq=(Request)queue.peek(); keepGoing=view_bundling && firstRequest.canBeProcessedTogether(nextReq); } else { wait_time=end_time - System.currentTimeMillis(); if(wait_time > 0) queue.waitUntilClosed(wait_time); // misnomer: waits until element has been added or q closed keepGoing=queue.size() > 0 && firstRequest.canBeProcessedTogether((Request)queue.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 queue.size();} public boolean suspended() {return suspended;} public String dumpQueue() { StringBuilder sb=new StringBuilder(); List v=queue.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; 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: impl.merge(firstReq.views); break; default: log.error("request " + firstReq.type + " is unknown; discarded"); } } } /** * 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 MergeId token; final Map tasks; final ViewHandler handler; public Resumer(final MergeId token, final Map t, final ViewHandler handler) { this.token=token; this.tasks=t; this.handler=handler; } public void run() { boolean executed=true; synchronized(tasks) { Future future=tasks.get(token); if(future != null) { future.cancel(false); executed=true; } else { executed=false; } tasks.remove(token); } if(executed) { handler.resume(token); } } } }libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/pbcast/GmsImpl.java0000644000175000017500000001244511647260573027347 0ustar moellermoeller package org.jgroups.protocols.pbcast; import org.jgroups.*; import org.jgroups.logging.Log; import org.jgroups.util.Digest; import org.jgroups.util.MergeId; import java.util.Collection; import java.util.Map; import java.util.Vector; public abstract class GmsImpl { protected final GMS gms; protected final Merger merger; protected final Log log; volatile boolean leaving=false; protected GmsImpl(GMS gms) { this.gms=gms; merger=gms.merger; log=gms.getLog(); } public abstract void join(Address mbr, boolean useFlushIfPresent); public abstract void joinWithStateTransfer(Address local_addr,boolean useFlushIfPresent); public abstract void leave(Address mbr); public void handleJoinResponse(JoinRsp join_rsp) {} public void handleLeaveResponse() {} public void suspect(Address mbr) {} public void unsuspect(Address mbr) {} public void merge(Map views) {} // only processed by coord public void handleMergeRequest(Address sender, MergeId merge_id, Collection mbrs) {} // only processed by coords public void handleMergeResponse(MergeData data, MergeId merge_id) {} // only processed by coords public void handleMergeView(MergeData data, MergeId merge_id) {} // only processed by coords public void handleMergeCancelled(MergeId merge_id) {} // only processed by coords public void handleDigestResponse(Address sender, Digest digest) {} // only processed by coords public void handleMembershipChange(Collection requests) {} public void handleViewChange(View new_view, Digest digest) {} public void init() throws Exception {leaving=false;} public void start() throws Exception {leaving=false;} public void stop() {leaving=true;} protected void sendMergeRejectedResponse(Address sender, MergeId 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.getId(), hdr); if(log.isDebugEnabled()) log.debug("merge 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; Map views; // different view on MERGE boolean useFlushIfPresent; Request(int type, Address mbr, boolean suspected) { this.type=type; this.mbr=mbr; this.suspected=suspected; } Request(int type, Address mbr, boolean suspected, Map views, boolean useFlushPresent) { this(type, mbr, suspected); this.views=views; this.useFlushIfPresent=useFlushPresent; } Request(int type, Address mbr, boolean suspected, Map views) { this(type, mbr, suspected, views, true); } 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(" + views.size() + " views)"; } return ""); 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(); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/pbcast/MergeData.java0000644000175000017500000000376111647260573027631 0ustar moellermoeller 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 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(); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/pbcast/Merger.java0000644000175000017500000007324111647260573027221 0ustar moellermoellerpackage org.jgroups.protocols.pbcast; import org.jgroups.*; import org.jgroups.annotations.GuardedBy; import org.jgroups.logging.Log; import org.jgroups.util.*; import java.util.*; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * Handles merging. Called by CoordGmsImpl and ParticipantGmsImpl * @author Bela Ban */ public class Merger { private final GMS gms; private final Log log; private final MergeTask merge_task=new MergeTask(); /** For MERGE_REQ/MERGE_RSP correlation, contains MergeData elements */ private final ResponseCollector merge_rsps=new ResponseCollector(); /** For GET_DIGEST / DIGEST_RSP correlation */ private final ResponseCollector digest_collector=new ResponseCollector(); /** To serialize access to merge_id */ private final Lock merge_lock=new ReentrantLock(); @GuardedBy("merge_lock") private MergeId merge_id=null; @GuardedBy("merge_canceller_lock") private Future merge_canceller_future=null; private final Lock merge_canceller_lock=new ReentrantLock(); public Merger(GMS gms, Log log) { this.gms=gms; this.log=log; } /** * Invoked upon receiving a MERGE event from the MERGE layer. Starts the merge protocol. * See description of protocol in DESIGN. * @param views A List of different views detected by the merge protocol */ public void merge(Map views) { if(isMergeInProgress()) { if(log.isTraceEnabled()) log.trace(gms.local_addr + ": merge is already running (merge_id=" + merge_id + ")"); return; } // we need the merge *coordinators* not merge participants because not everyone can lead a merge ! Collection
                coords=Util.determineMergeCoords(views); Collection
                merge_participants=Util.determineMergeParticipants(views); Membership tmp=new Membership(coords); // establish a deterministic order, so that coords can elect leader tmp.sort(); Address merge_leader=tmp.elementAt(0); if(log.isDebugEnabled()) log.debug("determining merge leader from " + merge_participants); if(merge_leader.equals(gms.local_addr)) { if(log.isDebugEnabled()) log.debug("I (" + gms.local_addr + ") will be the leader. Starting the merge task for " + merge_participants); merge_task.start(views); } 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. * @param sender The address of the merge leader * @param merge_id The merge ID * @param mbrs The set of members from which we expect responses */ public void handleMergeRequest(Address sender, MergeId merge_id, Collection mbrs) { boolean success=matchMergeId(merge_id) || setMergeId(null, merge_id); if(!success) { if(log.isWarnEnabled()) log.warn(gms.local_addr + ": merge is already in progress"); sendMergeRejectedResponse(sender, merge_id); return; } /* Clears the view handler queue and discards all JOIN/LEAVE/MERGE requests until after the MERGE */ gms.getViewHandler().suspend(merge_id); if(log.isDebugEnabled()) log.debug(gms.local_addr + ": got merge request from " + sender + ", merge_id=" + merge_id + ", mbrs=" + mbrs); // merge the membership of the current view with mbrs List
                members=new LinkedList
                (); if(mbrs != null) { // didn't use a set because we didn't want to change the membership order at this time (although not incorrect) for(Address mbr: mbrs) { if(!members.contains(mbr)) members.add(mbr); } } ViewId tmp_vid=gms.view_id != null? gms.view_id.copy() : null; if(tmp_vid == null) { log.warn("view ID is null; cannot return merge response"); sendMergeRejectedResponse(sender, merge_id); return; } View view=new View(tmp_vid, new Vector
                (members)); //[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 successfulFlush=gms.startFlush(view); if(!successfulFlush) { sendMergeRejectedResponse(sender, merge_id); if(log.isWarnEnabled()) log.warn(gms.local_addr + ": flush failed; sending merge rejected message to "+ sender+ ", merge_id="+ merge_id); cancelMerge(merge_id); return; } Digest digest=fetchDigestsFromAllMembersInSubPartition(members); if(digest.size() == 0) { log.error("failed fetching digests from subpartition members; dropping merge response"); return; } sendMergeResponse(sender, view, digest, merge_id); } public void handleMergeResponse(MergeData data, MergeId merge_id) { if(!matchMergeId(merge_id)) { if(log.isErrorEnabled()) log.error(gms.local_addr + ": this.merge_id (" + this.merge_id + ") is different from merge_id (" + merge_id + ')'); return; } merge_rsps.add(data.getSender(), data); } /** * 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 MergeId merge_id) { if(!matchMergeId(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.getId(), ack_hdr); gms.getDownProtocol().down(new Event(Event.MSG, ack)); } cancelMerge(merge_id); } public void handleMergeCancelled(MergeId merge_id) { try { gms.stopFlush(); } catch(Throwable t) { log.error("stop flush failed", t); } if(matchMergeId(merge_id)) { if(log.isDebugEnabled()) log.debug(gms.local_addr + ": merge " + merge_id + " is cancelled"); cancelMerge(merge_id); } } public void handleDigestResponse(Address sender, Digest digest) { digest_collector.add(sender, digest); } /** * Removes all members from a given view which don't have us in their view * (https://jira.jboss.org/browse/JGRP-1061). Example: *
                     * A: AB
                     * B: AB
                     * C: ABC
                     * 
                * becomes *
                     * A: AB
                     * B: AB
                     * C: C // A and B don't have C in their views
                     * 
                * @param map A map of members and their associated views */ public static void sanitizeViews(Map map) { if(map == null) return; for(Map.Entry entry: map.entrySet()) { Address key=entry.getKey(); Collection
                members=new ArrayList
                (entry.getValue().getMembers()); boolean modified=false; for(Iterator
                it=members.iterator(); it.hasNext();) { Address val=it.next(); if(val.equals(key)) // we can always talk to ourself ! continue; View view=map.get(val); final Collection
                tmp_mbrs=view != null? view.getMembers() : null; if(tmp_mbrs != null && !tmp_mbrs.contains(key)) { it.remove(); modified=true; } } if(modified) { View old_view=entry.getValue(); entry.setValue(new View(old_view.getVid(), members)); } } } /** Send back a response containing view and digest to sender */ private void sendMergeResponse(Address sender, View view, Digest digest, MergeId 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_id=merge_id; hdr.view=view; hdr.my_digest=digest; msg.putHeader(gms.getId(), hdr); if(log.isDebugEnabled()) log.debug(gms.local_addr + ": sending merge response=" + hdr); gms.getDownProtocol().down(new Event(Event.MSG, msg)); } /** * Sends the new view and digest to all subgroup coordinors in coords. Each coord will in turn *
                  *
                1. broadcast 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(Collection
                coords, MergeData combined_merge_data, MergeId merge_id) { if(coords == null || combined_merge_data == null) return; View view=combined_merge_data.view; Digest digest=combined_merge_data.digest; if(view == null || digest == 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 + ": sending merge view " + view.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=view; hdr.my_digest=digest; hdr.merge_id=merge_id; msg.putHeader(gms.getId(), 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 merge view " + view + " in " + (stop - start) + "ms"); } catch(TimeoutException e) { log.warn(gms.local_addr + ": failed to collect all ACKs for merge (" + size + ") for view " + view + " after " + timeout + "ms, missing ACKs from " + gms.merge_ack_collector.printMissing()); } } } protected void sendMergeRejectedResponse(Address sender, MergeId 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.getId(), hdr); if(log.isDebugEnabled()) log.debug("merge response=" + hdr); gms.getDownProtocol().down(new Event(Event.MSG, msg)); } private void sendMergeCancelledMessage(Collection
                coords, MergeId merge_id) { if(coords == null || 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.getId(), hdr); if(log.isDebugEnabled()) log.debug(gms.local_addr + ": sending cancel merge to " + coord); gms.getDownProtocol().down(new Event(Event.MSG, msg)); } } /** * Multicasts a GET_DIGEST_REQ to all current members and waits for all responses (GET_DIGEST_RSP) or N ms. * @return */ private Digest fetchDigestsFromAllMembersInSubPartition(List
                current_mbrs) { if(current_mbrs == null) return null; GMS.GmsHeader hdr=new GMS.GmsHeader(GMS.GmsHeader.GET_DIGEST_REQ); Message get_digest_req=new Message(); get_digest_req.setFlag(Message.OOB); get_digest_req.putHeader(gms.getId(), hdr); long max_wait_time=gms.merge_timeout > 0? gms.merge_timeout / 2 : 2000L; digest_collector.reset(current_mbrs); // add my own digest first Digest digest=(Digest)gms.getDownProtocol().down(Event.GET_DIGEST_EVT); digest_collector.add(gms.local_addr, digest); gms.getDownProtocol().down(new Event(Event.MSG, get_digest_req)); digest_collector.waitForAllResponses(max_wait_time); if(log.isDebugEnabled()) { if(digest_collector.hasAllResponses()) log.debug(gms.local_addr + ": fetched all digests for " + current_mbrs); else log.debug(gms.local_addr + ": fetched incomplete digests (after timeout of " + max_wait_time + ") ms for " + current_mbrs); } Map responses=new HashMap(digest_collector.getResults()); MutableDigest retval=new MutableDigest(responses.size()); for(Digest dig: responses.values()) { if(dig != null) retval.add(dig); } return retval; } /** * Fetches the digests from all members and installs them again. Used only for diagnosis and support; don't * use this otherwise ! */ void fixDigests() { Digest digest=fetchDigestsFromAllMembersInSubPartition(gms.view.getMembers()); Message msg=new Message(); GMS.GmsHeader hdr=new GMS.GmsHeader(GMS.GmsHeader.INSTALL_DIGEST); hdr.my_digest=digest; msg.putHeader(gms.getId(), hdr); gms.getDownProtocol().down(new Event(Event.MSG, msg)); } void stop() { merge_task.stop(); } void cancelMerge(MergeId id) { if(setMergeId(id, null)) { merge_task.stop(); merge_rsps.reset(); gms.getViewHandler().resume(id); } } private boolean setMergeId(MergeId expected, MergeId new_value) { merge_lock.lock(); try { boolean match=Util.match(this.merge_id, expected); if(match) { this.merge_id=new_value; stopMergeCanceller(); if(this.merge_id != null) startMergeCanceller(); } return match; } finally { merge_lock.unlock(); } } /** Only used for testing, might get removed any time. Do not use ! */ public MergeId getMergeId() { merge_lock.lock(); try { return merge_id; } finally { merge_lock.unlock(); } } private boolean isMergeInProgress() { merge_lock.lock(); try { return merge_id != null; } finally { merge_lock.unlock(); } } private boolean matchMergeId(MergeId id) { merge_lock.lock(); try { return Util.match(this.merge_id, id); } finally { merge_lock.unlock(); } } private void startMergeCanceller() { merge_canceller_lock.lock(); try { if(merge_canceller_future == null || merge_canceller_future.isDone()) { MergeCanceller task=new MergeCanceller(this.merge_id); merge_canceller_future=gms.timer.schedule(task, (long)(gms.merge_timeout * 1.5), 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(); } } /** * 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. */ class MergeTask implements Runnable { private Thread thread=null; /** List of all subpartition coordinators and their members */ private final ConcurrentMap> coords=Util.createConcurrentMap(8, 0.75f, 8); /** * @param views Guaranteed to be non-null and to have >= 2 members, or else this thread would not be started */ public synchronized void start(Map views) { if(thread == null || thread.isAlive()) { this.coords.clear(); // now remove all members which don't have us in their view, so RPCs won't block (e.g. FLUSH) // https://jira.jboss.org/browse/JGRP-1061 sanitizeViews(views); // Add all different coordinators of the views into the hashmap and sets their members: Collection
                coordinators=Util.determineMergeCoords(views); for(Address coord: coordinators) { View view=views.get(coord); if(view != null) this.coords.put(coord, new ArrayList
                (view.getMembers())); } // For the merge participants which are not coordinator, we simply add them, and the associated // membership list consists only of themselves Collection
                merge_participants=Util.determineMergeParticipants(views); merge_participants.removeAll(coordinators); for(Address merge_participant: merge_participants) { Collection
                tmp=new ArrayList
                (); tmp.add(merge_participant); coords.putIfAbsent(merge_participant, tmp); } thread=gms.getThreadFactory().newThread(this, "MergeTask"); thread.setDaemon(true); thread.start(); } } public synchronized void stop() { Thread tmp=thread; if(thread != null && thread.isAlive()) tmp.interrupt(); thread=null; } /** Runs the merge protocol as a leader */ public void run() { // 1. Generate merge_id MergeId new_merge_id=MergeId.create(gms.local_addr); Collection
                coordsCopy=null; try { boolean success=setMergeId(null, new_merge_id); if(!success) { log.warn("failed to set my own merge_id (" + merge_id + ") to " + new_merge_id); return; } coordsCopy=new ArrayList
                (coords.keySet()); /* 2. Fetch the current Views/Digests from all subgroup coordinators */ success=getMergeDataFromSubgroupCoordinators(coords, new_merge_id, gms.merge_timeout); if(!success) throw new Exception("merge leader did not get data from all partition coordinators " + coords.keySet()); /* 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) */ removeRejectedMergeRequests(coords.keySet()); if(merge_rsps.size() == 0) throw new Exception("did not get any merge responses from partition coordinators"); if(!coords.keySet().contains(gms.local_addr)) // another member might have invoked a merge req on us before we got there... throw new Exception("merge leader rejected merge request"); /* 4. Combine all views and digests into 1 View/1 Digest */ Vector merge_data=new Vector(merge_rsps.getResults().values()); MergeData combined_merge_data=consolidateMergeData(merge_data); if(combined_merge_data == null) throw new Exception("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.keySet(), combined_merge_data, new_merge_id); } catch(Throwable ex) { if(log.isWarnEnabled()) log.warn(gms.local_addr + ": " + ex.getLocalizedMessage() + ", merge is cancelled"); sendMergeCancelledMessage(coordsCopy, new_merge_id); } finally { gms.getViewHandler().resume(new_merge_id); 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(); if(log.isDebugEnabled()) log.debug(gms.local_addr + ": merge leader completed merge task"); thread=null; } } /** * 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 map of coordinatgor addresses and associated membership lists * @param new_merge_id The new merge id * @param timeout Max number of msecs to wait for the merge responses from the subgroup coords */ private boolean getMergeDataFromSubgroupCoordinators(Map> coords, MergeId new_merge_id, long timeout) { boolean gotAllResponses; long start=System.currentTimeMillis(); merge_rsps.reset(coords.keySet()); if(log.isDebugEnabled()) log.debug(gms.local_addr + ": sending MERGE_REQ to " + coords.keySet()); for(Map.Entry> entry: coords.entrySet()) { Address coord=entry.getKey(); Collection

                mbrs=entry.getValue(); Message msg=new Message(coord, null, null); msg.setFlag(Message.OOB); GMS.GmsHeader hdr=new GMS.GmsHeader(GMS.GmsHeader.MERGE_REQ, mbrs); hdr.mbr=gms.local_addr; hdr.merge_id=new_merge_id; msg.putHeader(gms.getId(), hdr); gms.getDownProtocol().down(new Event(Event.MSG, msg)); } // wait until num_rsps_expected >= num_rsps or timeout elapsed merge_rsps.waitForAllResponses(timeout); gotAllResponses=merge_rsps.hasAllResponses(); long stop=System.currentTimeMillis(); if(log.isDebugEnabled()) log.debug(gms.local_addr + ": collected " + merge_rsps.size() + " merge response(s) in " + (stop-start) + " ms"); return gotAllResponses; } /** Removed rejected merge requests from merge_rsps and coords. This method has a lock on merge_rsps */ private void removeRejectedMergeRequests(Collection
                coords) { for(Map.Entry entry: merge_rsps.getResults().entrySet()) { Address member=entry.getKey(); MergeData data=entry.getValue(); if(data.merge_rejected) { if(data.getSender() != null) coords.remove(data.getSender()); merge_rsps.remove(member); } } } /** * 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) { long logical_time=0; // for new_vid Membership new_mbrs=new Membership(); Vector subgroups=new Vector(11); // contains a list of Views, each View is a subgroup for(MergeData tmp_data: merge_rsps) { View tmp_view=tmp_data.getView(); if(tmp_view != null) { ViewId 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(); Address 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 ViewId new_vid=new ViewId(new_coord, logical_time + 1); // determine the new view MergeView new_view=new MergeView(new_vid, new_mbrs.getMembers(), subgroups); // 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 view=" + new_view + "\nconsolidated digest=" + new_digest); return new MergeData(gms.local_addr, new_view, new_digest); } /** * 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(); } } private class MergeCanceller implements Runnable { private final MergeId my_merge_id; MergeCanceller(MergeId my_merge_id) { this.my_merge_id=my_merge_id; } public void run() { cancelMerge(my_merge_id); } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/pbcast/NAKACK.java0000644000175000017500000017141111647260573026726 0ustar moellermoellerpackage org.jgroups.protocols.pbcast; import org.jgroups.*; import org.jgroups.annotations.*; import org.jgroups.conf.PropertyConverters; import org.jgroups.protocols.TP; import org.jgroups.stack.*; import org.jgroups.util.BoundedList; import org.jgroups.util.Digest; import org.jgroups.util.TimeScheduler; import org.jgroups.util.Util; 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.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 */ @MBean(description="Reliable transmission multipoint FIFO protocol") @DeprecatedProperty(names={"max_xmit_size", "eager_lock_release", "stats_list_size", "max_xmit_buf_size", "enable_xmit_time_stats"}) public class NAKACK extends Protocol implements Retransmitter.RetransmitCommand, NakReceiverWindow.Listener, TP.ProbeHandler { /** 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 static final int NUM_REBROADCAST_MSGS=3; /* ----------------------------------------------------- Properties --------------------- ------------------------------------ */ @Property(name="retransmit_timeout", converter=PropertyConverters.LongArray.class, description="Timeout before requesting retransmissions. Default is 600, 1200, 2400, 4800") private long[] retransmit_timeouts= { 600, 1200, 2400, 4800 }; // time(s) to wait before requesting retransmission @Deprecated @Property(description="Garbage collection lag") private int gc_lag=20; // number of msgs garbage collection lags behind @Property(description="Max number of messages to be removed from a NakReceiverWindow. This property might " + "get removed anytime, so don't use it !") private int max_msg_batch_size=20000; /** * Retransmit messages using multicast rather than unicast. This has the advantage that, if many receivers * lost a message, the sender only retransmits once */ @Property(description="Retransmit messages using multicast rather than unicast") 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 */ @Property(description="Use a multicast to request retransmission of missing messages. Default is false") 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 */ @Property(description="Ask a random member for retransmission of a missing message. Default is 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 */ @Property(description="The first value (in milliseconds) to use in the exponential backoff. Enabled if greater than 0. Default is 0") private long exponential_backoff=0; /** * If enabled, we use statistics gathered from actual retransmission times * to compute the new retransmission times */ @Property(description="Use statistics gathered from actual retransmission times to compute new retransmission times. Default is false") private boolean use_stats_for_retransmission=false; @Property(description="Whether to use the old retransmitter which retransmits individual messages or the new one " + "which uses ranges of retransmitted messages. Default is true. Note that this property will be removed in 3.0; " + "it is only used to switch back to the old (and proven) retransmitter mechanism if issues occur") private boolean use_range_based_retransmitter=true; /** * 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. */ @Property(description="Should messages delivered to application be discarded") private boolean discard_delivered_msgs=false; @Property(description="Size of retransmission history. Default is 50 entries") private int xmit_history_max_size=50; @Property(description="Timeout to rebroadcast messages. Default is 2000 msec") private long max_rebroadcast_timeout=2000; /** * When not finding a message on an XMIT request, include the last N * stability messages in the error message */ @Property(description="Should stability history be printed if we fail in retransmission. Default is false") protected boolean print_stability_history_on_failed_xmit=false; /** If true, logs messages discarded because received from other members */ @Property(description="discards warnings about promiscuous traffic") private boolean log_discard_msgs=true; @Property(description="If true, trashes warnings about retransmission messages not found in the xmit_table (used for testing)") private boolean log_not_found_msgs=true; @Property(description="Number of rows of the matrix in the retransmission table (only for experts)",writable=false) int xmit_table_num_rows=5; @Property(description="Number of elements of a row of the matrix in the retransmission table (only for experts). " + "The capacity of the matrix is xmit_table_num_rows * xmit_table_msgs_per_row",writable=false) int xmit_table_msgs_per_row=10000; @Property(description="Resize factor of the matrix in the retransmission table (only for experts)",writable=false) double xmit_table_resize_factor=1.2; @Property(description="Number of milliseconds after which the matrix in the retransmission table " + "is compacted (only for experts)",writable=false) long xmit_table_max_compaction_time=10 * 60 * 1000; /* -------------------------------------------------- JMX ---------------------------------------------------------- */ @ManagedAttribute(description="Number of retransmit requests received") private long xmit_reqs_received; @ManagedAttribute(description="Number of retransmit requests sent") private long xmit_reqs_sent; @ManagedAttribute(description="Number of retransmit responses received") private long xmit_rsps_received; @ManagedAttribute(description="Number of retransmit responses sent") private long xmit_rsps_sent; @ManagedAttribute(description="Number of missing messages received") private long missing_msgs_received; /** Captures stats on XMIT_REQS, XMIT_RSPS per sender */ private ConcurrentMap sent=Util.createConcurrentMap(); /** Captures stats on XMIT_REQS, XMIT_RSPS per receiver */ private ConcurrentMap received=Util.createConcurrentMap(); /** Per-sender map of seqnos and timestamps, to keep track of avg times for retransmission of messages */ private final ConcurrentMap> xmit_stats=Util.createConcurrentMap(); /** Maintains a list of the last N retransmission times (duration to retransmit a message) for all members */ private final ConcurrentMap> xmit_times_history=Util.createConcurrentMap(); /** * 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(); /** Keeps the last 50 retransmit requests */ private final BoundedList xmit_history=new BoundedList(50); /* ------------------------------------------------- Fields ------------------------------------------------------------------------- */ 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(); /** Map to store sent and received messages (keyed by sender) */ private final ConcurrentMap xmit_table=Util.createConcurrentMap(); private volatile boolean leaving=false; private volatile boolean running=false; private TimeScheduler timer=null; 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; /** BoundedList, keeps the last 10 stability messages */ protected final BoundedList stability_msgs=new BoundedList(10); /** Keeps a bounded list of the last N digest sets */ protected final BoundedList digest_history=new BoundedList(10); public NAKACK() { } @Deprecated public static int getUndeliveredMessages() { return 0; } 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;} @ManagedAttribute(description="Total number of missing messages") public int getPendingRetransmissionRequests() { int num=0; for(NakReceiverWindow win: xmit_table.values()) { num+=win.getPendingXmits(); } return num; } @ManagedAttribute public int getXmitTableSize() { int num=0; for(NakReceiverWindow win: xmit_table.values()) { num+=win.size(); } return num; } @ManagedAttribute public long getCurrentSeqno() {return seqno;} @ManagedOperation public String printRetransmitStats() { StringBuilder sb=new StringBuilder(); for(Map.Entry entry: xmit_table.entrySet()) sb.append(entry.getKey()).append(": ").append(entry.getValue().printRetransmitStats()).append("\n"); return sb.toString(); } public int getReceivedTableSize() { return getPendingRetransmissionRequests(); } /** * Please don't use this method; it is only provided for unit testing ! * @param mbr * @return */ public NakReceiverWindow getWindow(Address mbr) { return xmit_table.get(mbr); } /** * Only used for unit tests, don't use ! * @param timer */ public void setTimer(TimeScheduler timer) { this.timer=timer; } public void resetStats() { xmit_reqs_received=xmit_reqs_sent=xmit_rsps_received=xmit_rsps_sent=missing_msgs_received=0; sent.clear(); received.clear(); stability_msgs.clear(); digest_history.clear(); xmit_history.clear(); } public void init() throws Exception { 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"); } } TP transport=getTransport(); if(transport != null) { transport.registerProbeHandler(this); if(!transport.supportsMulticasting()) { if(use_mcast_xmit) { log.warn("use_mcast_xmit should not be used because the transport (" + transport.getName() + ") does not support IP multicasting; setting use_mcast_xmit to false"); use_mcast_xmit=false; } if(use_mcast_xmit_req) { log.warn("use_mcast_xmit_req should not be used because the transport (" + transport.getName() + ") does not support IP multicasting; setting use_mcast_xmit_req to false"); use_mcast_xmit_req=false; } } } } @Deprecated public int getGcLag() { return gc_lag; } @Deprecated 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; } @Deprecated public static int getMaxXmitBufSize() { return 0; } @Deprecated public static void setMaxXmitBufSize(int max_xmit_buf_size) { ; } /** * * @return * @deprecated removed in 2.6 */ public static long getMaxXmitSize() { return -1; } /** * * @param max_xmit_size * @deprecated removed in 2.6 */ public void setMaxXmitSize(long max_xmit_size) { } public void setLogDiscardMessages(boolean flag) { log_discard_msgs=flag; } public void setLogDiscardMsgs(boolean flag) { setLogDiscardMessages(flag); } public boolean getLogDiscardMessages() { return log_discard_msgs; } public Map dumpStats() { Map retval=super.dumpStats(); retval.put("msgs", printMessages()); return retval; } public String printStats() { StringBuilder sb=new StringBuilder(); sb.append("sent:\n"); for(Iterator> it=sent.entrySet().iterator(); it.hasNext();) { Map.Entry entry=it.next(); Object key=entry.getKey(); if(key == null || key == Global.NULL) key=""; StatsEntry val=entry.getValue(); sb.append(key).append(": ").append(val).append("\n"); } sb.append("\nreceived:\n"); for(Iterator> it=received.entrySet().iterator(); it.hasNext();) { Map.Entry entry=it.next(); Object key=entry.getKey(); if(key == null || key == Global.NULL) key=""; StatsEntry val=entry.getValue(); sb.append(key).append(": ").append(val).append("\n"); } sb.append("\nStability messages received\n"); sb.append(printStabilityMessages()).append("\n"); return sb.toString(); } @ManagedOperation(description="TODO") 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(); } @ManagedOperation(description="Keeps information about the last N times a digest was set or merged") public String printDigestHistory() { StringBuilder sb=new StringBuilder(local_addr + ":\n"); for(String tmp: digest_history) sb.append(tmp).append("\n"); return sb.toString(); } @ManagedOperation(description="TODO") 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(); } @ManagedOperation(description="Returns the sizes of all NakReceiverWindow.RetransmitTables") public String printRetransmitTableSizes() { StringBuilder sb=new StringBuilder(); for(Map.Entry entry: xmit_table.entrySet()) { NakReceiverWindow win=entry.getValue(); sb.append(entry.getKey() + ": ").append(win.getRetransmiTableSize()) .append(" (capacity=" + win.getRetransmitTableCapacity()) .append(", fill factor=" + win.getRetransmitTableFillFactor() + "%)\n"); } return sb.toString(); } @ManagedOperation(description="Compacts the retransmission tables") public void compact() { for(Map.Entry entry: xmit_table.entrySet()) { NakReceiverWindow win=entry.getValue(); win.compact(); } } @ManagedAttribute 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; } @ManagedAttribute 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.OVERWRITE_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"); running=true; leaving=false; } public void stop() { running=false; reset(); // clears sent_msgs and destroys all NakReceiverWindows } /** * 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() || msg.isFlagSet(Message.NO_RELIABILITY)) 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.OVERWRITE_DIGEST: overwriteDigest((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); view=tmp_view; 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); 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.SET_LOCAL_ADDRESS: local_addr=(Address)evt.getArg(); 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(); if(msg.isFlagSet(Message.NO_RELIABILITY)) break; NakAckHeader hdr=(NakAckHeader)msg.getHeader(this.id); if(hdr == null) break; // pass up (e.g. unicast msg) if(!is_server) { // discard messages while not yet server (i.e., until JOIN has returned) if(log.isTraceEnabled()) log.trace("message " + msg.getSrc() + "::" + hdr.seqno + " 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: handleXmitRsp(msg, hdr); 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.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(!running) { if(log.isTraceEnabled()) log.trace("[" + local_addr + "] discarded message as we're not in the 'running' state, message: " + msg); return; } long msg_id; NakReceiverWindow win=xmit_table.get(local_addr); if(win == null) { // discard message if there is no entry for local_addr if(log.isWarnEnabled() && log_discard_msgs) log.warn(local_addr + ": discarded message from " + local_addr + " with no window, my view is " + view); return; } if(msg.getSrc() == null) 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(this.id, NakAckHeader.createMessageHeader(msg_id)); win.add(msg_id, msg); 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(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; } 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 + ": dropped message from " + sender + " (not in table " + xmit_table.keySet() +"), view=" + view); return; } boolean loopback=local_addr.equals(sender); boolean added=loopback || win.add(hdr.seqno, msg); if(added && log.isTraceEnabled()) log.trace(new StringBuilder().append(local_addr).append(": received ").append(sender).append('#').append(hdr.seqno)); // OOB msg is passed up. When removed, we discard it. Affects ordering: http://jira.jboss.com/jira/browse/JGRP-379 if(added && msg.isFlagSet(Message.OOB)) { if(loopback) msg=win.get(hdr.seqno); // we *have* to get a message, because loopback means we didn't add it to win ! if(msg != null && msg.isFlagSet(Message.OOB)) { if(msg.setTransientFlagIfAbsent(Message.OOB_DELIVERED)) up_prot.up(new Event(Event.MSG, msg)); } } // 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; } boolean remove_msgs=discard_delivered_msgs && !loopback; boolean released_processing=false; try { while(true) { // we're removing a msg and set processing to false (if null) *atomically* (wrt to add()) List msgs=win.removeMany(processing, remove_msgs, max_msg_batch_size); if(msgs == null || msgs.isEmpty()) { released_processing=true; if(rebroadcasting) checkForRebroadcasts(); return; } for(final Message msg_to_deliver: msgs) { // discard OOB msg if it has already been delivered (http://jira.jboss.com/jira/browse/JGRP-379) if(msg_to_deliver.isFlagSet(Message.OOB) && !msg_to_deliver.setTransientFlagIfAbsent(Message.OOB_DELIVERED)) continue; //msg_to_deliver.removeHeader(getName()); // Changed by bela Jan 29 2003: not needed (see above) try { up_prot.up(new Event(Event.MSG, msg_to_deliver)); } catch(Throwable t) { log.error("couldn't deliver message " + msg_to_deliver, t); } } } } finally { // 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) { if(first_seqno > last_seqno) return; 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(stats) { xmit_reqs_received+=last_seqno - first_seqno +1; updateStats(received, xmit_requester, 1, 0, 0); } 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"); // don't print the table unless we are in trace mode because it can be LARGE if (log.isTraceEnabled()) { sb.append(":\n").append(printMessages()); } if(print_stability_history_on_failed_xmit) { sb.append(" (stability history:\n").append(printStabilityHistory()); } log.error(sb.toString()); } return; } long diff=last_seqno - first_seqno +1; if(diff >= 10) { List msgs=win.get(first_seqno, last_seqno); if(msgs != null) { for(Message msg: msgs) sendXmitRsp(xmit_requester, msg); } } else { for(long i=first_seqno; i <= last_seqno; i++) { Message msg=win.get(i); if(msg == null) { if(log.isWarnEnabled() && log_not_found_msgs && !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.toString()); } continue; } sendXmitRsp(xmit_requester, msg); } } } private void cancelRebroadcasting() { rebroadcast_lock.lock(); try { rebroadcasting=false; rebroadcast_done.signalAll(); } finally { rebroadcast_lock.unlock(); } } private static void updateStats(ConcurrentMap map, Address key, int req, int rsp, int missing) { StatsEntry entry=map.get(key); if(entry == null) { entry=new StatsEntry(); StatsEntry tmp=map.putIfAbsent(key, entry); if(tmp != null) entry=tmp; } 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 */ private void sendXmitRsp(Address dest, Message msg) { if(msg == null) { if(log.isErrorEnabled()) log.error("message is null, cannot send retransmission"); return; } if(stats) { xmit_rsps_sent++; updateStats(sent, dest, 0, 1, 0); } if(msg.getSrc() == null) msg.setSrc(local_addr); if(use_mcast_xmit) { // we simply send the original multicast message down_prot.down(new Event(Event.MSG, msg)); return; } Message xmit_msg=msg.copy(true, true); // copy payload and headers xmit_msg.setDest(dest); NakAckHeader hdr=(NakAckHeader)xmit_msg.getHeader(id); hdr.type=NakAckHeader.XMIT_RSP; // change the type in the copy from MSG --> XMIT_RSP down_prot.down(new Event(Event.MSG, xmit_msg)); } private void handleXmitRsp(Message msg, NakAckHeader hdr) { if(msg == null) return; try { if(stats) { xmit_rsps_received++; updateStats(received, msg.getSrc(), 0, 1, 0); } msg.setDest(null); hdr.type=NakAckHeader.MSG; // change the type back from XMIT_RSP --> MSG up(new Event(Event.MSG, msg)); if(rebroadcasting) checkForRebroadcasts(); } 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(); // Cannot ask for 0 to be retransmitted because the first seqno in NAKACK and UNICAST(2) is always 1 ! // Also, we need to ask for retransmission of my_high+1, because we already *have* my_high, and don't // need it, so the retransmission range is [my_high+1 .. their_high]: *exclude* my_high, but *include* // their_high my_high=Math.max(1, my_entry.getHighest() +1); if(their_high > my_high) { if(log.isTraceEnabled()) log.trace("[" + local_addr + "] fetching " + my_high + "-" + their_high + " from " + sender); 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(); } } } protected void checkForRebroadcasts() { 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(); } } /** * Remove old members from NakReceiverWindows. 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) { for(Address member: xmit_table.keySet()) { if(!new_members.contains(member)) { if(local_addr != null && local_addr.equals(member)) continue; NakReceiverWindow win=xmit_table.remove(member); win.destroy(); if(log.isDebugEnabled()) log.debug("removed " + member + " from xmit_table (not member anymore)"); } } } /** * Returns a message digest: for each member P the lowest, highest delivered and highest received seqno is added */ public Digest getDigest() { final Map map=new HashMap(); for(Map.Entry entry: xmit_table.entrySet()) { Address sender=entry.getKey(); // guaranteed to be non-null (CCHM) NakReceiverWindow win=entry.getValue(); // guaranteed to be non-null (CCHM) long[] digest=win.getDigest(); map.put(sender, new Digest.Entry(digest[0], digest[1], digest[2])); } 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) { setDigest(digest, false); } /** * For all members of the digest, adjust the NakReceiverWindows in xmit_table. 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) * if the digest's seqno is greater than the seqno in the window. */ private void mergeDigest(Digest digest) { setDigest(digest, true); } /** * Overwrites existing entries, but does NOT remove entries not found in the digest * @param digest */ private void overwriteDigest(Digest digest) { if(digest == null) return; StringBuilder sb=new StringBuilder("\n[overwriteDigest()]\n"); 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) continue; long highest_delivered_seqno=val.getHighestDeliveredSeqno(); long low_seqno=val.getLow(); NakReceiverWindow win=xmit_table.get(sender); if(win != null) { if(local_addr.equals(sender)) { win.setHighestDelivered(highest_delivered_seqno); continue; // don't destroy my own window } xmit_table.remove(sender); win.destroy(); // stops retransmission } win=createNakReceiverWindow(sender, highest_delivered_seqno, low_seqno); xmit_table.put(sender, win); } sb.append("\n").append("resulting digest: " + getDigest()); digest_history.add(sb.toString()); if(log.isDebugEnabled()) log.debug(sb.toString()); } /** * Sets or merges the digest. If there is no entry for a given member in xmit_table, create a new NakReceiverWindow. * Else skip the existing entry, unless it is a merge. In this case, skip the existing entry if its seqno is * greater than or equal to the one in the digest, or reset the window and create a new one if not. * @param digest The digest * @param merge Whether to merge the new digest with our own, or not */ private void setDigest(Digest digest, boolean merge) { if(digest == null) return; StringBuilder sb=new StringBuilder(merge? "\n[mergeDigest()]\n" : "\n[setDigest()]\n"); sb.append("existing digest: " + getDigest()).append("\nnew digest: " + digest); boolean set_own_seqno=false; for(Map.Entry entry: digest.getSenders().entrySet()) { Address sender=entry.getKey(); Digest.Entry val=entry.getValue(); if(sender == null || val == null) continue; long highest_delivered_seqno=val.getHighestDeliveredSeqno(); long low_seqno=val.getLow(); NakReceiverWindow win=xmit_table.get(sender); if(win != null) { // We only reset the window if its seqno is lower than the seqno shipped with the digest. Also, we // don't reset our own window (https://jira.jboss.org/jira/browse/JGRP-948, comment 20/Apr/09 03:39 AM) if(!merge || (local_addr != null && local_addr.equals(sender)) // never overwrite our own entry || win.getHighestDelivered() >= highest_delivered_seqno) // my seqno is >= digest's seqno for sender continue; xmit_table.remove(sender); win.destroy(); // stops retransmission if(sender.equals(local_addr)) { // Adjust the seqno: https://jira.jboss.org/browse/JGRP-1251 seqno_lock.lock(); try { seqno=highest_delivered_seqno; set_own_seqno=true; } finally { seqno_lock.unlock(); } } } win=createNakReceiverWindow(sender, highest_delivered_seqno, low_seqno); xmit_table.put(sender, win); } sb.append("\n").append("resulting digest: " + getDigest()); if(set_own_seqno) sb.append("\nnew seqno for " + local_addr + ": " + seqno); digest_history.add(sb.toString()); if(log.isDebugEnabled()) log.debug(sb.toString()); } private NakReceiverWindow createNakReceiverWindow(Address sender, long initial_seqno, long lowest_seqno) { NakReceiverWindow win=new NakReceiverWindow(sender, this, initial_seqno, lowest_seqno, timer, use_range_based_retransmitter, xmit_table_num_rows, xmit_table_msgs_per_row, xmit_table_resize_factor, xmit_table_max_compaction_time, false); 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)); if(stats) win.setListener(this); return win; } /** * 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, final 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=NakAckHeader.createXmitRequestHeader(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(this.id, 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()); } down_prot.down(new Event(Event.MSG, retransmit_msg)); if(stats) { xmit_reqs_sent+=last_seqno - first_seqno +1; updateStats(sent, sender, 1, 0, 0); } xmit_history.add(sender + ": " + first_seqno + "-" + last_seqno); } /* ------------------- End of Interface Retransmitter.RetransmitCommand -------------------- */ /* ----------------------- Interface NakReceiverWindow.Listener ---------------------- */ public void missingMessageReceived(long seqno, final 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 // needs to be synchronized because we rely on the previous value for computation of the next value 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(stats) { missing_msgs_received++; updateStats(received, original_sender, 0, 0, 1); } } /** Called when a message gap is detected */ public void messageGapDetected(long from, long to, Address src) { ; } /* ------------------- End of Interface NakReceiverWindow.Listener ------------------- */ private void reset() { seqno_lock.lock(); try { seqno=0; } finally { seqno_lock.unlock(); } for(NakReceiverWindow win: xmit_table.values()) { win.destroy(); } xmit_table.clear(); } @ManagedOperation(description="TODO") public String printMessages() { StringBuilder ret=new StringBuilder(local_addr + ":\n"); for(Map.Entry entry: xmit_table.entrySet()) { Address addr=entry.getKey(); NakReceiverWindow win=entry.getValue(); ret.append(addr).append(": ").append(win.toString()).append('\n'); } return ret.toString(); } @ManagedOperation(description="TODO") 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(); } @ManagedOperation(description="TODO") 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(); } @ManagedOperation(description="TODO") 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(); } @ManagedOperation(description="Prints the last N retransmission requests") public String printXmitHistory() { StringBuilder sb=new StringBuilder(); for(String req: xmit_history) sb.append(req).append("\n"); return sb.toString(); } @ManagedAttribute 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; } @ManagedAttribute 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; } } // ProbeHandler interface public Map handleProbe(String... keys) { Map retval=new HashMap(); for(String key: keys) { if(key.equals("digest-history")) retval.put(key, printDigestHistory()); if(key.equals("dump-digest")) retval.put(key, "\n" + printMessages()); } return retval; } // ProbeHandler interface public String[] supportedKeys() { return new String[]{"digest-history", "dump-digest"}; } 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(); } } /* ----------------------------- End of Private Methods ------------------------------------ */ }libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/pbcast/NakAckHeader.java0000644000175000017500000001042111647260573030230 0ustar moellermoeller 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.Util; import java.io.*; /** * @author Bela Ban */ public class NakAckHeader extends Header { 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) Address sender; // the original sender of the message (for XMIT_REQ) public NakAckHeader() { } public static NakAckHeader createMessageHeader(long seqno) { return new NakAckHeader(MSG, seqno); } public static NakAckHeader createXmitRequestHeader(long low, long high, Address orginal_sender) { return new NakAckHeader(XMIT_REQ, low, high, orginal_sender); } public static NakAckHeader createXmitResponseHeader() { return new NakAckHeader(XMIT_RSP, -1); } /** * Constructor for regular messages or XMIT responses */ private NakAckHeader(byte type, long seqno) { this.type=type; this.seqno=seqno; } /** * Constructor for retransmit requests (XMIT_REQs) (low and high define the range of msgs) */ private NakAckHeader(byte type, long low, long high, Address sender) { this.type=type; range=new Range(low, high); this.sender=sender; } public byte getType() { return type; } public long getSeqno() { return seqno; } public Range getRange() { return range; } public Address getSender() { return sender; } public void writeTo(DataOutputStream out) throws IOException { out.writeByte(type); switch(type) { case MSG: case XMIT_RSP: out.writeLong(seqno); break; case XMIT_REQ: Util.writeStreamable(range, out); Util.writeAddress(sender, out); break; } } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { type=in.readByte(); switch(type) { case MSG: case XMIT_RSP: seqno=in.readLong(); break; case XMIT_REQ: range=(Range)Util.readStreamable(Range.class, in); sender=Util.readAddress(in); break; } } public int size() { int retval=Global.BYTE_SIZE; // type switch(type) { case MSG: case XMIT_RSP: return retval + Global.LONG_SIZE; // seqno case XMIT_REQ: 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; } return retval; } public NakAckHeader copy() { NakAckHeader ret=new NakAckHeader(); ret.type=type; ret.seqno=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: case XMIT_RSP: // seqno and sender ret.append(", seqno=").append(seqno); break; case XMIT_REQ: // range and sender if(range != null) ret.append(", range=" + range); break; } if(sender != null) ret.append(", sender=").append(sender); ret.append(']'); return ret.toString(); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/pbcast/ParticipantGmsImpl.java0000644000175000017500000001426411647260573031547 0ustar moellermoeller package org.jgroups.protocols.pbcast; import org.jgroups.*; import org.jgroups.util.Promise; import org.jgroups.util.Digest; import org.jgroups.util.MergeId; import java.util.Vector; import java.util.Collection; import java.util.LinkedHashSet; /** * @author Bela Ban */ public class ParticipantGmsImpl extends ServerGmsImpl { 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, boolean useFlushIfPresent) { wrongMethod("join"); } public void joinWithStateTransfer(Address mbr,boolean useFlushIfPresent) { 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)); 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)); 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(); 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); } /* ---------------------------------- 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.getId(), hdr); gms.getDownProtocol().down(new Event(Event.MSG, msg)); } /* ------------------------------ End of Private Methods ------------------------------------ */ } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/pbcast/STABLE.java0000644000175000017500000007260411647260573026754 0ustar moellermoellerpackage org.jgroups.protocols.pbcast; import org.jgroups.*; import org.jgroups.annotations.*; import org.jgroups.stack.Protocol; import org.jgroups.util.Digest; import org.jgroups.util.MutableDigest; import org.jgroups.util.TimeScheduler; import org.jgroups.util.Util; import java.io.*; import java.lang.management.ManagementFactory; import java.lang.management.MemoryMXBean; 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; /** * Computes the broadcast messages that are stable; i.e., have been received by * all members. Sends STABLE events up the stack when this is the case. This * allows NAKACK to garbage collect messages that have been seen by all members. *

                * 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 */ @MBean(description="Computes the broadcast messages that are stable") @DeprecatedProperty(names={"digest_timeout","max_gossip_runs","max_suspend_time"}) public class STABLE extends Protocol { private static final long MAX_SUSPEND_TIME=200000; /* ------------------------------------------ Properties ------------------------------------------ */ /** * Sends a STABLE gossip every 20 seconds on average. 0 disables gossiping of STABLE messages */ @Property(description="Average time to send a STABLE message. Default is 20000 msec") 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 */ @Property(description="Delay before stability message is sent. Default is 6000 msec") private long stability_delay=6000; /** * 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 */ @Property(description="Maximum number of bytes received in all messages before sending a STABLE message is triggered ." + "If ergonomics is enabled, this value is computed as max(MAX_HEAP * cap, N * max_bytes) where N = number of members") protected long max_bytes=2000000; protected long original_max_bytes=max_bytes; @Property(description="Max percentage of the max heap (-Xmx) to be used for max_bytes. " + "Only used if ergonomics is enabled. 0 disables setting max_bytes dynamically.") protected double cap=0.10; // 10% of the max heap by default /* --------------------------------------------- JMX ---------------------------------------------- */ 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; /* --------------------------------------------- Fields ------------------------------------------------------ */ 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(); @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) /** 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(); protected final MemoryMXBean memory_manager=ManagementFactory.getMemoryMXBean(); public STABLE() { } 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; this.original_max_bytes=max_bytes; } @ManagedAttribute(name="BytesReceived") public long getBytes() {return num_bytes_received;} @ManagedAttribute public int getStableSent() {return num_stable_msgs_sent;} @ManagedAttribute public int getStableReceived() {return num_stable_msgs_received;} @ManagedAttribute public int getStabilitySent() {return num_stability_msgs_sent;} @ManagedAttribute public int getStabilityReceived() {return num_stability_msgs_received;} @ManagedAttribute public boolean getStableTaskRunning() { stable_task_lock.lock(); try { return stable_task_future != null && !stable_task_future.isDone() && !stable_task_future.isCancelled(); } finally { stable_task_lock.unlock(); } } 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; } 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 init() throws Exception { super.init(); original_max_bytes=max_bytes; } public void start() throws Exception { timer=getTransport().getTimer(); if(timer == null) throw new Exception("timer cannot be retrieved"); 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(this.id); 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; } 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()) { boolean send_stable_msg=false; received.lock(); try { num_bytes_received+=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; send_stable_msg=true; } } finally { received.unlock(); } if(send_stable_msg) { 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); } } } 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; case Event.SET_LOCAL_ADDRESS: local_addr=(Address)evt.getArg(); break; } return down_prot.down(evt); } @ManagedOperation 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; if(ergonomics && cap > 0) { long max_heap=(long)(memory_manager.getHeapMemoryUsage().getMax() * cap); long new_size=tmp.size() * original_max_bytes; max_bytes=Math.min(max_heap, new_size); if(log.isDebugEnabled()) log.debug("[ergonomics] setting max_bytes to " + Util.printBytes(max_bytes) + " (" + tmp.size() + " members)"); } } 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()) { 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(local_addr + ": 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; 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; boolean 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(local_addr + ": 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(local_addr + ": received digest from " + sender + " (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(local_addr + ": 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(this.id, 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(local_addr + ": 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 { 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() { } 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 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(local_addr + ": 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(id, hdr); if(log.isTraceEnabled()) log.trace(local_addr + ": 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(); } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/pbcast/STATE_TRANSFER.java0000644000175000017500000005130011647260573030154 0ustar moellermoellerpackage org.jgroups.protocols.pbcast; import org.jgroups.*; import org.jgroups.annotations.DeprecatedProperty; import org.jgroups.annotations.GuardedBy; import org.jgroups.annotations.MBean; import org.jgroups.annotations.ManagedAttribute; import org.jgroups.stack.Protocol; import org.jgroups.stack.StateTransferInfo; import org.jgroups.util.Digest; import org.jgroups.util.Util; import java.io.*; import java.util.*; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; /** * STATE_TRANSFER protocol based on byte array transfer. 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 */ @MBean(description="State transfer protocol based on byte array transfer") @DeprecatedProperty(names= { "use_flush", "flush_timeout" }) public class STATE_TRANSFER extends Protocol { /* --------------------------------------------- JMX statistics --------------------------------------------- */ private long start, stop; // to measure state transfer time private final AtomicInteger num_state_reqs=new AtomicInteger(0); private final AtomicLong num_bytes_sent=new AtomicLong(0); private double avg_state_size=0; /* --------------------------------------------- Fields ------------------------------------------------------ */ private Address local_addr=null; @GuardedBy("members") private final Vector
                members=new Vector
                (); /** * Map of state requesters. Keys are state IDs, values are Sets * of Addresses (one for each requester) */ private final Map> state_requesters=new HashMap>(); /** set to true while waiting for a STATE_RSP */ private volatile boolean waiting_for_state_response=false; private boolean flushProtocolInStack=false; public STATE_TRANSFER() {} @ManagedAttribute public int getNumberOfStateRequests() { return num_state_reqs.get(); } @ManagedAttribute public long getNumberOfStateBytesSent() { return num_bytes_sent.get(); } @ManagedAttribute 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.OVERWRITE_DIGEST)); return retval; } public void resetStats() { super.resetStats(); num_state_reqs.set(0); num_bytes_sent.set(0); avg_state_size=0; } 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)); } 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(this.id); 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.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(this.id, new StateHeader(StateHeader.STATE_REQ, local_addr, System.currentTimeMillis(), 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; case Event.SET_LOCAL_ADDRESS: local_addr=(Address)evt.getArg(); 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.incrementAndGet(); if(state != null) num_bytes_sent.addAndGet(state.length); avg_state_size=num_bytes_sent.doubleValue() / num_state_reqs.doubleValue(); } 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(this.id, 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() { synchronized(members) { for(Address member:members) { if(!local_addr.equals(member)) { return member; } } } return null; } 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 */ private void handleStateRsp(StateHeader hdr, byte[] state) { Digest tmp_digest=hdr.my_digest; boolean digest_needed=isDigestNeeded(); waiting_for_state_response=false; if(digest_needed && tmp_digest != null) { down_prot.down(new Event(Event.OVERWRITE_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)); log.debug("received state, size=" + (state == null? "0" : state.length) + " bytes. Time=" + (stop - start) + " milliseconds"); StateTransferInfo info=new StateTransferInfo(hdr.sender, hdr.state_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 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; } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/pbcast/STREAMING_STATE_TRANSFER.java0000644000175000017500000011362311647260573031574 0ustar moellermoellerpackage org.jgroups.protocols.pbcast; import org.jgroups.*; import org.jgroups.annotations.*; import org.jgroups.conf.PropertyConverters; import org.jgroups.stack.IpAddress; import org.jgroups.stack.Protocol; import org.jgroups.stack.StateTransferInfo; import org.jgroups.util.Digest; import org.jgroups.util.ShutdownRejectedExecutionHandler; import org.jgroups.util.Util; import java.io.*; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; import java.util.HashMap; import java.util.Map; import java.util.Vector; import java.util.concurrent.*; 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 */ @MBean(description = "State trasnfer protocol based on streaming state transfer") @DeprecatedProperty(names = {"use_flush", "flush_timeout", "use_reading_thread"}) public class STREAMING_STATE_TRANSFER extends Protocol { /* * ----------------------------------------------Properties ----------------------------------- * */ @LocalAddress @Property(description = "The interface (NIC) used to accept state requests. " + "The following special values are also recognized: GLOBAL, SITE_LOCAL, LINK_LOCAL and NON_LOOPBACK", systemProperty={Global.BIND_ADDR, Global.BIND_ADDR_OLD}, defaultValueIPv4=Global.NON_LOOPBACK_ADDRESS, defaultValueIPv6=Global.NON_LOOPBACK_ADDRESS) private InetAddress bind_addr; @Property(name="bind_interface", converter=PropertyConverters.BindInterface.class, description="The interface (NIC) which should be used by this transport", dependsUpon="bind_addr") protected String bind_interface_str=null; @Property(description = "The port listening for state requests. Default value of 0 binds to any (ephemeral) port") private int bind_port = 0; @Property(description = "Maximum number of pool threads serving state requests. Default is 5") private int max_pool = 5; @Property(description = "Keep alive for pool threads serving state requests. Default is 20000 msec") private long pool_thread_keep_alive = 20 * 1000; @Property(description = "Buffer size for state transfer. Default is 8192 bytes") private int socket_buffer_size = 8 * 1024; @Property(description = "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; @Property(description = "If true default transport is 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
                (); } @ManagedAttribute public int getNumberOfStateRequests() { return num_state_reqs.get(); } @ManagedAttribute public long getNumberOfStateBytesSent() { return num_bytes_sent.get(); } @ManagedAttribute 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.OVERWRITE_DIGEST)); return retval; } public void resetStats() { super.resetStats(); num_state_reqs.set(0); num_bytes_sent.set(0); avg_state_size = 0; } 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(this.id); 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.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(this.id, 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"); 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; case Event.SET_LOCAL_ADDRESS: local_addr=(Address)evt.getArg(); 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) { // setup socket plumbing if needed if (spawner == null && !use_default_transport) { spawner = new StateProviderThreadSpawner(setupThreadPool(), Util.createServerSocket(getSocketFactory(), Global.STREAMING_STATE_TRANSFER_SERVER_SOCK, bind_addr, bind_port)); 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(this.id, 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() { private final AtomicInteger threadNumber = new AtomicInteger(1); public Thread newThread(final Runnable command) { return getThreadFactory().newThread(command, "STREAMING_STATE_TRANSFER-sender-" + threadNumber.getAndIncrement()); } }; 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.OVERWRITE_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(); InputStream bis = 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()); Util.connect(socket, new InetSocketAddress(address.getIpAddress(), address.getPort()), 0); 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); bis = new BufferedInputStream(new StreamingInputStreamWrapper(socket),socket_buffer_size); sti = new StateTransferInfo(hdr.sender, bis, 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(bis); 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 { getSocketFactory().close(serverSocket); } 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) { OutputStream bos = 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(); bos = new BufferedOutputStream(new StreamingOutputStreamWrapper(socket),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("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(bos); Util.close(socket); } } } private class StreamingInputStreamWrapper extends FilterInputStream { private final Socket inputStreamOwner; private final AtomicBoolean closed = new AtomicBoolean(false); public StreamingInputStreamWrapper(Socket inputStreamOwner) throws IOException { super(inputStreamOwner.getInputStream()); this.inputStreamOwner = inputStreamOwner; } public void close() throws IOException { if (closed.compareAndSet(false, true)) { if (log.isDebugEnabled()) { log.debug("State reader is closing the socket "); } Util.close(inputStreamOwner); up(new Event(Event.STATE_TRANSFER_INPUTSTREAM_CLOSED)); down(new Event(Event.STATE_TRANSFER_INPUTSTREAM_CLOSED)); } super.close(); } } private class StreamingOutputStreamWrapper extends FilterOutputStream { private final Socket outputStreamOwner; private final AtomicBoolean closed = new AtomicBoolean(false); private long bytesWrittenCounter = 0; public StreamingOutputStreamWrapper(Socket outputStreamOwner) throws IOException { super(outputStreamOwner.getOutputStream()); this.outputStreamOwner = outputStreamOwner; } public void close() throws IOException { if (closed.compareAndSet(false, true)) { if (log.isDebugEnabled()) { log.debug("State writer is closing the socket "); } Util.close(outputStreamOwner); 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 { //Thanks Kornelius Elstner //https://jira.jboss.org/browse/JGRP-1223 out.write(b, off, len); bytesWrittenCounter += len; } public void write(byte[] b) throws IOException { super.write(b); bytesWrittenCounter += b.length; } public void write(int b) throws IOException { super.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(id); 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(id, 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 { 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 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; } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/pbcast/ServerGmsImpl.java0000644000175000017500000000247411647260573030537 0ustar moellermoellerpackage org.jgroups.protocols.pbcast; import org.jgroups.Address; import org.jgroups.util.MergeId; import org.jgroups.util.Digest; import java.util.Collection; /** * Common super class for CoordGmsImpl and ParticipantGmsImpl * @author Bela Ban */ public abstract class ServerGmsImpl extends GmsImpl { protected ServerGmsImpl(GMS gms) { super(gms); } /** * 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. * @param sender The address of the merge leader * @param merge_id The merge ID * @param mbrs The set of members from which we expect responses */ public void handleMergeRequest(Address sender, MergeId merge_id, Collection mbrs) { merger.handleMergeRequest(sender, merge_id, mbrs); } /** * 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 MergeId merge_id) { merger.handleMergeView(data, merge_id); } public void handleDigestResponse(Address sender, Digest digest) { merger.handleDigestResponse(sender, digest); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/protocols/pbcast/package.html0000644000175000017500000000010411647260573027402 0ustar moellermoeller Supports probabilistic broadcasts. libjgroups-java-2.12.2.Final.orig/src/org/jgroups/stack/0000755000175000017500000000000011647260573022733 5ustar moellermoellerlibjgroups-java-2.12.2.Final.orig/src/org/jgroups/stack/AckMcastReceiverWindow.java0000644000175000017500000000724011647260573030144 0ustar moellermoeller package org.jgroups.stack; import org.jgroups.logging.Log; import org.jgroups.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); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/stack/AckMcastSenderWindow.java0000644000175000017500000004536711647260573027634 0ustar moellermoellerpackage org.jgroups.stack; import org.jgroups.logging.Log; import org.jgroups.logging.LogFactory; import org.jgroups.Address; import org.jgroups.Message; import org.jgroups.util.DefaultTimeScheduler; 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 */ 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 DefaultTimeScheduler(), 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) { timer.stop(); } 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(); } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/stack/AckReceiverWindow.java0000644000175000017500000002521111647260573027152 0ustar moellermoellerpackage org.jgroups.stack; import org.jgroups.Message; import org.jgroups.util.Tuple; import org.jgroups.util.Util; import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReferenceArray; /** * 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 */ public class AckReceiverWindow { private final AtomicLong next_to_remove; private final AtomicBoolean processing=new AtomicBoolean(false); private final ConcurrentMap segments=Util.createConcurrentMap(); private volatile Segment current_segment=null; private volatile Segment current_remove_segment=null; private final int segment_capacity; private long highest_segment_created=0; public static final Message TOMBSTONE=new Message(false) { public String toString() { return "tombstone"; } }; public AckReceiverWindow(long initial_seqno) { this(initial_seqno, 20000); } public AckReceiverWindow(long initial_seqno, int segment_capacity) { next_to_remove=new AtomicLong(initial_seqno); this.segment_capacity=segment_capacity; long index=next_to_remove.get() / segment_capacity; long first_seqno=(next_to_remove.get() / segment_capacity) * segment_capacity; this.segments.put(index, new Segment(first_seqno, segment_capacity)); Segment initial_segment=findOrCreateSegment(next_to_remove.get()); current_segment=initial_segment; current_remove_segment=initial_segment; for(long i=0; i < next_to_remove.get(); i++) { initial_segment.add(i, TOMBSTONE); initial_segment.remove(i); } } 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; } /** * Adds a message if not yet received * @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) { Segment segment=current_segment; if(segment == null || !segment.contains(seqno)) { segment=findOrCreateSegment(seqno); if(segment != null) current_segment=segment; } if(segment == null) return -1; return segment.add(seqno, msg); } /** * 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() { long next=next_to_remove.get(); Segment segment=current_remove_segment; if(segment == null || !segment.contains(next)) { segment=findSegment(next); if(segment != null) current_remove_segment=segment; } if(segment == null) return null; Message retval=segment.remove(next); if(retval != null) { next_to_remove.compareAndSet(next, next +1); if(segment.allRemoved()) segments.remove(next / segment_capacity); } return retval; } /** * Removes as many messages as possible (in sequence, without gaps) * @param max Max number of messages to be removed * @return Tuple,Long>: a tuple of the message list and the highest seqno removed */ public Tuple,Long> removeMany(final int max) { List list=null; // we remove msgs.size() messages *max* Tuple,Long> retval=null; int count=0; boolean looping=true; while(count < max && looping) { long next=next_to_remove.get(); Segment segment=current_remove_segment; if(segment == null || !segment.contains(next)) { segment=findSegment(next); if(segment != null) current_remove_segment=segment; } if(segment == null) return retval; long segment_id=next; long end=segment.getEndIndex(); while(next < end && count < max) { Message msg=segment.remove(next); if(msg == null) { looping=false; break; } if(list == null) { list=new LinkedList(); // we remove msgs.size() messages *max* retval=new Tuple,Long>(list, 0L); } list.add(msg); count++; retval.setVal2(next); next_to_remove.compareAndSet(next, ++next); if(segment.allRemoved()) segments.remove(segment_id / segment_capacity); } } return retval; } public List removeManyAsList(int max) { Tuple, Long> tuple=removeMany(max); return tuple != null? tuple.getVal1() : null; } public void reset() { segments.clear(); } public int size() { int retval=0; for(Segment segment: segments.values()) retval+=segment.size(); return retval; } public String toString() { StringBuilder sb=new StringBuilder(); int size=size(); sb.append(size + " messages"); if(size <= 100) sb.append(" in " + segments.size() + " segments"); return sb.toString(); } public String printMessages() { StringBuilder sb=new StringBuilder(); List keys=new LinkedList(segments.keySet()); Collections.sort(keys); for(long key: keys) { Segment segment=segments.get(key); if(segment == null) continue; for(long i=segment.getStartIndex(); i < segment.getEndIndex(); i++) { Message msg=segment.get(i); if(msg == null) continue; if(msg == TOMBSTONE) sb.append("T "); else sb.append(i + " "); } } return sb.toString(); } private Segment findOrCreateSegment(long seqno) { long index=seqno / segment_capacity; if(index > highest_segment_created) { long start_seqno=seqno / segment_capacity * segment_capacity; Segment segment=new Segment(start_seqno, segment_capacity); Segment tmp=segments.putIfAbsent(index, segment); if(tmp != null) // segment already exists segment=tmp; else highest_segment_created=index; return segment; } return segments.get(index); } private Segment findSegment(long seqno) { long index=seqno / segment_capacity; return segments.get(index); } private static class Segment { final long start_index; // e.g. 5000. Then seqno 5100 would be at index 100 final int capacity; final AtomicReferenceArray array; final AtomicInteger num_tombstones=new AtomicInteger(0); public Segment(long start_index, int capacity) { this.start_index=start_index; this.capacity=capacity; this.array=new AtomicReferenceArray(capacity); } public long getStartIndex() { return start_index; } public long getEndIndex() { return start_index + capacity; } public boolean contains(long seqno) { return seqno >= start_index && seqno < getEndIndex(); } public Message get(long seqno) { int index=index(seqno); if(index < 0 || index >= array.length()) return null; return array.get(index); } public byte add(long seqno, Message msg) { int index=index(seqno); if(index < 0) return -1; boolean success=array.compareAndSet(index, null, msg); if(success) { return 1; } else return 0; } public Message remove(long seqno) { int index=index(seqno); if(index < 0) return null; Message retval=array.get(index); if(retval != null && retval != TOMBSTONE && array.compareAndSet(index, retval, TOMBSTONE)) { num_tombstones.incrementAndGet(); return retval; } return null; } public boolean allRemoved() { return num_tombstones.get() >= capacity; } public String toString() { return start_index + " - " + (start_index + capacity -1) + " (" + size() + " elements)"; } public int size() { int retval=0; for(int i=0; i < capacity; i++) { Message tmp=array.get(i); if(tmp != null && tmp != TOMBSTONE) retval++; } return retval; } private int index(long seqno) { if(seqno < start_index) return -1; int index=(int)(seqno - start_index); if(index < 0 || index >= capacity) throw new IndexOutOfBoundsException("index=" + index + ", start_index=" + start_index + ", seqno=" + seqno); return index; } } }libjgroups-java-2.12.2.Final.orig/src/org/jgroups/stack/AckSenderWindow.java0000644000175000017500000001375211647260573026635 0ustar moellermoeller package org.jgroups.stack; 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 { private RetransmitCommand retransmit_command = null; // called to request XMIT of msgs private final ConcurrentMap msgs=new ConcurrentHashMap(); private Interval interval=new StaticInterval(400,800,1200,1600); private final Retransmitter retransmitter; private long lowest=Global.DEFAULT_FIRST_UNICAST_SEQNO; // lowest seqno, used by ack() private long highest=0; public interface RetransmitCommand { void retransmit(long seqno, Message msg); } public AckSenderWindow(RetransmitCommand com) { retransmit_command = com; retransmitter = new DefaultRetransmitter(null, this, null); retransmitter.setRetransmitTimeouts(interval); } public AckSenderWindow(RetransmitCommand com, Interval interval, TimeScheduler sched) { retransmit_command = com; this.interval = interval; retransmitter = new DefaultRetransmitter(null, this, sched); retransmitter.setRetransmitTimeouts(interval); } public AckSenderWindow(RetransmitCommand com, Interval interval, TimeScheduler sched, Address sender) { retransmit_command = com; this.interval = interval; retransmitter = new DefaultRetransmitter(sender, this, sched); retransmitter.setRetransmitTimeouts(interval); } /** * Creates an instance without retransmitter */ public AckSenderWindow() { retransmitter=null; } /** Only to be used for testing purposes */ public synchronized long getLowest() { return lowest; } public long getHighest() { return highest; } public void reset() { msgs.clear(); if(retransmitter != null) retransmitter.reset(); lowest=Global.DEFAULT_FIRST_UNICAST_SEQNO; } public Message get(long seqno) { return msgs.get(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); if(retransmitter != null) retransmitter.add(seqno, seqno); highest=Math.max(highest, seqno); } public void addToMessages(long seqno, Message msg) { msgs.putIfAbsent(seqno, msg); highest=Math.max(highest, seqno); } public void addToRetransmitter(long seqno, Message msg) { if(retransmitter != null) 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); } removeRange(prev_lowest, seqno); } /** 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(); int num_msgs=msgs.size(); sb.append(num_msgs).append(" msgs"); if(retransmitter != null) sb.append(" (").append(retransmitter.size()).append(" to retransmit)"); if(num_msgs > 0) { sb.append(": "); 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"); if(retransmitter != null) sb.append(" (").append(retransmitter.size()).append(" to retransmit)"); sb.append(":\n"); sb.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) { 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 ---------------- */ private void removeRange(long from, long to) { for(long i=from; i <= to; i++) { msgs.remove(i); if(retransmitter != null) retransmitter.remove(i); } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/stack/AddressGenerator.java0000644000175000017500000000042211647260573027030 0ustar moellermoellerpackage org.jgroups.stack; import org.jgroups.Address; /** * Callback to provide custom addresses. Will be called by {@link org.jgroups.JChannel#connect(String)}. * @author Bela Ban * @since 2.12 */ public interface AddressGenerator { Address generateAddress(); } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/stack/Configurator.java0000644000175000017500000017620211647260573026250 0ustar moellermoellerpackage org.jgroups.stack; import org.jgroups.Event; import org.jgroups.Global; import org.jgroups.annotations.DeprecatedProperty; import org.jgroups.annotations.LocalAddress; import org.jgroups.annotations.Property; import org.jgroups.conf.PropertyHelper; import org.jgroups.conf.ProtocolConfiguration; import org.jgroups.logging.Log; import org.jgroups.logging.LogFactory; import org.jgroups.protocols.TP; import org.jgroups.util.StackType; 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.lang.reflect.*; import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; import java.net.InetSocketAddress; 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 * @author Richard Achmatowicz */ public class Configurator { protected static final Log log=LogFactory.getLog(Configurator.class); private final ProtocolStack stack; public Configurator() { stack=null; } public Configurator(ProtocolStack protocolStack) { stack=protocolStack; } public Protocol setupProtocolStack(List config) throws Exception{ return setupProtocolStack(config, stack); } public Protocol setupProtocolStack(ProtocolStack copySource)throws Exception{ List protocols=copySource.copyProtocols(stack); Collections.reverse(protocols); return connectProtocols(protocols); } /** * 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     |
                     *   -----------------------
                     * 
                */ private static Protocol setupProtocolStack(List protocol_configs, ProtocolStack st) throws Exception { List protocols=createProtocols(protocol_configs, st); if(protocols == null) return null; // check InetAddress related features of stack Map> inetAddressMap = createInetAddressMap(protocol_configs, protocols) ; Collection addrs=getAddresses(inetAddressMap); StackType ip_version=Util.getIpStackType(); // 0 = n/a, 4 = IPv4, 6 = IPv6 if(!addrs.isEmpty()) { // check that all user-supplied InetAddresses have a consistent version: // 1. If an addr is IPv6 and we have an IPv4 stack --> FAIL // 2. If an address is an IPv4 class D (multicast) address and the stack is IPv6: FAIL // Else pass for(InetAddress addr: addrs) { if(addr instanceof Inet6Address && ip_version == StackType.IPv4) throw new IllegalArgumentException("found IPv6 address " + addr + " in an IPv4 stack"); if(addr instanceof Inet4Address && addr.isMulticastAddress() && ip_version == StackType.IPv6) throw new Exception("found IPv4 multicast address " + addr + " in an IPv6 stack"); } } // process default values setDefaultValues(protocol_configs, protocols, ip_version); ensureValidBindAddresses(protocols); return connectProtocols(protocols); } public static void setDefaultValues(List protocols) throws Exception { if(protocols == null) return; // check InetAddress related features of stack Collection addrs=getInetAddresses(protocols); StackType ip_version=Util.getIpStackType(); // 0 = n/a, 4 = IPv4, 6 = IPv6 if(!addrs.isEmpty()) { // check that all user-supplied InetAddresses have a consistent version: // 1. If an addr is IPv6 and we have an IPv4 stack --> FAIL // 2. If an address is an IPv4 class D (multicast) address and the stack is IPv6: FAIL // Else pass for(InetAddress addr : addrs) { if(addr instanceof Inet6Address && ip_version == StackType.IPv4) throw new IllegalArgumentException("found IPv6 address " + addr + " in an IPv4 stack"); if(addr instanceof Inet4Address && addr.isMulticastAddress() && ip_version == StackType.IPv6) throw new Exception("found IPv4 multicast address " + addr + " in an IPv6 stack"); } } // process default values setDefaultValues(protocols, ip_version); } /** * 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=createLayer(stack, config); prot.init(); 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 */ public static Protocol connectProtocols(List protocol_list) throws Exception { Protocol current_layer=null, next_layer=null; for(int i=0; i < protocol_list.size(); i++) { current_layer=protocol_list.get(i); if(i + 1 >= protocol_list.size()) break; next_layer=protocol_list.get(i + 1); next_layer.setDownProtocol(current_layer); current_layer.setUpProtocol(next_layer); if(current_layer instanceof TP) { TP transport = (TP)current_layer; if(transport.isSingleton()) { ConcurrentMap up_prots=transport.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); } } } // basic protocol sanity check sanityCheck(protocol_list); 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 */ private static List parseProtocols(String config_str) throws IOException { List retval=new LinkedList(); 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 List of ProtocolConfigurations */ public static List parseConfigurations(String configuration) throws Exception { List retval=new ArrayList(); List protocol_string=parseProtocols(configuration); if(protocol_string == null) return null; for(String component_string: protocol_string) { retval.add(new ProtocolConfiguration(component_string)); } return retval; } public static String printConfigurations(Collection configs) { StringBuilder sb=new StringBuilder(); boolean first=true; for(ProtocolConfiguration config: configs) { if(first) first=false; else sb.append(":"); sb.append(config.getProtocolName()); if(!config.getProperties().isEmpty()) { sb.append('(').append(config.propertiesToString()).append(')'); } } return sb.toString(); } 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 List of Protocols */ private static List createProtocols(List protocol_configs, final ProtocolStack stack) throws Exception { List retval=new LinkedList(); ProtocolConfiguration protocol_config; Protocol layer; String singleton_name; for(int i=0; i < protocol_configs.size(); i++) { protocol_config=protocol_configs.get(i); singleton_name=protocol_config.getProperties().get(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=createLayer(stack, protocol_config); if(layer == null) return null; singleton_transports.put(singleton_name, new Tuple((TP)layer,new ProtocolStack.RefCounter((short)0,(short)0))); retval.add(layer); } } continue; } layer=createLayer(stack, protocol_config); if(layer == null) return null; retval.add(layer); } return retval; } protected static Protocol createLayer(ProtocolStack stack, ProtocolConfiguration config) throws Exception { String protocol_name=config.getProtocolName(); Map properties=config.getProperties(); Protocol retval=null; if(protocol_name == null || properties == null) return null; String defaultProtocolName=ProtocolConfiguration.protocol_prefix + '.' + protocol_name; Class clazz=null; try { clazz=Util.loadClass(defaultProtocolName, stack.getClass()); } catch(ClassNotFoundException e) { } if(clazz == null) { try { clazz=Util.loadClass(protocol_name, stack.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(stack); removeDeprecatedProperties(retval, properties); // before processing Field and Method based properties, take dependencies specified // with @Property.dependsUpon into account AccessibleObject[] dependencyOrderedFieldsAndMethods = computePropertyDependencies(retval, properties) ; for(AccessibleObject ordered: dependencyOrderedFieldsAndMethods) { if (ordered instanceof Field) { resolveAndAssignField(retval, (Field)ordered, properties) ; } else if (ordered instanceof Method) { resolveAndInvokePropertyMethod(retval, (Method)ordered, properties) ; } } List additional_objects=retval.getConfigurableObjects(); if(additional_objects != null && !additional_objects.isEmpty()) { for(Object obj: additional_objects) { resolveAndAssignFields(obj, properties); resolveAndInvokePropertyMethods(obj, properties); } } if(!properties.isEmpty()) { 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; } /** Throws an exception if sanity check fails. Possible sanity check is uniqueness of all protocol names */ public static void sanityCheck(List protocols) throws Exception { // check for unique IDs Set ids=new HashSet(); for(Protocol protocol: protocols) { short id=protocol.getId(); if(id > 0 && ids.add(id) == false) throw new Exception("Protocol ID " + id + " (name=" + protocol.getName() + ") is duplicate; protocol IDs have to be unique"); } // For each protocol, get its required up and down services and check (if available) if they're satisfied for(Protocol protocol: protocols) { List required_down_services=protocol.requiredDownServices(); List required_up_services=protocol.requiredUpServices(); if(required_down_services != null && !required_down_services.isEmpty()) { // the protocols below 'protocol' have to provide the services listed in required_down_services List tmp=new ArrayList(required_down_services); removeProvidedUpServices(protocol, tmp); if(!tmp.isEmpty()) throw new Exception("events " + printEvents(tmp) + " are required by " + protocol.getName() + ", but not provided by any of the protocols below it"); } if(required_up_services != null && !required_up_services.isEmpty()) { // the protocols above 'protocol' have to provide the services listed in required_up_services List tmp=new ArrayList(required_up_services); removeProvidedDownServices(protocol, tmp); if(!tmp.isEmpty()) throw new Exception("events " + printEvents(tmp) + " are required by " + protocol.getName() + ", but not provided by any of the protocols above it"); } } } protected static String printEvents(List events) { StringBuilder sb=new StringBuilder("["); for(int evt: events) sb.append(Event.type2String(evt)).append(" "); sb.append("]"); return sb.toString(); } /** * Removes all events provided by the protocol below protocol from events * @param protocol * @param events */ protected static void removeProvidedUpServices(Protocol protocol, List events) { if(protocol == null || events == null) return; for(Protocol prot=protocol.getDownProtocol(); prot != null && !events.isEmpty(); prot=prot.getDownProtocol()) { List provided_up_services=prot.providedUpServices(); if(provided_up_services != null && !provided_up_services.isEmpty()) events.removeAll(provided_up_services); } } /** * Removes all events provided by the protocol above protocol from events * @param protocol * @param events */ protected static void removeProvidedDownServices(Protocol protocol, List events) { if(protocol == null || events == null) return; for(Protocol prot=protocol.getUpProtocol(); prot != null && !events.isEmpty(); prot=prot.getUpProtocol()) { List provided_down_services=prot.providedDownServices(); if(provided_down_services != null && !provided_down_services.isEmpty()) events.removeAll(provided_down_services); } } /** * Returns all inet addresses found */ public static Collection getAddresses(Map> inetAddressMap) throws Exception { Set addrs=new HashSet(); for(Map.Entry> inetAddressMapEntry : inetAddressMap.entrySet()) { Map protocolInetAddressMap=inetAddressMapEntry.getValue(); for(Map.Entry protocolInetAddressMapEntry : protocolInetAddressMap.entrySet()) { InetAddressInfo inetAddressInfo=protocolInetAddressMapEntry.getValue(); // add InetAddressInfo to sets based on IP version List addresses=inetAddressInfo.getInetAddresses(); for(InetAddress address : addresses) { if(address == null) throw new RuntimeException("failed converting address info to IP address: " + inetAddressInfo); addrs.add(address); } } } return addrs; } /** * This method takes a set of InetAddresses, represented by an inetAddressmap, and: * - if the resulting set is non-empty, goes through to see if all InetAddress-related * user settings have a consistent IP version: v4 or v6, and throws an exception if not * - if the resulting set is empty, sets the default IP version based on available stacks * and if a dual stack, stack preferences * - sets the IP version to be used in the JGroups session * @return StackType.IPv4 for IPv4, StackType.IPv6 for IPv6, StackType.Unknown if the version cannot be determined */ public static StackType determineIpVersionFromAddresses(Collection addrs) throws Exception { Set ipv4_addrs= new HashSet() ; Set ipv6_addrs= new HashSet() ; for(InetAddress address: addrs) { if (address instanceof Inet4Address) ipv4_addrs.add(address) ; else ipv6_addrs.add(address) ; } if(log.isTraceEnabled()) log.trace("all addrs=" + addrs + ", IPv4 addrs=" + ipv4_addrs + ", IPv6 addrs=" + ipv6_addrs); // the user supplied 1 or more IP address inputs. Check if we have a consistent set if (!addrs.isEmpty()) { if (!ipv4_addrs.isEmpty() && !ipv6_addrs.isEmpty()) { throw new RuntimeException("all addresses have to be either IPv4 or IPv6: IPv4 addresses=" + ipv4_addrs + ", IPv6 addresses=" + ipv6_addrs); } return !ipv6_addrs.isEmpty()? StackType.IPv6 : StackType.IPv4; } return StackType.Unknown; } /* * A method which does the following: * - discovers all Fields or Methods within the protocol stack which set * InetAddress, IpAddress, InetSocketAddress (and Lists of such) for which the user *has* * specified a default value. * - stores the resulting set of Fields and Methods in a map of the form: * Protocol -> Property -> InetAddressInfo * where InetAddressInfo instances encapsulate the InetAddress related information * of the Fields and Methods. */ public static Map> createInetAddressMap(List protocol_configs, List protocols) throws Exception { // Map protocol -> Map, where the latter is protocol specific Map> inetAddressMap = new HashMap>() ; // collect InetAddressInfo for (int i = 0; i < protocol_configs.size(); i++) { ProtocolConfiguration protocol_config = protocol_configs.get(i) ; Protocol protocol = protocols.get(i) ; String protocolName = protocol.getName(); // regenerate the Properties which were destroyed during basic property processing Map properties = protocol_config.getOriginalProperties(); // check which InetAddress-related properties are ***non-null ***, and // create an InetAddressInfo structure for them // Method[] methods=protocol.getClass().getMethods(); Method[] methods=Util.getAllDeclaredMethodsWithAnnotations(protocol.getClass(), Property.class); for(int j = 0; j < methods.length; j++) { if (methods[j].isAnnotationPresent(Property.class) && isSetPropertyMethod(methods[j])) { String propertyName = PropertyHelper.getPropertyName(methods[j]) ; String propertyValue = properties.get(propertyName); // if there is a systemProperty attribute defined in the annotation, set the property value from the system property String tmp=grabSystemProp(methods[j].getAnnotation(Property.class)); if(tmp != null) propertyValue=tmp; if (propertyValue != null && InetAddressInfo.isInetAddressRelated(methods[j])) { Object converted = null ; try { converted=PropertyHelper.getConvertedValue(protocol, methods[j], properties, propertyValue, false); } catch(Exception e) { throw new Exception("String value could not be converted for method " + propertyName + " in " + protocolName + " with default value " + propertyValue + ".Exception is " +e, e); } InetAddressInfo inetinfo = new InetAddressInfo(protocol, methods[j], properties, propertyValue, converted) ; Map protocolInetAddressMap=inetAddressMap.get(protocolName); if(protocolInetAddressMap == null) { protocolInetAddressMap = new HashMap() ; inetAddressMap.put(protocolName, protocolInetAddressMap) ; } protocolInetAddressMap.put(propertyName, inetinfo) ; } } } //traverse class hierarchy and find all annotated fields and add them to the list if annotated for(Class clazz=protocol.getClass(); clazz != null; clazz=clazz.getSuperclass()) { Field[] fields=clazz.getDeclaredFields(); for(int j = 0; j < fields.length; j++ ) { if (fields[j].isAnnotationPresent(Property.class)) { String propertyName = PropertyHelper.getPropertyName(fields[j], properties) ; String propertyValue = properties.get(propertyName) ; // if there is a systemProperty attribute defined in the annotation, set the property value from the system property String tmp=grabSystemProp(fields[j].getAnnotation(Property.class)); if(tmp != null) propertyValue=tmp; if ((propertyValue != null || !PropertyHelper.usesDefaultConverter(fields[j])) && InetAddressInfo.isInetAddressRelated(protocol, fields[j])) { Object converted = null ; try { converted=PropertyHelper.getConvertedValue(protocol, fields[j], properties, propertyValue, false); } catch(Exception e) { throw new Exception("String value could not be converted for method " + propertyName + " in " + protocolName + " with default value " + propertyValue + ".Exception is " +e, e); } InetAddressInfo inetinfo = new InetAddressInfo(protocol, fields[j], properties, propertyValue, converted) ; Map protocolInetAddressMap=inetAddressMap.get(protocolName); if(protocolInetAddressMap == null) { protocolInetAddressMap = new HashMap() ; inetAddressMap.put(protocolName, protocolInetAddressMap) ; } protocolInetAddressMap.put(propertyName, inetinfo) ; }// recompute } } } } return inetAddressMap ; } public static List getInetAddresses(List protocols) throws Exception { List retval=new LinkedList(); // collect InetAddressInfo for(Protocol protocol : protocols) { String protocolName=protocol.getName(); //traverse class hierarchy and find all annotated fields and add them to the list if annotated for(Class clazz=protocol.getClass(); clazz != null; clazz=clazz.getSuperclass()) { Field[] fields=clazz.getDeclaredFields(); for(int j=0; j < fields.length; j++) { if(fields[j].isAnnotationPresent(Property.class)) { if(InetAddressInfo.isInetAddressRelated(protocol, fields[j])) { Object value=getValueFromProtocol(protocol, fields[j]); if(value instanceof InetAddress) retval.add((InetAddress)value); else if(value instanceof IpAddress) retval.add(((IpAddress)value).getIpAddress()); else if(value instanceof InetSocketAddress) retval.add(((InetSocketAddress)value).getAddress()); } } } } } return retval; } /* * Method which processes @Property.default() values, associated with the annotation * using the defaultValue= attribute. This method does the following: * - locate all properties which have no user value assigned * - if the defaultValue attribute is not "", generate a value for the field using the * property converter for that property and assign it to the field */ public static void setDefaultValues(List protocol_configs, List protocols, StackType ip_version) throws Exception { InetAddress default_ip_address=Util.getNonLoopbackAddress(); if(default_ip_address == null) { log.warn("unable to find an address other than loopback for IP version " + ip_version); default_ip_address=Util.getLocalhost(ip_version); } for(int i=0; i < protocol_configs.size(); i++) { ProtocolConfiguration protocol_config=protocol_configs.get(i); Protocol protocol=protocols.get(i); String protocolName=protocol.getName(); // regenerate the Properties which were destroyed during basic property processing Map properties=protocol_config.getOriginalProperties(); Method[] methods=Util.getAllDeclaredMethodsWithAnnotations(protocol.getClass(), Property.class); for(int j=0; j < methods.length; j++) { if(isSetPropertyMethod(methods[j])) { String propertyName=PropertyHelper.getPropertyName(methods[j]); Object propertyValue=getValueFromProtocol(protocol, propertyName); if(propertyValue == null) { // if propertyValue is null, check if there is a we can use Property annotation=methods[j].getAnnotation(Property.class); // get the default value for the method- check for InetAddress types String defaultValue=null; if(InetAddressInfo.isInetAddressRelated(methods[j])) { defaultValue=ip_version == StackType.IPv4? annotation.defaultValueIPv4() : annotation.defaultValueIPv6(); if(defaultValue != null && defaultValue.length() > 0) { Object converted=null; try { if(defaultValue.equalsIgnoreCase(Global.NON_LOOPBACK_ADDRESS)) converted=default_ip_address; else converted=PropertyHelper.getConvertedValue(protocol, methods[j], properties, defaultValue, true); methods[j].invoke(protocol, converted); } catch(Exception e) { throw new Exception("default could not be assigned for method " + propertyName + " in " + protocolName + " with default " + defaultValue, e); } if(log.isDebugEnabled()) log.debug("set property " + protocolName + "." + propertyName + " to default value " + converted); } } } } } //traverse class hierarchy and find all annotated fields and add them to the list if annotated Field[] fields=Util.getAllDeclaredFieldsWithAnnotations(protocol.getClass(), Property.class); for(int j=0; j < fields.length; j++) { String propertyName=PropertyHelper.getPropertyName(fields[j], properties); Object propertyValue=getValueFromProtocol(protocol, fields[j]); if(propertyValue == null) { // add to collection of @Properties with no user specified value Property annotation=fields[j].getAnnotation(Property.class); // get the default value for the field - check for InetAddress types String defaultValue=null; if(InetAddressInfo.isInetAddressRelated(protocol, fields[j])) { defaultValue=ip_version == StackType.IPv4? annotation.defaultValueIPv4() : annotation.defaultValueIPv6(); if(defaultValue != null && defaultValue.length() > 0) { // condition for invoking converter if(defaultValue != null || !PropertyHelper.usesDefaultConverter(fields[j])) { Object converted=null; try { if(defaultValue.equalsIgnoreCase(Global.NON_LOOPBACK_ADDRESS)) converted=default_ip_address; else converted=PropertyHelper.getConvertedValue(protocol, fields[j], properties, defaultValue, true); if(converted != null) setField(fields[j], protocol, converted); } catch(Exception e) { throw new Exception("default could not be assigned for field " + propertyName + " in " + protocolName + " with default value " + defaultValue, e); } if(log.isDebugEnabled()) log.debug("set property " + protocolName + "." + propertyName + " to default value " + converted); } } } } } } } public static void setDefaultValues(List protocols, StackType ip_version) throws Exception { InetAddress default_ip_address=Util.getNonLoopbackAddress(); if(default_ip_address == null) { log.warn("unable to find an address other than loopback for IP version " + ip_version); default_ip_address=Util.getLocalhost(ip_version); } for(Protocol protocol : protocols) { String protocolName=protocol.getName(); //traverse class hierarchy and find all annotated fields and add them to the list if annotated Field[] fields=Util.getAllDeclaredFieldsWithAnnotations(protocol.getClass(), Property.class); for(int j=0; j < fields.length; j++) { // get the default value for the field - check for InetAddress types if(InetAddressInfo.isInetAddressRelated(protocol, fields[j])) { Object propertyValue=getValueFromProtocol(protocol, fields[j]); if(propertyValue == null) { // add to collection of @Properties with no user specified value Property annotation=fields[j].getAnnotation(Property.class); String defaultValue=ip_version == StackType.IPv4? annotation.defaultValueIPv4() : annotation.defaultValueIPv6(); if(defaultValue != null && defaultValue.length() > 0) { // condition for invoking converter Object converted=null; try { if(defaultValue.equalsIgnoreCase(Global.NON_LOOPBACK_ADDRESS)) converted=default_ip_address; else converted=PropertyHelper.getConvertedValue(protocol, fields[j], defaultValue, true); if(converted != null) setField(fields[j], protocol, converted); } catch(Exception e) { throw new Exception("default could not be assigned for field " + fields[j].getName() + " in " + protocolName + " with default value " + defaultValue, e); } if(log.isDebugEnabled()) log.debug("set property " + protocolName + "." + fields[j].getName() + " to default value " + converted); } } } } } } /** * Makes sure that all fields annotated with @LocalAddress is (1) an InetAddress and (2) a valid address on any * local network interface * @param protocols * @throws Exception */ public static void ensureValidBindAddresses(List protocols) throws Exception { for(Protocol protocol : protocols) { String protocolName=protocol.getName(); //traverse class hierarchy and find all annotated fields and add them to the list if annotated Field[] fields=Util.getAllDeclaredFieldsWithAnnotations(protocol.getClass(), LocalAddress.class); for(int i=0; i < fields.length; i++) { Object val=getValueFromProtocol(protocol, fields[i]); if(!(val instanceof InetAddress)) throw new Exception("field " + protocolName + "." + fields[i].getName() + " is not an InetAddress"); Util.checkIfValidAddress((InetAddress)val, protocolName); } } } public static Object getValueFromProtocol(Protocol protocol, Field field) throws IllegalAccessException { if(protocol == null || field == null) return null; if(!Modifier.isPublic(field.getModifiers())) field.setAccessible(true); return field.get(protocol); } public static Object getValueFromProtocol(Protocol protocol, String field_name) throws IllegalAccessException { if(protocol == null || field_name == null) return null; Field field=Util.getField(protocol.getClass(), field_name); return field != null? getValueFromProtocol(protocol, field) : null; } /** * This method creates a list of all properties (Field or Method) in dependency order, * where dependencies are specified using the dependsUpon specifier of the Property annotation. * In particular, it does the following: * (i) creates a master list of properties * (ii) checks that all dependency references are present * (iii) creates a copy of the master list in dependency order */ static AccessibleObject[] computePropertyDependencies(Object obj, Map properties) { // List of Fields and Methods of the protocol annotated with @Property List unorderedFieldsAndMethods = new LinkedList() ; List orderedFieldsAndMethods = new LinkedList() ; // Maps property name to property object Map propertiesInventory = new HashMap() ; // get the methods for this class and add them to the list if annotated with @Property Method[] methods=obj.getClass().getMethods(); for(int i = 0; i < methods.length; i++) { if (methods[i].isAnnotationPresent(Property.class) && isSetPropertyMethod(methods[i])) { String propertyName = PropertyHelper.getPropertyName(methods[i]) ; unorderedFieldsAndMethods.add(methods[i]) ; propertiesInventory.put(propertyName, methods[i]) ; } } //traverse class hierarchy and find all annotated fields and add them to the list if annotated for(Class clazz=obj.getClass(); clazz != null; clazz=clazz.getSuperclass()) { Field[] fields=clazz.getDeclaredFields(); for(int i = 0; i < fields.length; i++ ) { if (fields[i].isAnnotationPresent(Property.class)) { String propertyName = PropertyHelper.getPropertyName(fields[i], properties) ; unorderedFieldsAndMethods.add(fields[i]) ; // may need to change this based on name parameter of Property propertiesInventory.put(propertyName, fields[i]) ; } } } // at this stage, we have all Fields and Methods annotated with @Property checkDependencyReferencesPresent(unorderedFieldsAndMethods, propertiesInventory) ; // order the fields and methods by dependency orderedFieldsAndMethods = orderFieldsAndMethodsByDependency(unorderedFieldsAndMethods, propertiesInventory) ; // convert to array of Objects AccessibleObject[] result = new AccessibleObject[orderedFieldsAndMethods.size()] ; for(int i = 0; i < orderedFieldsAndMethods.size(); i++) { result[i] = orderedFieldsAndMethods.get(i) ; } return result ; } static List orderFieldsAndMethodsByDependency(List unorderedList, Map propertiesMap) { // Stack to detect cycle in depends relation Stack stack = new Stack() ; // the result list List orderedList = new LinkedList() ; // add the elements from the unordered list to the ordered list // any dependencies will be checked and added first, in recursive manner for(int i = 0; i < unorderedList.size(); i++) { AccessibleObject obj = unorderedList.get(i) ; addPropertyToDependencyList(orderedList, propertiesMap, stack, obj) ; } return orderedList ; } /** * DFS of dependency graph formed by Property annotations and dependsUpon parameter * This is used to create a list of Properties in dependency order */ static void addPropertyToDependencyList(List orderedList, Map props, Stack stack, AccessibleObject obj) { if (orderedList.contains(obj)) return ; if (stack.search(obj) > 0) { throw new RuntimeException("Deadlock in @Property dependency processing") ; } // record the fact that we are processing obj stack.push(obj) ; // process dependencies for this object before adding it to the list Property annotation = obj.getAnnotation(Property.class) ; String dependsClause = annotation.dependsUpon() ; StringTokenizer st = new StringTokenizer(dependsClause, ",") ; while (st.hasMoreTokens()) { String token = st.nextToken().trim(); AccessibleObject dep = props.get(token) ; // if null, throw exception addPropertyToDependencyList(orderedList, props, stack, dep) ; } // indicate we're done with processing dependencies stack.pop() ; // we can now add in dependency order orderedList.add(obj) ; } /* * Checks that for every dependency referred, there is a matching property */ static void checkDependencyReferencesPresent(List objects, Map props) { // iterate overall properties marked by @Property for(int i = 0; i < objects.size(); i++) { // get the Property annotation AccessibleObject ao = objects.get(i) ; Property annotation = ao.getAnnotation(Property.class) ; if (annotation == null) { throw new IllegalArgumentException("@Property annotation is required for checking dependencies;" + " annotation is missing for Field/Method " + ao.toString()) ; } String dependsClause = annotation.dependsUpon() ; if (dependsClause.trim().length() == 0) continue ; // split dependsUpon specifier into tokens; trim each token; search for token in list StringTokenizer st = new StringTokenizer(dependsClause, ",") ; while (st.hasMoreTokens()) { String token = st.nextToken().trim() ; // check that the string representing a property name is in the list boolean found = false ; Set keyset = props.keySet(); for (Iterator iter = keyset.iterator(); iter.hasNext();) { if (iter.next().equals(token)) { found = true ; break ; } } if (!found) { throw new IllegalArgumentException("@Property annotation " + annotation.name() + " has an unresolved dependsUpon property: " + token) ; } } } } public static void resolveAndInvokePropertyMethods(Object obj, Map props) throws Exception { Method[] methods=obj.getClass().getMethods(); for(Method method: methods) { resolveAndInvokePropertyMethod(obj, method, props) ; } } public static void resolveAndInvokePropertyMethod(Object obj, Method method, Map props) throws Exception { String methodName=method.getName(); Property annotation=method.getAnnotation(Property.class); if(annotation != null && isSetPropertyMethod(method)) { String propertyName=PropertyHelper.getPropertyName(method) ; String propertyValue=props.get(propertyName); // if there is a systemProperty attribute defined in the annotation, set the property value from the system property String tmp=grabSystemProp(method.getAnnotation(Property.class)); if(tmp != null) propertyValue=tmp; if(propertyName != null && propertyValue != null) { String deprecated_msg=annotation.deprecatedMessage(); if(deprecated_msg != null && deprecated_msg.length() > 0) { log.warn(method.getDeclaringClass().getSimpleName() + "." + methodName + " has been deprecated : " + deprecated_msg); } } if(propertyValue != null) { Object converted=null; try { converted=PropertyHelper.getConvertedValue(obj, method, props, propertyValue, true); method.invoke(obj, converted); } catch(Exception e) { String name=obj instanceof Protocol? ((Protocol)obj).getName() : obj.getClass().getName(); throw new Exception("Could not assign property " + propertyName + " in " + name + ", method is " + methodName + ", converted value is " + converted, e); } } props.remove(propertyName); } } public static void resolveAndAssignFields(Object obj, Map props) throws Exception { //traverse class hierarchy and find all annotated fields for(Class clazz=obj.getClass(); clazz != null; clazz=clazz.getSuperclass()) { Field[] fields=clazz.getDeclaredFields(); for(Field field: fields) { resolveAndAssignField(obj, field, props) ; } } } public static void resolveAndAssignField(Object obj, Field field, Map props) throws Exception { Property annotation=field.getAnnotation(Property.class); if(annotation != null) { String propertyName = PropertyHelper.getPropertyName(field, props) ; String propertyValue=props.get(propertyName); // if there is a systemProperty attribute defined in the annotation, set the property value from the system property String tmp=grabSystemProp(field.getAnnotation(Property.class)); if(tmp != null) propertyValue=tmp; if(propertyName != null && propertyValue != null) { String deprecated_msg=annotation.deprecatedMessage(); if(deprecated_msg != null && deprecated_msg.length() > 0) { log.warn(field.getDeclaringClass().getSimpleName() + "." + field.getName() + " has been deprecated: " + deprecated_msg); } } if(propertyValue != null || !PropertyHelper.usesDefaultConverter(field)){ Object converted=null; try { converted=PropertyHelper.getConvertedValue(obj, field, props, propertyValue, true); if(converted != null) setField(field, obj, converted); } catch(Exception e) { String name=obj instanceof Protocol? ((Protocol)obj).getName() : obj.getClass().getName(); throw new Exception("Property assignment of " + propertyName + " in " + name + " with original property value " + propertyValue + " and converted to " + converted + " could not be assigned", e); } } props.remove(propertyName); } } public static void removeDeprecatedProperties(Object obj, Map props) throws Exception { //traverse class hierarchy and find all deprecated properties for(Class clazz=obj.getClass(); clazz != null; clazz=clazz.getSuperclass()) { if(clazz.isAnnotationPresent(DeprecatedProperty.class)) { DeprecatedProperty declaredAnnotation=clazz.getAnnotation(DeprecatedProperty.class); String[] deprecatedProperties=declaredAnnotation.names(); for(String propertyName : deprecatedProperties) { String propertyValue=props.get(propertyName); if(propertyValue != null) { if(log.isWarnEnabled()) { String name=obj instanceof Protocol? ((Protocol)obj).getName() : obj.getClass().getName(); log.warn(name + " property " + propertyName + " was deprecated and is ignored"); } props.remove(propertyName); } } } } } public static boolean isSetPropertyMethod(Method method) { return (method.getName().startsWith("set") && method.getReturnType() == java.lang.Void.TYPE && method.getParameterTypes().length == 1); } public static void setField(Field field, Object target, Object value) { if(!Modifier.isPublic(field.getModifiers())) { field.setAccessible(true); } try { field.set(target, value); } catch(IllegalAccessException iae) { throw new IllegalArgumentException("Could not set field " + field, iae); } } public static Object getField(Field field, Object target) { if(!Modifier.isPublic(field.getModifiers())) { field.setAccessible(true); } try { return field.get(target); } catch(IllegalAccessException iae) { throw new IllegalArgumentException("Could not get field " + field, iae); } } private static String grabSystemProp(Property annotation) { String[] system_property_names=annotation.systemProperty(); String retval=null; for(String system_property_name: system_property_names) { if(system_property_name != null && system_property_name.length() > 0) { if(system_property_name.equals(Global.BIND_ADDR) || system_property_name.equals(Global.BIND_ADDR_OLD)) if(Util.isBindAddressPropertyIgnored()) continue; try { retval=System.getProperty(system_property_name); if(retval != null) return retval; } catch(SecurityException ex) { log.error("failed getting system property for " + system_property_name, ex); } } } return retval; } /* --------------------------- End of Private Methods ---------------------------------- */ public static class InetAddressInfo { Protocol protocol ; AccessibleObject fieldOrMethod ; Map properties ; String propertyName ; String stringValue ; Object convertedValue ; boolean isField ; boolean isParameterized ; // is the associated type parametrized? (e.g. Collection) Object baseType ; // what is the base type (e.g. Collection) InetAddressInfo(Protocol protocol, AccessibleObject fieldOrMethod, Map properties, String stringValue, Object convertedValue) { // check input values if (protocol == null) { throw new IllegalArgumentException("Protocol for Field/Method must be non-null") ; } if (fieldOrMethod instanceof Field) { isField = true ; } else if (fieldOrMethod instanceof Method) { isField = false ; } else throw new IllegalArgumentException("AccesibleObject is neither Field nor Method") ; if (properties == null) { throw new IllegalArgumentException("Properties for Field/Method must be non-null") ; } // set the values passed by the user - need to check for null this.protocol = protocol ; this.fieldOrMethod = fieldOrMethod ; this.properties = properties ; this.stringValue = stringValue ; this.convertedValue = convertedValue ; // set the property name Property annotation=fieldOrMethod.getAnnotation(Property.class); if (isField()) propertyName=PropertyHelper.getPropertyName((Field)fieldOrMethod, properties) ; else propertyName=PropertyHelper.getPropertyName((Method)fieldOrMethod) ; // is variable type parameterized this.isParameterized = false ; if (isField()) this.isParameterized = hasParameterizedType((Field)fieldOrMethod) ; else this.isParameterized = hasParameterizedType((Method)fieldOrMethod) ; // if parameterized, what is the base type? this.baseType = null ; if (isField() && isParameterized) { // the Field has a single type ParameterizedType fpt = (ParameterizedType)((Field)fieldOrMethod).getGenericType() ; this.baseType = fpt.getActualTypeArguments()[0] ; } else if (!isField() && isParameterized) { // the Method has several parameters (and so types) Type[] types = (Type[])((Method)fieldOrMethod).getGenericParameterTypes(); ParameterizedType mpt = (ParameterizedType) types[0] ; this.baseType = mpt.getActualTypeArguments()[0] ; } } // Protocol getProtocol() {return protocol ;} Object getProtocol() {return protocol ;} AccessibleObject getFieldOrMethod() {return fieldOrMethod ;} boolean isField() { return isField ; } String getStringValue() {return stringValue ;} String getPropertyName() {return propertyName ;} Map getProperties() {return properties ;} Object getConvertedValue() {return convertedValue ;} boolean isParameterized() {return isParameterized ;} Object getBaseType() { return baseType ;} static boolean hasParameterizedType(Field f) { if (f == null) { throw new IllegalArgumentException("Field argument is null") ; } Type type = f.getGenericType(); return (type instanceof ParameterizedType) ; } static boolean hasParameterizedType(Method m) throws IllegalArgumentException { if (m == null) { throw new IllegalArgumentException("Method argument is null") ; } Type[] types = m.getGenericParameterTypes() ; return (types[0] instanceof ParameterizedType) ; } static boolean isInetAddressRelated(Protocol prot, Field f) { if (hasParameterizedType(f)) { // check for List, List, List ParameterizedType fieldtype = (ParameterizedType) f.getGenericType() ; // check that this parameterized type satisfies our constraints try { parameterizedTypeSanityCheck(fieldtype) ; } catch(IllegalArgumentException e) { // because this Method's parameter fails the sanity check, its probably not an InetAddress related structure return false ; } Class listType = (Class) fieldtype.getActualTypeArguments()[0] ; return isInetAddressOrCompatibleType(listType) ; } else { // check if the non-parameterized type is InetAddress, InetSocketAddress or IpAddress Class fieldtype = f.getType() ; return isInetAddressOrCompatibleType(fieldtype) ; } } /* * Checks if this method's single parameter represents of of the following: * an InetAddress, IpAddress or InetSocketAddress or one of * List, List or List */ static boolean isInetAddressRelated(Method m) { if (hasParameterizedType(m)) { Type[] types = m.getGenericParameterTypes(); ParameterizedType methodParamType = (ParameterizedType)types[0] ; // check that this parameterized type satisfies our constraints try { parameterizedTypeSanityCheck(methodParamType) ; } catch(IllegalArgumentException e) { if(log.isErrorEnabled()) { log.error("Method " + m.getName() + " failed paramaterizedTypeSanityCheck()") ; } // because this Method's parameter fails the sanity check, its probably not // an InetAddress related structure return false ; } Class listType = (Class) methodParamType.getActualTypeArguments()[0] ; return isInetAddressOrCompatibleType(listType) ; } else { Class methodParamType = m.getParameterTypes()[0] ; return isInetAddressOrCompatibleType(methodParamType) ; } } static boolean isInetAddressOrCompatibleType(Class c) { return c.equals(InetAddress.class) || c.equals(InetSocketAddress.class) || c.equals(IpAddress.class) ; } /* * Check if the parameterized type represents one of: * List, List, List */ static void parameterizedTypeSanityCheck(ParameterizedType pt) throws IllegalArgumentException { Type rawType = pt.getRawType() ; Type[] actualTypes = pt.getActualTypeArguments() ; // constraints on use of parameterized types with @Property if (!(rawType instanceof Class && rawType.equals(List.class))) { throw new IllegalArgumentException("Invalid parameterized type definition - parameterized type must be a List") ; } // check for non-parameterized type in List if (!(actualTypes[0] instanceof Class)) { throw new IllegalArgumentException("Invalid parameterized type - List must not contain a parameterized type") ; } } /* * Converts the computedValue to a list of InetAddresses. * Because computedValues may be null, we need to return * a zero length list in some cases. */ public List getInetAddresses() { List addresses = new ArrayList() ; if (getConvertedValue() == null) return addresses ; // if we take only an InetAddress argument if (!isParameterized()) { addresses.add(getInetAddress(getConvertedValue())) ; return addresses ; } // if we take a List or similar else { List values = (List) getConvertedValue() ; if (values.isEmpty()) return addresses ; for (int i = 0; i < values.size(); i++) { addresses.add(getInetAddress(values.get(i))) ; } return addresses ; } } private static InetAddress getInetAddress(Object obj) throws IllegalArgumentException { if (obj == null) throw new IllegalArgumentException("Input argument must represent a non-null IP address") ; if (obj instanceof InetAddress) return (InetAddress) obj ; else if (obj instanceof IpAddress) return ((IpAddress) obj).getIpAddress() ; else if (obj instanceof InetSocketAddress) return ((InetSocketAddress) obj).getAddress() ; else { if (log.isWarnEnabled()) log.warn("Input argument does not represent one of InetAddress...: class=" + obj.getClass().getName()) ; throw new IllegalArgumentException("Input argument does not represent one of InetAddress. IpAddress not InetSocketAddress") ; } } public String toString() { StringBuilder sb = new StringBuilder() ; sb.append("InetAddressInfo(") ; sb.append("protocol=" + protocol.getName()) ; sb.append(", propertyName=" + getPropertyName()) ; sb.append(", string value=" + getStringValue()) ; sb.append(", parameterized=" + isParameterized()) ; if (isParameterized()) sb.append(", baseType=" + getBaseType()) ; sb.append(")") ; return sb.toString(); } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/stack/DefaultRetransmitter.java0000644000175000017500000001121311647260573027744 0ustar moellermoeller package org.jgroups.stack; import org.jgroups.Address; import org.jgroups.util.TimeScheduler; import org.jgroups.util.Util; import java.util.Set; import java.util.concurrent.ConcurrentMap; /** * 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.4 $ */ public class DefaultRetransmitter extends Retransmitter { private final ConcurrentMap msgs=Util.createConcurrentMap(); /** * 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 DefaultRetransmitter(Address sender, RetransmitCommand cmd, TimeScheduler sched) { super(sender, cmd, sched); } /** * 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 */ 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 SeqnoTask(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 String toString() { Set keys=msgs.keySet(); int size=keys.size(); StringBuilder sb=new StringBuilder(); sb.append(size).append(" messages to retransmit"); if(size < 50) sb.append(": ").append(keys); return sb.toString(); } public int size() { return msgs.size(); } protected class SeqnoTask extends Task { private long seqno=-1; protected SeqnoTask(long seqno, Interval intervals, RetransmitCommand cmd, Address msg_sender) { super(intervals, cmd, msg_sender); this.seqno=seqno; } public String toString() { return String.valueOf(seqno); } protected void callRetransmissionCommand() { command.retransmit(seqno, seqno, msg_sender); } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/stack/ExponentialInterval.java0000644000175000017500000000122111647260573027565 0ustar moellermoellerpackage org.jgroups.stack; /** * @author Bela Ban */ 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); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/stack/GossipData.java0000644000175000017500000001144011647260573025634 0ustar moellermoeller package org.jgroups.stack; import org.jgroups.Address; import org.jgroups.PhysicalAddress; 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.util.LinkedList; import java.util.List; import java.util.Collection; import java.util.ArrayList; /** * 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 String logical_name=null; List
                mbrs=null; // GET_RSP Collection physical_addrs=null; // GET_RSP, GET_REQ byte[] buffer=null; // MESSAGE int offset=0; int length=0; public GossipData() { // for streamable } public GossipData(byte type) { this.type=type; } public GossipData(byte type, String group, Address addr) { this(type); this.group=group; this.addr=addr; } public GossipData(byte type, String group, Address addr, List
                mbrs) { this(type, group, addr); this.mbrs=mbrs; } public GossipData(byte type, String group, Address addr, List
                mbrs, List physical_addrs) { this(type, group, addr, mbrs); this.physical_addrs=physical_addrs; } public GossipData(byte type, String group, Address addr, String logical_name, List phys_addrs) { this(type, group, addr); this.logical_name=logical_name; this.physical_addrs=phys_addrs; } public GossipData(byte type, String group, Address addr, byte[] buffer) { this(type, group, addr, buffer, 0, buffer.length); } public GossipData(byte type, String group, Address addr, byte[] buffer, int offset, int length) { this(type, group, addr); this.buffer=buffer; this.offset=offset; this.length=length; } public byte getType() {return type;} public String getGroup() {return group;} public Address getAddress() {return addr;} public String getLogicalName() {return logical_name;} public List
                getMembers() {return mbrs;} public byte[] getBuffer() {return buffer;} public Collection getPhysicalAddresses() { return physical_addrs; } 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); if(logical_name != null) sb.append(", logical_name=" + logical_name); if(mbrs != null && !mbrs.isEmpty()) sb.append(", mbrs=").append(mbrs); if(physical_addrs != null && !physical_addrs.isEmpty()) sb.append(", physical_addrs=").append(Util.printListWithDelimiter(physical_addrs, ", ")); if(buffer != null) sb.append(", buffer: " + length + " bytes"); sb.append(")"); return sb.toString(); } public void writeTo(DataOutputStream out) throws IOException { out.writeByte(type); Util.writeString(group, out); Util.writeAddress(addr, out); Util.writeString(logical_name, out); Util.writeAddresses(mbrs, out); Util.writeAddresses(physical_addrs, out); Util.writeByteBuffer(buffer, offset, length, out); } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { type=in.readByte(); group=Util.readString(in); addr=Util.readAddress(in); logical_name=Util.readString(in); mbrs=(List
                )Util.readAddresses(in, LinkedList.class); physical_addrs=(Collection)Util.readAddresses(in, ArrayList.class); buffer=Util.readByteBuffer(in); if(buffer != null) { offset=0; length=buffer.length; } } public int size() { int retval=Global.BYTE_SIZE; // type retval+=Global.BYTE_SIZE * 3; // presence byte for group and logical_name, buffer if(group != null) retval+=group.length() +2; retval+=Util.size(addr); if(logical_name != null) retval+=logical_name.length() +2; retval+=Util.size(mbrs); retval+=Util.size(physical_addrs); if(buffer != null) retval+=Global.INT_SIZE + length; return retval; } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/stack/GossipRouter.java0000644000175000017500000011134011647260573026243 0ustar moellermoellerpackage org.jgroups.stack; import org.jgroups.Address; import org.jgroups.PhysicalAddress; import org.jgroups.protocols.PingData; import org.jgroups.annotations.ManagedAttribute; import org.jgroups.annotations.ManagedOperation; import org.jgroups.annotations.Property; import org.jgroups.jmx.JmxConfigurator; import org.jgroups.logging.Log; import org.jgroups.logging.LogFactory; import org.jgroups.util.*; import org.jgroups.util.UUID; import javax.management.MBeanServer; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.EOFException; import java.io.IOException; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketTimeoutException; import java.util.*; import java.util.Map.Entry; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.atomic.AtomicBoolean; /** * 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. *

                * Note that a GossipRouter is also a good way of running JGroups in Amazon's EC2 environment which (as of summer 09) * doesn't support IP multicasting. * @author Bela Ban * @author Vladimir Blagojevic * @author Ovidiu Feodorov * @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 GOSSIP_GET=4; // GET(group) --> List (members) public static final byte MESSAGE=10; public static final byte SUSPECT=11; public static final byte PING=12; public static final byte CLOSE=13; public static final byte CONNECT_OK=14; public static final byte OP_FAIL=15; public static final byte DISCONNECT_OK=16; public static final int PORT=12001; @ManagedAttribute(description="server port on which the GossipRouter accepts client connections", writable=true) private int port; @ManagedAttribute(description="address to which the GossipRouter should bind", writable=true, name="bindAddress") private String bindAddressString; @ManagedAttribute(description="time (in msecs) until gossip entry expires", writable=true) private long expiryTime=0; // Maintains associations between groups and their members private final ConcurrentMap> routingTable=new ConcurrentHashMap>(); /** * Store physical address(es) associated with a logical address. Used mainly by TCPGOSSIP */ private final Map> address_mappings=new ConcurrentHashMap>(); private ServerSocket srvSock=null; private InetAddress bindAddress=null; @Property(description="Time (in ms) for setting SO_LINGER on sockets returned from accept(). 0 means do not set SO_LINGER") private long linger_timeout=2000L; @Property(description="Time (in ms) for SO_TIMEOUT on sockets returned from accept(). 0 means don't set SO_TIMEOUT") private long sock_read_timeout=0L; @Property(description="The max queue size of backlogged connections") private int backlog=1000; private final AtomicBoolean running = new AtomicBoolean(false); @ManagedAttribute(description="whether to discard message sent to self", writable=true) private boolean discard_loopbacks=false; protected List connectionTearListeners=new CopyOnWriteArrayList(); protected ThreadFactory default_thread_factory=new DefaultThreadFactory(Util.getGlobalThreadGroup(), "gossip-handlers", true, true); protected Timer timer=null; protected final Log log=LogFactory.getLog(this.getClass()); private boolean jmx=false; private boolean registered=false; public GossipRouter() { this(PORT); } public GossipRouter(int port) { this(port, null); } public GossipRouter(int port, String bindAddressString) { this(port,bindAddressString,false,0); } public GossipRouter(int port, String bindAddressString, boolean jmx) { this(port, bindAddressString,jmx,0); } public GossipRouter(int port, String bindAddressString, boolean jmx, long expiryTime) { this.port = port; this.bindAddressString = bindAddressString; this.jmx = jmx; this.expiryTime = expiryTime; this.connectionTearListeners.add(new FailureDetectionListener()); } 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) { } @Deprecated public static long getGossipRequestTimeout() { return 0; } @Deprecated public void setRoutingClientReplyTimeout(long routingClientReplyTimeout) { } @Deprecated public static long getRoutingClientReplyTimeout() { return 0; } @ManagedAttribute(description="status") public boolean isStarted() { return isRunning(); } 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 static String type2String(int type) { switch (type) { case CONNECT: return "CONNECT"; case DISCONNECT: return "DISCONNECT"; case GOSSIP_GET: return "GOSSIP_GET"; case MESSAGE: return "MESSAGE"; case SUSPECT: return "SUSPECT"; case PING: return "PING"; case CLOSE: return "CLOSE"; case CONNECT_OK: return "CONNECT_OK"; case DISCONNECT_OK: return "DISCONNECT_OK"; case OP_FAIL: return "OP_FAIL"; default: return "unknown (" + type + ")"; } } /** * Lifecycle operation. Called after create(). When this method is called, the managed attributes * have already been set.
                * Brings the Router into a fully functional state. */ @ManagedOperation(description="Lifecycle operation. Called after create(). When this method is called, " + "the managed attributes have already been set. Brings the Router into a fully functional state.") public void start() throws Exception { if(running.compareAndSet(false, true)) { if(jmx && !registered) { MBeanServer server=Util.getMBeanServer(); JmxConfigurator.register(this, server, "jgroups:name=GossipRouter"); registered=true; } if(bindAddressString != null) { bindAddress=InetAddress.getByName(bindAddressString); srvSock=new ServerSocket(port, backlog, bindAddress); } else { srvSock=new ServerSocket(port, backlog); } Runtime.getRuntime().addShutdownHook(new Thread() { public void run() { GossipRouter.this.stop(); } }); // start the main server thread new Thread(new Runnable() { public void run() { mainLoop(); } }, "GossipRouter").start(); long expiryTime = getExpiryTime(); if (expiryTime > 0) { timer = new Timer(true); timer.schedule(new TimerTask() { public void run() { sweep(); } }, expiryTime, expiryTime); } } else { throw new Exception("Router already started."); } } /** * Always called before destroy(). Close connections and frees resources. */ @ManagedOperation(description="Always called before destroy(). Closes connections and frees resources") public void stop() { clear(); if(running.compareAndSet(true, false)){ Util.close(srvSock); if(log.isInfoEnabled()) log.info("router stopped"); } } @ManagedOperation(description="Closes all connections and clears routing table (leave the server socket open)") public void clear() { if(running.get()) { for(ConcurrentMap map: routingTable.values()) { for(ConnectionHandler ce: map.values()) ce.close(); } routingTable.clear(); } } public void destroy() { } @ManagedAttribute(description="operational status", name="running") public boolean isRunning() { return running.get(); } @ManagedOperation(description="dumps the contents of the routing table") public String dumpRoutingTable() { String label="routing"; StringBuilder sb=new StringBuilder(); if(routingTable.isEmpty()) { sb.append("empty ").append(label).append(" table"); } else { boolean first=true; for(Map.Entry> entry : routingTable.entrySet()) { String gname=entry.getKey(); if(!first) sb.append("\n"); else first=false; sb.append(gname + ": "); Map map=entry.getValue(); if(map == null || map.isEmpty()) { sb.append("null"); } else { sb.append(Util.printListWithDelimiter(map.keySet(), ", ")); } } } return sb.toString(); } @ManagedOperation(description="dumps the contents of the routing table") public String dumpRoutingTableDetailed() { String label="routing"; StringBuilder sb=new StringBuilder(); if(routingTable.isEmpty()) { sb.append("empty ").append(label).append(" table"); } else { boolean first=true; for(Map.Entry> entry : routingTable.entrySet()) { String gname=entry.getKey(); if(!first) sb.append("\n"); else first=false; sb.append(gname + ":\n"); Map map=entry.getValue(); if(map == null || map.isEmpty()) { sb.append("null"); } else { for(Map.Entry en: map.entrySet()) { sb.append(en.getKey() + ": "); ConnectionHandler handler=en.getValue(); sb.append("sock=" +handler.sock).append("\n"); } } sb.append("\n"); } } return sb.toString(); } @ManagedOperation(description="dumps the mappings between logical and physical addresses") public String dumpAddresssMappings() { StringBuilder sb=new StringBuilder(); for(Map.Entry> entry: address_mappings.entrySet()) { sb.append(entry.getKey()).append(": ").append(entry.getValue()).append("\n"); } return sb.toString(); } private void mainLoop() { if(bindAddress == null) bindAddress=srvSock.getInetAddress(); printStartupInfo(); while(isRunning()) { Socket sock=null; try { 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); if(log.isDebugEnabled()) log.debug("Accepted connection, socket is " + sock); ConnectionHandler ch=new ConnectionHandler(sock); getDefaultThreadPoolThreadFactory().newThread(ch).start(); } catch(IOException e) { //only consider this exception if GR is not shutdown if(isRunning()) { log.error("failure handling connection from " + sock, e); Util.close(sock); } } } } /** * Removes expired gossip entries (entries older than EXPIRY_TIME msec). * @since 2.2.1 */ private void sweep() { long diff, currentTime = System.currentTimeMillis(); List victims = new ArrayList(); for (Iterator>> it = routingTable.entrySet().iterator(); it.hasNext();) { Map map = it.next().getValue(); if (map == null || map.isEmpty()) { it.remove(); continue; } for (Iterator> it2 = map.entrySet().iterator(); it2.hasNext();) { ConnectionHandler ch = it2.next().getValue(); diff = currentTime - ch.timestamp; if (diff > expiryTime) { victims.add(ch); } } } for (ConnectionHandler v : victims) { v.close(); } } private void route(Address dest, String group, byte[] msg) { if(dest == null) { // send to all members in group if(group == null) { if(log.isErrorEnabled()) log.error("group is null"); } else { sendToAllMembersInGroup(group, msg); } } else { // send unicast ConnectionHandler handler=findAddressEntry(group, dest); if(handler == null) { if(log.isTraceEnabled()) log.trace("cannot find " + dest + " in the routing table, \nrouting table=\n" + dumpRoutingTable()); return; } if(handler.output == null) { if(log.isErrorEnabled()) log.error(dest + " is associated with a null output stream"); return; } try { sendToMember(dest, handler.output, msg); } catch(Exception e) { if(log.isErrorEnabled()) log.error("failed sending message to " + dest + ": " + e.getMessage()); removeEntry(group, dest); // will close socket } } } private void removeEntry(String group, Address addr) { // Remove from routing table ConcurrentMap map; if(group != null) { map=routingTable.get(group); if(map != null && map.remove(addr) != null) { if(log.isTraceEnabled()) log.trace("Removed " +addr + " from group " + group); if(map.isEmpty()) { routingTable.remove(group); if(log.isTraceEnabled()) log.trace("Removed group " + group); } } } else { for(Map.Entry> entry: routingTable.entrySet()) { map=entry.getValue(); if(map != null && map.remove(addr) != null && map.isEmpty()) { routingTable.remove(entry.getKey()); if(log.isTraceEnabled()) log.trace("Removed " + entry.getKey() + " from group " + group); } } } address_mappings.remove(addr); UUID.remove(addr); } /** * @return null if not found */ private ConnectionHandler findAddressEntry(String group, Address addr) { if(group == null || addr == null) return null; ConcurrentMap map=routingTable.get(group); if(map == null) return null; return map.get(addr); } private void sendToAllMembersInGroup(String group, byte[] msg) { final ConcurrentMap map=routingTable.get(group); if(map == null || map.isEmpty()) { if(log.isWarnEnabled()) log.warn("didn't find any members for group " + group); return; } synchronized(map) { for(Map.Entry entry: map.entrySet()) { ConnectionHandler handler=entry.getValue(); DataOutputStream dos=handler.output; if(dos != null) { try { sendToMember(null, dos, msg); } catch(Exception e) { if(log.isWarnEnabled()) log.warn("cannot send to " + entry.getKey() + ": " + e.getMessage()); } } } } } private static void sendToMember(Address dest, final DataOutputStream out, byte[] msg) throws IOException { if(out == null) return; synchronized(out) { GossipData request=new GossipData(GossipRouter.MESSAGE, null, dest, msg); request.writeTo(out); out.flush(); } } private void notifyAbnormalConnectionTear(final ConnectionHandler ch, final Exception e) { for (ConnectionTearListener l : connectionTearListeners) { l.connectionTorn(ch, e); } } public interface ConnectionTearListener { public void connectionTorn(ConnectionHandler ch, Exception e); } /* * https://jira.jboss.org/jira/browse/JGRP-902 */ class FailureDetectionListener implements ConnectionTearListener { public void connectionTorn(ConnectionHandler ch, Exception e) { Set groups = ch.known_groups; for (String group : groups) { if(group == null) continue; Map map = routingTable.get(group); if (map != null && !map.isEmpty()) { for (Iterator> i = map.entrySet().iterator(); i.hasNext();) { ConnectionHandler entry = i.next().getValue(); DataOutputStream stream = entry.output; try { for (Address a : ch.logical_addrs) { GossipData suspect = new GossipData(GossipRouter.SUSPECT); suspect.writeTo(stream); Util.writeAddress(a, stream); stream.flush(); } } catch (Exception ioe) { // intentionally ignored } } } } } } /** * 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); } /** * Handles the requests from a client (RouterStub) */ class ConnectionHandler implements Runnable { private final AtomicBoolean active = new AtomicBoolean(false); private final Socket sock; private final DataOutputStream output; private final DataInputStream input; private final List

                logical_addrs=new ArrayList
                (); Set known_groups = new HashSet(); private long timestamp; public ConnectionHandler(Socket sock) throws IOException { this.sock=sock; this.input=new DataInputStream(sock.getInputStream()); this.output=new DataOutputStream(sock.getOutputStream()); } void close() { if(active.compareAndSet(true, false)) { if(log.isDebugEnabled()) log.debug(this + " is being closed"); Util.close(input); Util.close(output); Util.close(sock); for(Address addr: logical_addrs) { removeEntry(null, addr); } } } public void run() { if(active.compareAndSet(false, true)) { try { if(log.isDebugEnabled()) log.debug(this + " entering receive loop"); readLoop(); } finally { close(); } } } public boolean isRunning() { return active.get(); } private void readLoop() { while(isRunning()) { GossipData request; Address addr; String group; try { request=new GossipData(); request.readFrom(input); byte command=request.getType(); addr=request.getAddress(); group=request.getGroup(); known_groups.add(group); timestamp = System.currentTimeMillis(); if(log.isTraceEnabled()) log.trace(this + " received " + request); switch(command) { case GossipRouter.CONNECT: handleConnect(request, addr, group); break; case GossipRouter.PING: // do nothing here - client doesn't expect response data break; case GossipRouter.MESSAGE: if(request.buffer == null || request.buffer.length == 0) { if(log.isWarnEnabled()) log.warn(this +" received null message"); break; } try { route(addr, request.getGroup(), request.getBuffer()); } catch(Exception e) { if(log.isErrorEnabled()) log.error(this +" failed in routing request to " + addr, e); } break; case GossipRouter.GOSSIP_GET: Set physical_addrs; List mbrs=new ArrayList(); ConcurrentMap map=routingTable.get(group); if(map != null) { for(Address logical_addr: map.keySet()) { physical_addrs=address_mappings.get(logical_addr); PingData rsp=new PingData(logical_addr, null, true, UUID.get(logical_addr), physical_addrs != null? new ArrayList(physical_addrs) : null); mbrs.add(rsp); } } output.writeShort(mbrs.size()); for(PingData data: mbrs) data.writeTo(output); output.flush(); if(log.isDebugEnabled()) log.debug(this + " responded to GOSSIP_GET with " + mbrs); break; case GossipRouter.DISCONNECT: try { removeEntry(group, addr); sendData(new GossipData(DISCONNECT_OK)); if(log.isDebugEnabled()) log.debug(this + " disconnect completed"); } catch(Exception e) { sendData(new GossipData(OP_FAIL)); } break; case GossipRouter.CLOSE: close(); break; case -1: // EOF notifyAbnormalConnectionTear(this, new EOFException("Connection broken")); break; } if(log.isTraceEnabled()) log.trace(this + " processed " + request); } catch(SocketTimeoutException ste) { } catch(IOException ioex) { notifyAbnormalConnectionTear(this, ioex); break; } catch(Exception ex) { if (active.get()) { if (log.isWarnEnabled()) log.warn("Exception in ConnectionHandler thread", ex); } break; } } } private void handleConnect(GossipData request, Address addr, String group) throws Exception { ConcurrentMap map = null; try { checkExistingConnection(addr,group); String logical_name = request.getLogicalName(); if (logical_name != null && addr instanceof org.jgroups.util.UUID) org.jgroups.util.UUID.add(addr, logical_name); // group name, logical address, logical name, physical addresses (could be null) logical_addrs.add(addr); // allows us to remove the entries for this connection on // socket close map = routingTable.get(group); if (map == null) { map = new ConcurrentHashMap(); routingTable.put(group, map); // no concurrent requests on the same connection } map.put(addr, this); Set physical_addrs; if (request.getPhysicalAddresses() != null) { physical_addrs = address_mappings.get(addr); if (physical_addrs == null) { physical_addrs = new HashSet(); address_mappings.put(addr, physical_addrs); } physical_addrs.addAll(request.getPhysicalAddresses()); } sendStatus(CONNECT_OK); if(log.isDebugEnabled()) log.debug(this + " connection handshake completed, added " +addr + " to group "+ group); } catch (Exception e) { removeEntry(group, addr); sendStatus(OP_FAIL); throw new Exception("Unsuccessful connection setup handshake for " + this); } } private boolean checkExistingConnection(Address addr, String group) throws Exception { boolean isOldExists = false; if (address_mappings.containsKey(addr)) { ConcurrentMap map = null; ConnectionHandler oldConnectionH = null; if (group != null) { map = routingTable.get(group); if (map != null) { oldConnectionH = map.get(addr); } } else { for (Map.Entry> entry : routingTable .entrySet()) { map = entry.getValue(); if (map != null) { oldConnectionH = map.get(addr); } } } if (oldConnectionH != null) { isOldExists = true; if (log.isDebugEnabled()) { log.debug("Found old connection[" + oldConnectionH + "] for addr[" + addr + "]. Closing old connection ..."); } oldConnectionH.close(); } else { if (log.isDebugEnabled()) { log.debug("No old connection for addr[" + addr + "] exists"); } } } return isOldExists; } private void sendStatus(byte status) { try { output.writeByte(status); output.flush(); } catch (IOException e1) { //ignored } } private void sendData(GossipData data) { try { data.writeTo(output); output.flush(); } catch (IOException e1) { //ignored } } public String toString() { StringBuilder sb=new StringBuilder(); sb.append("ConnectionHandler[peer: " + sock.getInetAddress()); if(!logical_addrs.isEmpty()) sb.append(", logical_addrs: " + Util.printListWithDelimiter(logical_addrs, ", ")); sb.append("]"); return sb.toString(); } } public static void main(String[] args) throws Exception { int port=12001; int backlog=0; long soLinger=-1; long soTimeout=-1; long expiry_time=0; GossipRouter router=null; String bind_addr=null; boolean jmx=false; for(int i=0; i < args.length; i++) { String 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_time=Long.parseLong(args[++i]); continue; } if("-jmx".equals(arg)) { jmx=true; continue; } // this option is not used and should be deprecated/removed in a future release if("-timeout".equals(arg)) { System.out.println(" -timeout is deprecated 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 deprecated 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. CTRL-C to exit JVM"); try { router=new GossipRouter(port, bind_addr, jmx); if(backlog > 0) router.setBacklog(backlog); if(soTimeout >= 0) router.setSocketReadTimeout(soTimeout); if(soLinger >= 0) router.setLingerTimeout(soLinger); if(expiry_time > 0) router.setExpiryTime(expiry_time); 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(); 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(" -jmx - Expose attributes and operations via JMX."); 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(); System.out.println(" -expiry - Time for closing idle connections. 0"); System.out.println(" means don't expire."); System.out.println(); } }libjgroups-java-2.12.2.Final.orig/src/org/jgroups/stack/Interval.java0000644000175000017500000000066411647260573025370 0ustar moellermoeller 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 */ 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(); } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/stack/IpAddress.java0000644000175000017500000002656711647260573025474 0ustar moellermoeller package org.jgroups.stack; import org.jgroups.logging.Log; import org.jgroups.logging.LogFactory; import org.jgroups.Address; import org.jgroups.Global; import org.jgroups.PhysicalAddress; import org.jgroups.util.Util; import java.io.*; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.UnknownHostException; import java.net.Inet6Address; /** * 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 PhysicalAddress { 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(Address 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; 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; if(resolve_dns) { host_name=ip_addr.getHostName(); } 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); if(ip_addr instanceof Inet6Address) out.writeInt(((Inet6Address)ip_addr).getScopeId()); } 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); if(len == Global.IPV6_SIZE) { int scope_id=in.readInt(); this.ip_addr=Inet6Address.getByAddress(null, a, scope_id); } else { 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(ip_addr instanceof Inet6Address) tmp_size+=Global.INT_SIZE; } 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; } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/stack/NakReceiverWindow.java0000644000175000017500000004765311647260573027203 0ustar moellermoeller package org.jgroups.stack; import org.jgroups.Address; import org.jgroups.Message; import org.jgroups.annotations.GuardedBy; import org.jgroups.logging.Log; import org.jgroups.logging.LogFactory; import org.jgroups.util.RetransmitTable; import org.jgroups.util.TimeScheduler; import java.util.LinkedList; import java.util.List; 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 */ 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(); private volatile boolean running=true; /** 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 RetransmitTable xmit_table; private final AtomicBoolean processing=new AtomicBoolean(false); /** 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(sender, cmd, highest_delivered_seqno, lowest_seqno, sched, true); } public NakReceiverWindow(Address sender, Retransmitter.RetransmitCommand cmd, long highest_delivered_seqno, long lowest_seqno, TimeScheduler sched, boolean use_range_based_retransmitter) { this(sender, cmd, highest_delivered_seqno, lowest_seqno, sched, use_range_based_retransmitter, 5, 10000, 1.2, 5 * 60 * 1000, false); } public NakReceiverWindow(Address sender, Retransmitter.RetransmitCommand cmd, long highest_delivered_seqno, long lowest_seqno, TimeScheduler sched, boolean use_range_based_retransmitter, int num_rows, int msgs_per_row, double resize_factor, long max_compaction_time, boolean automatic_purging) { 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=use_range_based_retransmitter? new RangeBasedRetransmitter(sender, cmd, sched) : new DefaultRetransmitter(sender, cmd, sched); xmit_table=new RetransmitTable(num_rows, msgs_per_row, low, resize_factor, max_compaction_time, automatic_purging); } /** * 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); } public AtomicBoolean getProcessing() { return processing; } public void setRetransmitTimeouts(Interval timeouts) { retransmitter.setRetransmitTimeouts(timeouts); } @Deprecated public void setDiscardDeliveredMessages(boolean flag) { } @Deprecated public int getMaxXmitBufSize() { return 0; } @Deprecated public void setMaxXmitBufSize(int 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; } } public int getRetransmiTableSize() {return xmit_table.size();} public int getRetransmitTableCapacity() {return xmit_table.capacity();} public double getRetransmitTableFillFactor() {return xmit_table.getFillFactor();} public void compact() { xmit_table.compact(); } /** * 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(final long seqno, final Message msg) { long old_next, next_to_add; int num_xmits=0; lock.writeLock().lock(); try { if(!running) return false; 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(seqno, msg); return true; } // Case #2: we received a message that has already been delivered: discard it if(seqno <= highest_delivered) { if(log.isTraceEnabled()) log.trace("seqno " + seqno + " is smaller than " + next_to_add + "); discarding message"); 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 existing=xmit_table.putIfAbsent(seqno, msg); if(existing != null) return false; // key/value was present num_xmits=retransmitter.remove(seqno); if(log.isTraceEnabled()) log.trace(new StringBuilder("added missing msg ").append(msg.getSrc()).append('#').append(seqno)); return true; } // Case #4: we received a seqno higher than expected: 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); lock.writeLock().unlock(); } if(listener != null && num_xmits > 0) { try {listener.missingMessageReceived(seqno, msg.getSrc());} catch(Throwable t) {} } return true; } public Message remove() { return remove(true, false); } public Message remove(boolean acquire_lock, boolean remove_msg) { Message retval; if(acquire_lock) lock.writeLock().lock(); try { long next=highest_delivered +1; retval=remove_msg? xmit_table.remove(next) : xmit_table.get(next); if(retval != null) { // message exists and is ready for delivery highest_delivered=next; return retval; } return null; } finally { if(acquire_lock) 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(final AtomicBoolean processing) { return removeMany(processing, false, 0); } /** * Removes as many messages as possible * @param remove_msgs Removes messages from xmit_table * @param max_results Max number of messages to remove in one batch * @return List A list of messages, or null if no available messages were found */ public List removeMany(final AtomicBoolean processing, boolean remove_msgs, int max_results) { List retval=null; int num_results=0; lock.writeLock().lock(); try { while(true) { long next=highest_delivered +1; Message msg=remove_msgs? xmit_table.remove(next) : xmit_table.get(next); if(msg != null) { // message exists and is ready for delivery highest_delivered=next; if(retval == null) retval=new LinkedList(); retval.add(msg); if(max_results <= 0 || ++num_results < max_results) continue; } if((retval == null || retval.isEmpty()) && processing != null) processing.set(false); return retval; } } finally { lock.writeLock().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.isWarnEnabled()) log.warn("seqno " + seqno + " is > highest_delivered (" + highest_delivered + ";) ignoring stability message"); return; } // we need to remove all seqnos *including* seqno xmit_table.purge(seqno); // 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(); } } /** * Destroys the NakReceiverWindow. After this method returns, no new messages can be added and a new * NakReceiverWindow should be used instead. Note that messages can still be removed though. */ public void destroy() { lock.writeLock().lock(); try { running=false; retransmitter.reset(); xmit_table.clear(); low=0; highest_delivered=0; // next (=first) to deliver will be 1 highest_received=0; highest_stability_seqno=0; } finally { lock.writeLock().unlock(); } } /** Returns the lowest, highest delivered and highest received seqnos */ public long[] getDigest() { lock.readLock().lock(); try { long[] retval=new long[3]; retval[0]=low; retval[1]=highest_delivered; retval[2]=highest_received; return retval; } finally { lock.readLock().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(); } } public long setHighestDelivered(long new_val) { lock.writeLock().lock(); try { long retval=highest_delivered; highest_delivered=new_val; return retval; } finally { lock.writeLock().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(); } } /** * Returns a list of messages in the range [from .. to], including from and to * @param from * @param to * @return A list of messages, or null if none in range [from .. to] was found */ public List get(long from, long to) { lock.readLock().lock(); try { return xmit_table.get(from, to); } 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 */ protected String printMessages() { StringBuilder sb=new StringBuilder(); lock.readLock().lock(); try { sb.append('[').append(low).append(" : ").append(highest_delivered).append(" (").append(highest_received).append(")"); if(xmit_table != null && !xmit_table.isEmpty()) { int non_received=xmit_table.getNullMessages(highest_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(); } finally { lock.readLock().unlock(); } } 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(); } public String printRetransmitStats() { return retransmitter instanceof RangeBasedRetransmitter? ((RangeBasedRetransmitter)retransmitter).printStats() : "n/a"; } /* ------------------------------- Private Methods -------------------------------------- */ /* --------------------------- End of Private Methods ----------------------------------- */ } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/stack/Protocol.java0000644000175000017500000003733411647260573025411 0ustar moellermoeller package org.jgroups.stack; import org.jgroups.Event; import org.jgroups.conf.ClassConfigurator; import org.jgroups.annotations.DeprecatedProperty; import org.jgroups.annotations.ManagedAttribute; import org.jgroups.annotations.Property; import org.jgroups.jmx.ResourceDMBean; import org.jgroups.logging.Log; import org.jgroups.logging.LogFactory; import org.jgroups.protocols.TP; import org.jgroups.util.SocketFactory; import org.jgroups.util.ThreadFactory; import org.jgroups.util.Util; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.*; /** * 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 */ @DeprecatedProperty(names={"down_thread","down_thread_prio","up_thread","up_thread_prio"}) public abstract class Protocol { protected Protocol up_prot=null, down_prot=null; protected ProtocolStack stack=null; @Property(description="Determines whether to collect statistics (and expose them via JMX). Default is true",writable=true) protected boolean stats=true; @Property(description="Enables ergonomics: dynamically find the best values for properties at runtime") protected boolean ergonomics=true; /** The name of the protocol. Is by default set to the protocol's classname. This property should rarely need to * be set, e.g. only in cases where we want to create more than 1 protocol of the same class in the same stack */ @Property(name="name",description="Give the protocol a different name if needed so we can have multiple " + "instances of it in the same stack (also change ID)",writable=false) protected String name=getClass().getSimpleName(); @Property(description="Give the protocol a different ID if needed so we can have multiple " + "instances of it in the same stack",writable=false) protected short id=ClassConfigurator.getProtocolId(getClass()); protected final Log log=LogFactory.getLog(this.getClass()); /** * Sets the level of a logger. This method is used to dynamically change the logging level of a * running system, e.g. via JMX. The appender of a level needs to exist. * @param level The new level. Valid values are "fatal", "error", "warn", "info", "debug", "trace" * (capitalization not relevant) */ @Property(name="level", description="Sets the logger level (see javadocs)") public void setLevel(String level) { log.setLevel(level); } public String getLevel() { return log.getLevel(); } public boolean isErgonomics() { return ergonomics; } public void setErgonomics(boolean ergonomics) { this.ergonomics=ergonomics; } /** * Configures the protocol initially. A configuration string consists of name=value * items, separated by a ';' (semicolon), e.g.:

                     * "loopback=false;unicast_inport=4444"
                     * 
                * @deprecated The properties are now set through the @Property annotation on the attribute or setter */ protected boolean setProperties(Properties props) { throw new UnsupportedOperationException("deprecated; use a setter instead"); } /** * Sets a property * @param key * @param val * @deprecated Use the corresponding setter instead */ public void setProperty(String key, String val) { throw new UnsupportedOperationException("deprecated; use a setter instead"); } /** 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. * @deprecated Use a setter instead */ public boolean setPropertiesInternal(Properties props) { throw new UnsupportedOperationException("use a setter instead"); } public Object getValue(String name) { if(name == null) return null; Field field=Util.getField(getClass(), name); if(field == null) throw new IllegalArgumentException("field \"" + name + "\n not found"); return Configurator.getField(field, this); } public Protocol setValues(Map values) { if(values == null) return this; for(Map.Entry entry: values.entrySet()) { String attrname=entry.getKey(); Object value=entry.getValue(); Field field=Util.getField(getClass(), attrname); if(field != null) { Configurator.setField(field, this, value); } } return this; } public Protocol setValue(String name, Object value) { if(name == null || value == null) return this; Field field=Util.getField(getClass(), name); if(field == null) throw new IllegalArgumentException("field \"" + name + "\n not found"); Configurator.setField(field, this, value); return this; } /** * @return * @deprecated Use a getter to get the actual instance variable */ public Properties getProperties() { if(log.isWarnEnabled()) log.warn("deprecated feature: please use a setter instead"); return new Properties(); } public ProtocolStack getProtocolStack(){ return stack; } /** * After configuring the protocol itself from the properties defined in the XML config, a protocol might have * additional objects which need to be configured. This callback allows a protocol developer to configure those * other objects. This call is guaranteed to be invoked after the protocol itself has * been configured. See AUTH for an example. * @return */ protected List getConfigurableObjects() { return null; } 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; } /** * Returns the SocketFactory associated with this protocol, if overridden in a subclass, or passes the call down * @return SocketFactory */ public SocketFactory getSocketFactory() { return down_prot != null? down_prot.getSocketFactory() : null; } /** * Sets a SocketFactory. Socket factories are typically provided by the transport ({@link org.jgroups.protocols.TP}) * or {@link org.jgroups.protocols.TP.ProtocolAdapter} * @param factory */ public void setSocketFactory(SocketFactory factory) { if(down_prot != null) down_prot.setSocketFactory(factory); } /** @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() { HashMap map=new HashMap(); for(Class clazz=this.getClass();clazz != null;clazz=clazz.getSuperclass()) { Field[] fields=clazz.getDeclaredFields(); for(Field field: fields) { if(field.isAnnotationPresent(ManagedAttribute.class) || (field.isAnnotationPresent(Property.class) && field.getAnnotation(Property.class).exposeAsManagedAttribute())) { String attributeName=field.getName(); try { field.setAccessible(true); Object value=field.get(this); map.put(attributeName, value != null? value.toString() : null); } catch(Exception e) { log.warn("Could not retrieve value of attribute (field) " + attributeName,e); } } } Method[] methods=this.getClass().getMethods(); for(Method method: methods) { if(method.isAnnotationPresent(ManagedAttribute.class) || (method.isAnnotationPresent(Property.class) && method.getAnnotation(Property.class).exposeAsManagedAttribute())) { String method_name=method.getName(); if(method_name.startsWith("is") || method_name.startsWith("get")) { try { Object value=method.invoke(this); String attributeName=Util.methodNameToAttributeName(method_name); map.put(attributeName, value != null? value.toString() : null); } catch(Exception e) { log.warn("Could not retrieve value of attribute (method) " + method_name,e); } } else if(method_name.startsWith("set")) { String stem=method_name.substring(3); Method getter=ResourceDMBean.findGetter(getClass(), stem); if(getter != null) { try { Object value=getter.invoke(this); String attributeName=Util.methodNameToAttributeName(method_name); map.put(attributeName, value != null? value.toString() : null); } catch(Exception e) { log.warn("Could not retrieve value of attribute (method) " + method_name, e); } } } } } } return map; } /** * 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; } /** All protocol names have to be unique ! */ public String getName() { return name; } public short getId() { return id; } public void setId(short id) { this.id=id; } 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); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/stack/ProtocolStack.java0000644000175000017500000012104011647260573026363 0ustar moellermoellerpackage org.jgroups.stack; import org.jgroups.*; import org.jgroups.annotations.Property; import org.jgroups.conf.ClassConfigurator; import org.jgroups.conf.PropertyConverter; import org.jgroups.conf.ProtocolConfiguration; import org.jgroups.protocols.TP; import org.jgroups.util.ThreadFactory; import org.jgroups.util.TimeScheduler; import org.jgroups.util.Tuple; import org.jgroups.util.Util; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.*; import java.util.Map.Entry; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; /** * 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 */ 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() /** * Holds the shared transports, keyed by 'TP.singleton_name'. The values are the transport and the use count for * init() (decremented by destroy()) and start() (decremented by stop() */ private static final ConcurrentMap> singleton_transports=new ConcurrentHashMap>(); private Protocol top_prot; private Protocol bottom_prot; // protected List configs; private JChannel channel; private volatile boolean stopped=true; private final TP.ProbeHandler props_handler=new TP.ProbeHandler() { public Map handleProbe(String... keys) { for(String key: keys) { if(key.equals("props")) { String tmp=printProtocolSpec(true); HashMap map=new HashMap(1); map.put("props", tmp); return map; } if(key.startsWith("print-protocols")) { List prots=getProtocols(); Collections.reverse(prots); StringBuilder sb=new StringBuilder(); for(Protocol prot: prots) sb.append(prot.getName()).append("\n"); HashMap map=new HashMap(1); map.put("protocols", sb.toString()); return map; } if(key.startsWith("remove-protocol")) { key=key.substring("remove-protocol".length()); int index=key.indexOf("="); if(index != -1) { String prot_name=key.substring(index +1); if(prot_name != null && prot_name.length() > 0) { try { Protocol removed=removeProtocol(prot_name); if(removed != null) log.debug("removed protocol " + prot_name + " from stack"); } catch(Exception e) { log.error("failed removing protocol " + prot_name, e); } } } } if(key.startsWith("insert-protocol")) { key=key.substring("insert-protocol".length()+1); int index=key.indexOf("="); if(index == -1) break; // 1. name of the protocol to be inserted String prot_name=key.substring(0, index).trim(); Protocol prot=null; try { prot=createProtocol(prot_name); prot.init(); prot.start(); } catch(Exception e) { log.error("failed creating an instance of " + prot_name, e); break; } key=key.substring(index+1); index=key.indexOf('='); if(index == -1) { log.error("= missing in insert-protocol command"); break; } // 2. "above" or "below" String tmp=key.substring(0, index); if(!tmp.equalsIgnoreCase("above") && !tmp.equalsIgnoreCase("below")) { log.error("Missing \"above\" or \"below\" in insert-protocol command"); break; } key=key.substring(index+1); String neighbor_prot=key.trim(); Protocol neighbor=findProtocol(neighbor_prot); if(neighbor == null) { log.error("Neighbor protocol " + neighbor_prot + " not found in stack"); break; } int position=tmp.equalsIgnoreCase("above")? ABOVE : BELOW; try { insertProtocol(prot, position, neighbor.getClass()); } catch(Exception e) { log.error("failed inserting protocol " + prot_name + " " + tmp + " " + neighbor_prot, e); } } } return null; } public String[] supportedKeys() { return new String[]{"props", "print-protocols", "\nremove-protocol=", "\ninsert-protocol=;above | below="}; } }; public ProtocolStack(JChannel channel) throws ChannelException { // this.configs=configs; this.channel=channel; Class tmp=ClassConfigurator.class; // load this class, trigger init() try { tmp.newInstance(); } catch(Exception e) { throw new ChannelException("failed initializing ClassConfigurator", e); } } /** Used for programmatic creation of ProtocolStack */ public ProtocolStack() { } public JChannel getChannel() {return channel;} public void setChannel(JChannel ch) { this.channel=ch; } /** * @deprecated Use {@link org.jgroups.stack.Protocol#getThreadFactory()} instead * @return */ public ThreadFactory 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) { } /** * @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.getMinThreads(); } 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 List getProtocols() { List v=new ArrayList(15); Protocol p=top_prot; while(p != null) { v.add(p); p=p.getDownProtocol(); } return v; } public List copyProtocols(ProtocolStack targetStack) throws IllegalAccessException, InstantiationException { List list=getProtocols(); List retval=new ArrayList(list.size()); for(Protocol prot: list) { Protocol new_prot=prot.getClass().newInstance(); new_prot.setProtocolStack(targetStack); retval.add(new_prot); for(Class clazz=prot.getClass(); clazz != null; clazz=clazz.getSuperclass()) { // copy all fields marked with @Property Field[] fields=clazz.getDeclaredFields(); for(Field field: fields) { if(field.isAnnotationPresent(Property.class)) { Object value=Configurator.getField(field, prot); Configurator.setField(field, new_prot, value); } } // copy all setters marked with @Property Method[] methods=clazz.getDeclaredMethods(); for(Method method: methods) { String methodName=method.getName(); if(method.isAnnotationPresent(Property.class) && Configurator.isSetPropertyMethod(method)) { Property annotation=method.getAnnotation(Property.class); List possible_names=new LinkedList(); if(annotation.name() != null) possible_names.add(annotation.name()); possible_names.add(methodName.substring(3)); possible_names.add(Util.methodNameToAttributeName(methodName)); Field field=findField(prot, possible_names); if(field != null) { Object value=Configurator.getField(field, prot); Configurator.setField(field, new_prot, value); } } } } } return retval; } private static Field findField(Object target, List possible_names) { if(target == null) return null; for(Class clazz=target.getClass(); clazz != null; clazz=clazz.getSuperclass()) { for(String name: possible_names) { try { Field field=clazz.getDeclaredField(name); if(field != null) return field; } catch(NoSuchFieldException e) { } } } return null; } /** Returns the bottom most protocol */ public TP getTransport() { return (TP)getBottomProtocol(); } 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) { return dumpStats(protocol_name, null); } public Map dumpStats(String protocol_name, List attrs) { Protocol prot=findProtocol(protocol_name); if(prot == null) return null; Map retval=new HashMap(), tmp; tmp=prot.dumpStats(); if(tmp != null) { if(attrs != null && !attrs.isEmpty()) { // weed out attrs not in list for(Iterator it=tmp.keySet().iterator(); it.hasNext();) { String attrname=it.next(); boolean found=false; for(String attr: attrs) { if(attrname.startsWith(attr)) { found=true; break; // found } } if(!found) it.remove(); } } retval.put(protocol_name, tmp); } 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.dumpTimerTasks(); } 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(); List protocols=getProtocols(); if(protocols == null || protocols.isEmpty()) return null; boolean first_colon_printed=false; Collections.reverse(protocols); for(Protocol prot: protocols) { String prot_name=prot.getClass().getName(); int index=prot_name.indexOf(Global.PREFIX); if(index >= 0) prot_name=prot_name.substring(Global.PREFIX.length()); if(first_colon_printed) { sb.append(":"); } else { first_colon_printed=true; } sb.append(prot_name); if(include_properties) { Map tmp=getProps(prot); if(!tmp.isEmpty()) { boolean printed=false; sb.append("("); for(Map.Entry entry: tmp.entrySet()) { if(printed) { sb.append(";"); } else { printed=true; } sb.append(entry.getKey()).append("=").append(entry.getValue()); } sb.append(")\n"); } } } return sb.toString(); } public String printProtocolSpecAsXML() { StringBuilder sb=new StringBuilder(); Protocol prot=bottom_prot; int len, max_len=30; sb.append("\n"); while(prot != null) { String prot_name=prot.getName(); if(prot_name != null) { if("ProtocolStack".equals(prot_name)) break; sb.append(" <").append(prot_name).append(" "); Map tmpProps=getProps(prot); if(tmpProps != null) { len=prot_name.length(); String s; for(Iterator> it=tmpProps.entrySet().iterator();it.hasNext();) { Entry 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() { return printProtocolSpecAsPlainString(false); } private String printProtocolSpecAsPlainString(boolean print_props) { StringBuilder sb=new StringBuilder(); List protocols=getProtocols(); if(protocols == null) return null; Collections.reverse(protocols); for(Protocol prot: protocols) { sb.append(prot.getClass().getName()).append("\n"); if(print_props) { Map tmp=getProps(prot); for(Map.Entry entry: tmp.entrySet()) { sb.append(" ").append(entry.getKey()).append("=").append(entry.getValue()).append("\n"); } } } return sb.toString(); } private static Map getProps(Protocol prot) { Map retval=new HashMap(); for(Class clazz=prot.getClass(); clazz != null; clazz=clazz.getSuperclass()) { // copy all fields marked with @Property Field[] fields=clazz.getDeclaredFields(); Property annotation; for(Field field: fields) { if(field.isAnnotationPresent(Property.class)) { Object value=Configurator.getField(field, prot); if(value != null) { annotation=field.getAnnotation(Property.class); Class conv_class=annotation.converter(); PropertyConverter conv=null; try { conv=(PropertyConverter)conv_class.newInstance(); } catch(Exception e) { } String tmp=conv != null? conv.toString(value) : value.toString(); retval.put(field.getName(), tmp); } } } // copy all setters marked with @Property Method[] methods=clazz.getDeclaredMethods(); for(Method method: methods) { String methodName=method.getName(); if(method.isAnnotationPresent(Property.class) && Configurator.isSetPropertyMethod(method)) { annotation=method.getAnnotation(Property.class); List possible_names=new LinkedList(); if(annotation.name() != null) possible_names.add(annotation.name()); possible_names.add(methodName.substring(3)); possible_names.add(Util.methodNameToAttributeName(methodName)); Field field=findField(prot, possible_names); if(field != null) { Object value=Configurator.getField(field, prot); if(value != null) { Class conv_class=annotation.converter(); PropertyConverter conv=null; try { conv=(PropertyConverter)conv_class.newInstance(); } catch(Exception e) { } String tmp=conv != null? conv.toString(value) : value.toString(); retval.put(field.getName(), tmp); } } } } } return retval; } public void setup(List configs) throws Exception { if(top_prot == null) { top_prot=new Configurator(this).setupProtocolStack(configs); top_prot.setUpProtocol(this); this.setDownProtocol(top_prot); bottom_prot=getBottomProtocol(); initProtocolStack(); } } public void setup(ProtocolStack stack) throws Exception { if(top_prot == null) { top_prot=new Configurator(this).setupProtocolStack(stack); top_prot.setUpProtocol(this); this.setDownProtocol(top_prot); bottom_prot=getBottomProtocol(); initProtocolStack(); } } /** * Adds a protocol at the tail of the protocol list * @param prot * @return * @since 2.11 */ public ProtocolStack addProtocol(Protocol prot) { if(prot == null) return this; prot.setUpProtocol(this); if(bottom_prot == null) { top_prot=bottom_prot=prot; return this; } prot.setDownProtocol(top_prot); prot.getDownProtocol().setUpProtocol(prot); top_prot=prot; return this; } /** * Adds a list of protocols * @param prots * @return * @since 2.11 */ public ProtocolStack addProtocols(Protocol ... prots) { if(prots != null) { for(Protocol prot: prots) addProtocol(prot); } return this; } /** * Adds a list of protocols * @param prots * @return * @since 2.1 */ public ProtocolStack addProtocols(List prots) { if(prots != null) { for(Protocol prot: prots) addProtocol(prot); } return 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 { if(neighbor_prot == null) throw new IllegalArgumentException("neighbor_prot is null"); if(position != ProtocolStack.ABOVE && position != ProtocolStack.BELOW) throw new IllegalArgumentException("position has to be ABOVE or BELOW"); Protocol neighbor=findProtocol(neighbor_prot); if(neighbor == null) throw new IllegalArgumentException("protocol \"" + neighbor_prot + "\" not found in " + stack.printProtocolSpec(false)); if(position == ProtocolStack.BELOW && neighbor instanceof TP) throw new IllegalArgumentException("Cannot insert protocol " + prot.getName() + " below transport protocol"); insertProtocolInStack(prot, neighbor, position); } public void insertProtocolInStack(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(); checkAndSwitchTop(neighbor, prot); prot.setUpProtocol(above); if(above != null) above.setDownProtocol(prot); prot.setDownProtocol(neighbor); neighbor.setUpProtocol(prot); } } private void checkAndSwitchTop(Protocol oldTop, Protocol newTop){ if(oldTop == top_prot) { top_prot = newTop; top_prot.setUpProtocol(this); } } public void insertProtocol(Protocol prot, int position, Class neighbor_prot) throws Exception { if(neighbor_prot == null) throw new IllegalArgumentException("neighbor_prot is null"); if(position != ProtocolStack.ABOVE && position != ProtocolStack.BELOW) throw new IllegalArgumentException("position has to be ABOVE or BELOW"); Protocol neighbor=findProtocol(neighbor_prot); if(neighbor == null) throw new IllegalArgumentException("protocol \"" + neighbor_prot + "\" not found in " + stack.printProtocolSpec(false)); insertProtocolInStack(prot, neighbor, position); } public void insertProtocol(Protocol prot, int position, Class ... neighbor_prots) throws Exception { if(neighbor_prots == null) throw new IllegalArgumentException("neighbor_prots is null"); if(position != ProtocolStack.ABOVE && position != ProtocolStack.BELOW) throw new IllegalArgumentException("position has to be ABOVE or BELOW"); Protocol neighbor=findProtocol(neighbor_prots); if(neighbor == null) throw new IllegalArgumentException("protocol \"" + neighbor_prots.toString() + "\" not found in " + stack.printProtocolSpec(false)); insertProtocolInStack(prot, neighbor, position); } public void insertProtocolAtTop(Protocol prot) { if(prot == null) throw new IllegalArgumentException("prot needs to be non-null"); // check if prot already exists (we cannot have more than 1 protocol of a given class) Class clazz=prot.getClass(); Protocol existing_instance=findProtocol(clazz); if(existing_instance != null) return; top_prot.up_prot=prot; prot.down_prot=top_prot; prot.up_prot=this; top_prot=prot; if(log.isDebugEnabled()) log.debug("inserted " + prot + " at the top of the stack"); } /** * 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 { if(prot_name == null) return null; Protocol prot=findProtocol(prot_name); if(prot == null) return null; Protocol above=prot.getUpProtocol(), below=prot.getDownProtocol(); checkAndSwitchTop(prot, below); if(above != null) above.setDownProtocol(below); if(below != null) below.setUpProtocol(above); prot.setUpProtocol(null); prot.setDownProtocol(null); try { prot.stop(); } catch(Throwable t) { log.error("failed stopping " + prot.getName() + ": " + t); } try { prot.destroy(); } catch(Throwable t) { log.error("failed destroying " + prot.getName() + ": " + t); } return prot; } public Protocol removeProtocol(Class ... protocols) { Protocol retval=null; if(protocols != null) for(Class cl: protocols) { Protocol tmp=removeProtocol(cl); if(tmp != null) retval=tmp; } return retval; } public Protocol removeProtocol(Class prot) { if(prot == null) return null; Protocol retval=findProtocol(prot); if(retval == null) return null; Protocol above=retval.getUpProtocol(), below=retval.getDownProtocol(); checkAndSwitchTop(retval, below); if(above != null) above.setDownProtocol(below); if(below != null) below.setUpProtocol(above); retval.setUpProtocol(null); retval.setDownProtocol(null); return retval; } /** 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 getBottomProtocol() { Protocol curr_prot=this; while(curr_prot != null && curr_prot.getDownProtocol() !=null) { curr_prot=curr_prot.getDownProtocol(); } return curr_prot; } public Protocol getTopProtocol() { return top_prot; } 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; } /** * Finds the first protocol of a list and returns it. Returns null if no protocol can be found * @param classes A list of protocol classes to find * @return Protocol The protocol found */ public Protocol findProtocol(Class ... classes) { for(Class clazz: classes) { Protocol prot=findProtocol(clazz); if(prot != null) return prot; } return null; } protected Protocol createProtocol(String classname) throws Exception { String defaultProtocolName=ProtocolConfiguration.protocol_prefix + '.' + classname; Class clazz=null; try { clazz=Util.loadClass(defaultProtocolName, getClass()); } catch(ClassNotFoundException e) { } if(clazz == null) { try { clazz=Util.loadClass(classname, getClass()); } catch(ClassNotFoundException e) { } if(clazz == null) { throw new Exception("unable to load class for protocol " + classname + " (either as an absolute - " + classname + " - or relative - " + defaultProtocolName + " - package name)"); } } Protocol retval=(Protocol)clazz.newInstance(); if(retval == null) throw new Exception("creation of instance for protocol " + classname + "failed"); retval.setProtocolStack(this); return retval; } public void init() throws Exception { List protocols=getProtocols(); Collections.reverse(protocols); top_prot=Configurator.connectProtocols(protocols); top_prot.setUpProtocol(this); this.setDownProtocol(top_prot); bottom_prot=getBottomProtocol(); Configurator.setDefaultValues(protocols); initProtocolStack(); } public void initProtocolStack() throws Exception { List protocols = getProtocols(); Collections.reverse(protocols); for(Protocol prot: protocols) { if(prot instanceof TP) { TP transport=(TP)prot; if(transport.isSingleton()) { String singleton_name=transport.getSingletonName(); synchronized(singleton_transports) { Tuple val=singleton_transports.get(singleton_name); if(val == null) { singleton_transports.put(singleton_name, new Tuple(transport,new ProtocolStack.RefCounter((short)1, (short)0))); } else { ProtocolStack.RefCounter counter=val.getVal2(); short num_inits=counter.incrementInitCount(); if(num_inits >= 1) { continue; } } } } } prot.init(); } } public void destroy() { if(top_prot != null) { for(Protocol prot: getProtocols()) { if(prot instanceof TP) { TP transport=(TP)prot; if(transport.isSingleton()) { String singleton_name=transport.getSingletonName(); synchronized(singleton_transports) { Tuple val=singleton_transports.get(singleton_name); if(val != null) { ProtocolStack.RefCounter counter=val.getVal2(); short num_inits=counter.decrementInitCount(); if(num_inits >= 1) { continue; } else singleton_transports.remove(singleton_name); } } } } prot.destroy(); } /* *Do not null top_prot reference since we need recreation of channel properties (JChannel#getProperties) *during channel recreation, especially if those properties were modified after channel was created. *We modify channel properties after channel creation in some tests for example * */ //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, Address local_addr) throws Exception { if(stopped == false) return; Protocol above_prot=null; for(final Protocol prot: getProtocols()) { if(prot instanceof TP) { String singleton_name=((TP)prot).getSingletonName(); TP transport=(TP)prot; if(transport.isSingleton() && cluster_name != null) { final Map up_prots=transport.getUpProtocols(); synchronized(singleton_transports) { 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, local_addr, prot.getId(), above_prot, prot, transport.getThreadNamingPattern()); ad.setProtocolStack(above_prot.getProtocolStack()); above_prot.setDownProtocol(ad); up_prots.put(cluster_name, ad); } } Tuple val=singleton_transports.get(singleton_name); if(val != null) { ProtocolStack.RefCounter counter=val.getVal2(); short num_starts=counter.incrementStartCount(); if(num_starts >= 1) { continue; } else { prot.start(); above_prot=prot; continue; } } } } } prot.start(); above_prot=prot; } TP transport=getTransport(); transport.registerProbeHandler(props_handler); 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; for(final Protocol prot: getProtocols()) { if(prot instanceof TP) { TP transport=(TP)prot; if(transport.isSingleton()) { String singleton_name=transport.getSingletonName(); final Map up_prots=transport.getUpProtocols(); synchronized(up_prots) { up_prots.remove(cluster_name); } synchronized(singleton_transports) { Tuple val=singleton_transports.get(singleton_name); if(val != null) { ProtocolStack.RefCounter counter=val.getVal2(); short num_starts=counter.decrementStartCount(); if(num_starts > 0) { continue; // don't call TP.stop() if we still have references to the transport } //else // singletons.remove(singleton_name); // do the removal in destroyProtocolStack() } } } } prot.stop(); } TP transport=getTransport(); transport.unregisterProbeHandler(props_handler); 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; } /** * Keeps track of the number os times init()/destroy() and start()/stop have been called. The variables * init_count and start_count are incremented or decremented accoordingly. Note that this class is not synchronized */ public static class RefCounter { private short init_count=0; private short start_count=0; public RefCounter(short init_count, short start_count) { this.init_count=init_count; this.start_count=start_count; } public short getInitCount() { return init_count; } public short getStartCount() { return start_count; } /** * Increments init_count, returns the old value before incr * @return */ public short incrementInitCount(){ return init_count++; } public short decrementInitCount() { init_count=(short)Math.max(init_count -1, 0); return init_count; } public short decrementStartCount() { start_count=(short)Math.max(start_count -1, 0); return start_count; } public short incrementStartCount() { return start_count++; } public String toString() { return "init_count=" + init_count + ", start_count=" + start_count; } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/stack/RangeBasedRetransmitter.java0000644000175000017500000001702711647260573030364 0ustar moellermoeller package org.jgroups.stack; import org.jgroups.Address; import org.jgroups.util.*; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentSkipListMap; import java.util.concurrent.atomic.AtomicLong; /** * This retransmitter is specialized in maintaining ranges of seqnos, e.g. [3-20, [89-89], [100-120]. * The ranges are stored in a sorted hashmap and the {@link Comparable#compareTo(Object)} method compares both ranges * again ranges, and ranges against seqnos. The latter helps to find a range given a seqno, e.g. seqno 105 will find * range [100-120].

                * Each range is implemented by {@link org.jgroups.util.SeqnoRange}, which has a bitset of all missing seqnos. When * a seqno is received, that bit set is updated; the bit corresponding to the seqno is set to 1. A task linked to * the range periodically retransmits missing messages.

                * When all bits are 1 (= all messages have been received), the range is removed from the hashmap and the retransmission * task is cancelled. * * @author Bela Ban */ public class RangeBasedRetransmitter extends Retransmitter { // todo: when JDK 6 is the baseline, convert the TreeMap to a TreeSet or ConcurrentSkipListSet and use ceiling() /** Sorted hashmap storing the ranges */ private final Map ranges=new ConcurrentSkipListMap(new SeqnoComparator()); /** Association between ranges and retransmission tasks */ private final Map tasks=new ConcurrentHashMap(); private final AtomicLong num_missing_seqnos=new AtomicLong(0); private final AtomicLong num_ranges=new AtomicLong(0); private final AtomicLong num_single_msgs=new AtomicLong(0); /** * 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 RangeBasedRetransmitter(Address sender, RetransmitCommand cmd, TimeScheduler sched) { super(sender, cmd, sched); } /** * 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 */ 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; } num_missing_seqnos.addAndGet(last_seqno - first_seqno +1); // create a single seqno if we have no range or else a SeqnoRange Seqno range=first_seqno == last_seqno? new Seqno(first_seqno) : new SeqnoRange(first_seqno, last_seqno); if(range instanceof SeqnoRange) num_ranges.incrementAndGet(); else num_single_msgs.incrementAndGet(); // each task needs its own retransmission interval, as they are stateful *and* mutable, so we *need* to copy ! RangeTask new_task=new RangeTask(range, RETRANSMIT_TIMEOUTS.copy(), cmd, sender); Seqno old_range=ranges.put(range, range); if(old_range != null) log.error("new range " + range + " overlaps with old range " + old_range); tasks.put(range, new_task); new_task.doSchedule(); // Entry adds itself to the timer if(log.isTraceEnabled()) log.trace("added range " + sender + " [" + range + "]"); } /** * 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) { int retval=0; Seqno range=ranges.get(new Seqno(seqno, true)); if(range == null) return 0; range.set(seqno); if(log.isTraceEnabled()) log.trace("removed " + sender + " #" + seqno + " from retransmitter"); // if the range has no missing messages, get the associated task and cancel it if(range.getNumberOfMissingMessages() == 0) { Task task=tasks.remove(range); if(task != null) { task.cancel(); retval=task.getNumRetransmits(); } else log.error("task for range " + range + " not found"); ranges.remove(range); if(log.isTraceEnabled()) log.trace("all messages for " + sender + " [" + range + "] have been received; removing range"); } return retval; } /** * Reset the retransmitter: clear all msgs and cancel all the * respective tasks */ public void reset() { synchronized(ranges) { for(Seqno range: ranges.keySet()) { // get task associated with range and cancel it Task task=tasks.get(range); if(task != null) { task.cancel(); tasks.remove(range); } } ranges.clear(); } for(Task task: tasks.values()) task.cancel(); num_missing_seqnos.set(0); num_ranges.set(0); num_single_msgs.set(0); } public String toString() { int missing_msgs=0; for(Seqno range: ranges.keySet()) missing_msgs+=range.getNumberOfMissingMessages(); StringBuilder sb=new StringBuilder(); sb.append(missing_msgs).append(" messages to retransmit"); if(missing_msgs < 50) { Collection all_missing_msgs=new LinkedList(); for(Seqno range: ranges.keySet()) { all_missing_msgs.addAll(range.getMessagesToRetransmit()); } sb.append(": ").append(all_missing_msgs); } return sb.toString(); } public int size() { int retval=0; for(Seqno range: ranges.keySet()) retval+=range.getNumberOfMissingMessages(); return retval; } public String printStats() { StringBuilder sb=new StringBuilder(); sb.append("total seqnos=" + num_missing_seqnos); sb.append(", single seqnos=" + num_single_msgs); sb.append(", ranges=" + num_ranges); double avg_seqnos_per_range=(double)(num_missing_seqnos.get() - num_single_msgs.get()) / num_ranges.get(); sb.append(", seqnos / range: " + avg_seqnos_per_range); return sb.toString(); } protected class RangeTask extends Task { protected final Seqno range; protected RangeTask(Seqno range, Interval intervals, RetransmitCommand cmd, Address msg_sender) { super(intervals, cmd, msg_sender); this.range=range; } public String toString() { return range.toString(); } protected void callRetransmissionCommand() { Collection missing=range.getMessagesToRetransmit(); if(missing.isEmpty()) { cancel(); } else { for(Range range: missing) { command.retransmit(range.low, range.high, msg_sender); } } } } /* ------------------------------- Private Methods -------------------------------------- */ /* ---------------------------- End of Private Methods ------------------------------------ */ }libjgroups-java-2.12.2.Final.orig/src/org/jgroups/stack/Retransmitter.java0000644000175000017500000001266011647260573026446 0ustar moellermoeller package org.jgroups.stack; import org.jgroups.Address; import org.jgroups.logging.Log; import org.jgroups.logging.LogFactory; import org.jgroups.util.TimeScheduler; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; /** * Abstract class of a retransmitter. * 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.
                * @author Bela Ban * @version $Revision: 1.33 $ */ public abstract class Retransmitter { /** Default retransmit intervals (ms) - exponential approx. */ protected Interval RETRANSMIT_TIMEOUTS=new StaticInterval(300, 600, 1200, 2400); protected final Address sender; protected final RetransmitCommand cmd; protected final TimeScheduler timer; 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) { this.sender=sender; this.cmd=cmd; timer=sched; } public void setRetransmitTimeouts(Interval interval) { if(interval != null) RETRANSMIT_TIMEOUTS=interval; } /** * Add messages from first_seqno to last_seqno for retransmission */ public abstract void add(long first_seqno, long last_seqno); /** * Remove the given sequence number from retransmission */ public abstract int remove(long seqno); /** * Reset the retransmitter: clear all msgs and cancel all the respective tasks */ public abstract void reset(); public abstract int size(); /* ------------------------------- Private Methods -------------------------------------- */ /* ---------------------------- End of Private Methods ------------------------------------ */ protected abstract class Task implements TimeScheduler.Task { protected final Interval intervals; protected volatile Future future; protected Address msg_sender=null; protected int num_retransmits=0; protected RetransmitCommand command; protected volatile boolean cancelled=false; protected Task(Interval intervals, RetransmitCommand cmd, Address msg_sender) { 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 { callRetransmissionCommand(); num_retransmits++; } catch(Throwable t) { if(log.isErrorEnabled()) log.error("failed retransmission task", t); } doSchedule(); } protected abstract void callRetransmissionCommand(); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/stack/RouterStub.java0000644000175000017500000002315711647260573025724 0ustar moellermoellerpackage org.jgroups.stack; import org.jgroups.Address; import org.jgroups.PhysicalAddress; import org.jgroups.logging.Log; import org.jgroups.logging.LogFactory; import org.jgroups.protocols.PingData; import org.jgroups.protocols.TUNNEL.StubReceiver; import org.jgroups.util.Util; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.Socket; import java.net.SocketException; import java.util.ArrayList; import java.util.List; /** * Client stub that talks to a remote GossipRouter * @author Bela Ban */ public class RouterStub { public static enum ConnectionStatus {INITIAL, CONNECTION_BROKEN, CONNECTION_ESTABLISHED, CONNECTED,DISCONNECTED}; private final String router_host; // name of the router host private final int router_port; // port on which router listens on private Socket sock=null; // socket connecting to the router private DataOutputStream output=null; private DataInputStream input=null; private volatile ConnectionStatus connectionState=ConnectionStatus.INITIAL; private static final Log log=LogFactory.getLog(RouterStub.class); private final ConnectionListener conn_listener; private final InetAddress bind_addr; private int sock_conn_timeout=3000; // max number of ms to wait for socket establishment to // GossipRouter private int sock_read_timeout=3000; // max number of ms to wait for socket reads (0 means block // forever, or until the sock is closed) private boolean tcp_nodelay=true; private StubReceiver receiver; public interface ConnectionListener { void connectionStatusChange(RouterStub stub, ConnectionStatus state); } /** * Creates a stub for a remote Router object. * @param routerHost The name of the router's host * @param routerPort The router's port * @throws SocketException */ public RouterStub(String routerHost, int routerPort, InetAddress bindAddress, ConnectionListener l) { router_host=routerHost != null? routerHost : "localhost"; router_port=routerPort; bind_addr=bindAddress; conn_listener=l; } public synchronized void setReceiver(StubReceiver receiver) { this.receiver = receiver; } public synchronized StubReceiver getReceiver() { return receiver; } public boolean isTcpNoDelay() { return tcp_nodelay; } public void setTcpNoDelay(boolean tcp_nodelay) { this.tcp_nodelay=tcp_nodelay; } public synchronized void interrupt() { if(receiver != null) { Thread thread = receiver.getThread(); if(thread != null) thread.interrupt(); } } public synchronized void join(long wait) throws InterruptedException { if(receiver != null) { Thread thread = receiver.getThread(); if(thread != null) thread.join(wait); } } 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 boolean isConnected() { return !(connectionState == ConnectionStatus.CONNECTION_BROKEN || connectionState == ConnectionStatus.INITIAL); } public ConnectionStatus getConnectionStatus() { return connectionState; } /** * Register this process with the router under group. * @param group The name of the group under which to register */ public synchronized void connect(String group, Address addr, String logical_name, List phys_addrs) throws Exception { doConnect(); GossipData request=new GossipData(GossipRouter.CONNECT, group, addr, logical_name, phys_addrs); request.writeTo(output); output.flush(); byte result = input.readByte(); if(result == GossipRouter.CONNECT_OK) { connectionStateChanged(ConnectionStatus.CONNECTED); } else { connectionStateChanged(ConnectionStatus.DISCONNECTED); throw new Exception("Connect failed received from GR " + getGossipRouterAddress()); } } public synchronized void doConnect() throws Exception { if(!isConnected()) { try { sock=new Socket(); sock.bind(new InetSocketAddress(bind_addr, 0)); sock.setSoTimeout(sock_read_timeout); sock.setSoLinger(true, 2); sock.setTcpNoDelay(tcp_nodelay); sock.setKeepAlive(true); Util.connect(sock, new InetSocketAddress(router_host, router_port), sock_conn_timeout); output=new DataOutputStream(sock.getOutputStream()); input=new DataInputStream(sock.getInputStream()); connectionStateChanged(ConnectionStatus.CONNECTION_ESTABLISHED); } catch(Exception e) { Util.close(sock); Util.close(input); Util.close(output); connectionStateChanged(ConnectionStatus.CONNECTION_BROKEN); throw new Exception("Could not connect to " + getGossipRouterAddress() , e); } } } /** * Checks whether the connection is open * @return */ public synchronized void checkConnection() { GossipData request=new GossipData(GossipRouter.PING); try { request.writeTo(output); output.flush(); } catch(IOException e) { connectionStateChanged(ConnectionStatus.CONNECTION_BROKEN); } } public synchronized void disconnect(String group, Address addr) { try { GossipData request=new GossipData(GossipRouter.DISCONNECT, group, addr); request.writeTo(output); output.flush(); } catch(Exception e) { } finally { connectionStateChanged(ConnectionStatus.DISCONNECTED); } } public synchronized void destroy() { try { GossipData request = new GossipData(GossipRouter.CLOSE); request.writeTo(output); output.flush(); } catch (Exception e) { } finally { Util.close(output); Util.close(input); Util.close(sock); } } /* * Used only in testing, never access socket directly * */ public Socket getSocket() { return sock; } public synchronized List getMembers(final String group) throws Exception { List retval=new ArrayList(); try { if(!isConnected() || input == null) throw new Exception ("not connected"); // we might get a spurious SUSPECT message from the router, just ignore it if(input.available() > 0) // fixes https://jira.jboss.org/jira/browse/JGRP-1151 input.skipBytes(input.available()); GossipData request=new GossipData(GossipRouter.GOSSIP_GET, group, null); request.writeTo(output); output.flush(); short num_rsps=input.readShort(); for(int i=0; i < num_rsps; i++) { PingData rsp=new PingData(); rsp.readFrom(input); retval.add(rsp); } } catch(Exception e) { connectionStateChanged(ConnectionStatus.CONNECTION_BROKEN); throw new Exception("Connection to " + getGossipRouterAddress() + " broken. Could not send GOSSIP_GET request", e); } return retval; } public InetSocketAddress getGossipRouterAddress() { return new InetSocketAddress(router_host, router_port); } public String toString() { return "RouterStub[localsocket=" + ((sock != null) ? sock.getLocalSocketAddress().toString() : "null")+ ",router_host=" + router_host + "::" + router_port + ",connected=" + isConnected() + "]"; } public void sendToAllMembers(String group, byte[] data, int offset, int length) throws Exception { sendToMember(group, null, data, offset, length); // null destination represents mcast } public synchronized void sendToMember(String group, Address dest, byte[] data, int offset, int length) throws Exception { try { GossipData request = new GossipData(GossipRouter.MESSAGE, group, dest, data, offset, length); request.writeTo(output); output.flush(); } catch (Exception e) { connectionStateChanged(ConnectionStatus.CONNECTION_BROKEN); throw new Exception("Connection to " + getGossipRouterAddress() + " broken. Could not send message to " + dest, e); } } public DataInputStream getInputStream() { return input; } private void connectionStateChanged(ConnectionStatus newState) { boolean notify=connectionState != newState; connectionState=newState; if(notify && conn_listener != null) { try { conn_listener.connectionStatusChange(this, newState); } catch(Throwable t) { log.error("failed notifying ConnectionListener " + conn_listener, t); } } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/stack/RouterStubManager.java0000644000175000017500000001774211647260573027222 0ustar moellermoeller/* * JBoss, Home of Professional Open Source. * Copyright 2009, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file 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.stack; import java.net.InetAddress; import java.net.InetSocketAddress; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import org.jgroups.Address; import org.jgroups.Event; import org.jgroups.PhysicalAddress; import org.jgroups.annotations.GuardedBy; import org.jgroups.logging.Log; import org.jgroups.logging.LogFactory; import org.jgroups.util.TimeScheduler; public class RouterStubManager implements RouterStub.ConnectionListener { @GuardedBy("reconnectorLock") private final Map> futures = new HashMap>(); private final Lock reconnectorLock = new ReentrantLock(); private final List stubs; private final Protocol owner; private final TimeScheduler timer; private final String channelName; private final Address logicalAddress; private final long interval; protected final Log log; public RouterStubManager(Protocol owner, String channelName, Address logicalAddress, long interval) { this.owner = owner; this.stubs = new CopyOnWriteArrayList(); this.log = LogFactory.getLog(owner.getClass()); this.timer = owner.getTransport().getTimer(); this.channelName = channelName; this.logicalAddress = logicalAddress; this.interval = interval; } private RouterStubManager(Protocol p) { this(p,null,null,0L); } public List getStubs(){ return stubs; } public RouterStub createAndRegisterStub(String routerHost, int routerPort, InetAddress bindAddress) { RouterStub s = new RouterStub(routerHost,routerPort,bindAddress,this); unregisterAndDestroyStub(s.getGossipRouterAddress()); stubs.add(s); return s; } public void registerStub(RouterStub s) { unregisterAndDestroyStub(s.getGossipRouterAddress()); stubs.add(s); } public boolean unregisterStub(final RouterStub s) { return stubs.remove(s); } public RouterStub unregisterStub(final InetSocketAddress address) { if(address == null) throw new IllegalArgumentException("Cannot remove null address"); for (RouterStub s : stubs) { if (s.getGossipRouterAddress().equals(address)) { stubs.remove(address); return s; } } return null; } public boolean unregisterAndDestroyStub(final InetSocketAddress address) { RouterStub unregisteredStub = unregisterStub(address); if(unregisteredStub !=null) { unregisteredStub.destroy(); return true; } return false; } public void disconnectStubs() { for (RouterStub stub : stubs) { try { stub.disconnect(channelName, logicalAddress); } catch (Exception e) { } } } public void destroyStubs() { for (RouterStub s : stubs) { stopReconnecting(s); s.destroy(); } stubs.clear(); } public void startReconnecting(final RouterStub stub) { reconnectorLock.lock(); try { InetSocketAddress routerAddress = stub.getGossipRouterAddress(); Future f = futures.get(routerAddress); if (f != null) { f.cancel(true); futures.remove(routerAddress); } final Runnable reconnector = new Runnable() { public void run() { try { if (log.isTraceEnabled()) log.trace("Reconnecting " + stub); String logical_name = org.jgroups.util.UUID.get(logicalAddress); PhysicalAddress physical_addr = (PhysicalAddress) owner.down(new Event( Event.GET_PHYSICAL_ADDRESS, logicalAddress)); List physical_addrs = Arrays.asList(physical_addr); stub.connect(channelName, logicalAddress, logical_name, physical_addrs); if (log.isTraceEnabled()) log.trace("Reconnected " + stub); } catch (Throwable ex) { if (log.isWarnEnabled()) log.warn("failed reconnecting stub to GR at "+ stub.getGossipRouterAddress() + ": " + ex); } } }; f = timer.scheduleWithFixedDelay(reconnector, 0, interval, TimeUnit.MILLISECONDS); futures.put(stub.getGossipRouterAddress(), f); } finally { reconnectorLock.unlock(); } } public void stopReconnecting(final RouterStub stub) { reconnectorLock.lock(); try { InetSocketAddress routerAddress = stub.getGossipRouterAddress(); Future f = futures.get(stub.getGossipRouterAddress()); if (f != null) { f.cancel(true); futures.remove(routerAddress); } final Runnable pinger = new Runnable() { public void run() { try { if(log.isTraceEnabled()) log.trace("Pinging " + stub); stub.checkConnection(); if(log.isTraceEnabled()) log.trace("Pinged " + stub); } catch (Throwable ex) { if (log.isWarnEnabled()) log.warn("failed pinging stub, GR at " + stub.getGossipRouterAddress()+ ": " + ex); } } }; f = timer.scheduleWithFixedDelay(pinger, 0, interval, TimeUnit.MILLISECONDS); futures.put(stub.getGossipRouterAddress(), f); } finally { reconnectorLock.unlock(); } } public void connectionStatusChange(RouterStub stub, RouterStub.ConnectionStatus newState) { if (newState == RouterStub.ConnectionStatus.CONNECTION_BROKEN) { stub.interrupt(); stub.destroy(); startReconnecting(stub); } else if (newState == RouterStub.ConnectionStatus.CONNECTED) { stopReconnecting(stub); } else if (newState == RouterStub.ConnectionStatus.DISCONNECTED) { // wait for disconnect ack; try { stub.join(interval); } catch (InterruptedException e) { } } } public static RouterStubManager emptyGossipClientStubManager(Protocol p) { return new RouterStubManager(p); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/stack/StateTransferInfo.java0000644000175000017500000000452411647260573027204 0ustar moellermoeller 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 */ 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(); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/stack/StaticInterval.java0000644000175000017500000000235411647260573026536 0ustar moellermoeller 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 */ 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++]); // } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/stack/package.html0000644000175000017500000000010711647260573025212 0ustar moellermoeller Support for managing protocol stacks. libjgroups-java-2.12.2.Final.orig/src/org/jgroups/util/0000755000175000017500000000000011647260573022603 5ustar moellermoellerlibjgroups-java-2.12.2.Final.orig/src/org/jgroups/util/AckCollector.java0000644000175000017500000000645611647260573026026 0ustar moellermoellerpackage org.jgroups.util; import org.jgroups.Address; import org.jgroups.TimeoutException; import org.jgroups.View; import org.jgroups.ViewId; import java.util.*; /** * Collects acks from a number of nodes, waits for all acks. Can also be time bounded * @author Bela Ban */ 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
                (); private int expected_acks=0; public AckCollector() { missing_acks=new ArrayList(); expected_acks=0; } public AckCollector(ViewId v, List l) { missing_acks=new ArrayList(l); if(v != null) { expected_acks=l != null? l.size() : 0; } } public String printMissing() { synchronized(this) { return missing_acks.toString(); } } @Deprecated public static String printReceived() { return "n/a"; } public String printSuspected() { synchronized(this) { return suspected_mbrs.toString(); } } public void reset(Collection
                members) { synchronized(this) { suspected_mbrs.clear(); missing_acks.clear(); if(members != null && !members.isEmpty()) { missing_acks.addAll(members); expected_acks=members.size(); } missing_acks.removeAll(suspected_mbrs); all_acks_received.reset(); } } public int size() { synchronized(this) { return missing_acks.size(); } } @Deprecated public static int receivedAcks() { return -1; } public int expectedAcks() { synchronized(this) { return expected_acks; } } 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(); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/util/AdditionalDataUUID.java0000644000175000017500000000546211647260573027006 0ustar moellermoellerpackage org.jgroups.util; import org.jgroups.Global; import java.io.*; import java.security.SecureRandom; /** * Subclass of {@link org.jgroups.util.UUID} which adds a string as payload. An instance of this can be fed to * {@link org.jgroups.JChannel#setAddressGenerator(org.jgroups.stack.AddressGenerator)}, with the address generator * creating PayloadUUIDs. * @author Bela Ban */ public class AdditionalDataUUID extends UUID { private static final long serialVersionUID=-8299077012964139735L; // don't need this as we already added AdditonalDataUUID to jg-magic-map.xml // static { // ClassConfigurator.add((short)3333, PayloadUUID.class); // } protected byte[] payload; public AdditionalDataUUID() { } protected AdditionalDataUUID(byte[] data, byte[] payload) { super(data); this.payload=payload; } public static AdditionalDataUUID randomUUID(byte[] payload) { return new AdditionalDataUUID(generateRandomBytes(), payload); } public static AdditionalDataUUID randomUUID(String logical_name, byte[] payload) { AdditionalDataUUID retval=new AdditionalDataUUID(generateRandomBytes(), payload); UUID.add(retval, logical_name); return retval; } protected static byte[] generateRandomBytes() { SecureRandom ng=numberGenerator; if(ng == null) numberGenerator=ng=new SecureRandom(); byte[] randomBytes=new byte[16]; ng.nextBytes(randomBytes); return randomBytes; } public int size() { int retval=super.size() + Global.BYTE_SIZE; if(payload != null) retval+=payload.length; return retval; } public void writeTo(DataOutputStream out) throws IOException { super.writeTo(out); Util.writeByteBuffer(payload, out); } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { super.readFrom(in); payload=Util.readByteBuffer(in); } public void writeExternal(ObjectOutput out) throws IOException { super.writeExternal(out); if(payload != null) { out.writeInt(payload.length); out.write(payload, 0, payload.length); } else out.writeInt(0); } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { super.readExternal(in); int size=in.readInt(); if(size > 0) { payload=new byte[size]; in.read(payload, 0, size); } } public String toString() { if(print_uuids) return toStringLong() + (payload == null? "" : " (" + payload.length + " bytes)"); return super.toString() + (payload == null? "" : " (" + payload.length + " bytes)"); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/util/AgeOutCache.java0000644000175000017500000000472111647260573025562 0ustar moellermoellerpackage 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 */ public class AgeOutCache { private final TimeScheduler timer; private long timeout; private final ConcurrentMap map=new ConcurrentHashMap(); private Handler handler=null; public interface Handler { void expired(K key); } public AgeOutCache(TimeScheduler timer, long timeout) { this.timer=timer; this.timeout=timeout; } public AgeOutCache(TimeScheduler 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) { Future future=timer.schedule(new Runnable() { public void run() { if(handler != null) { try { handler.expired(key); } catch(Throwable t) { } } Future tmp=map.remove(key); if(tmp != null) tmp.cancel(true); } }, timeout, TimeUnit.MILLISECONDS); Future 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) { Future 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(Future 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()) { sb.append(entry.getKey()).append(": ").append(entry.getValue()).append("\n"); } return sb.toString(); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/util/BoundedList.java0000755000175000017500000000177511647260573025677 0ustar moellermoellerpackage 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 */ 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 boolean addIfAbsent(T obj) { return obj != null && !contains(obj) && add(obj); } public T removeFromHead() { return poll(); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/util/Buffer.java0000644000175000017500000000250011647260573024654 0ustar moellermoellerpackage 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. Note that the underlying byte[] buffer must not be changed as long as this Buffer instance is in use ! * @author Bela Ban */ 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 Buffer(byte[] buf) { this(buf, 0, buf.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(); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/util/Command.java0000644000175000017500000000036511647260573025030 0ustar moellermoeller 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; } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/util/ConcurrentLinkedBlockingQueue.java0000644000175000017500000000741111647260573031400 0ustar moellermoellerpackage org.jgroups.util; import java.util.Collection; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.LockSupport; /** * Attempt at writing a fast transfer queue, which is bounded. The take() method blocks until there is an element, but * the offer() method drops the element and returns if the queue is full (doesn't block). I thought this class would * be useful in ThreadPoolExecutor, as a replacement for LinkedBlockingQueue, but the perf didn't change. I'll keep it * for later reference ... * @author Bela Ban */ public class ConcurrentLinkedBlockingQueue extends ConcurrentLinkedQueue implements BlockingQueue { private static final long serialVersionUID=-8884995454506956809L; private final int capacity; private final Lock lock=new ReentrantLock(); private final Condition not_empty=lock.newCondition(); private final AtomicInteger waiting_takers=new AtomicInteger(0); public ConcurrentLinkedBlockingQueue(int capacity) { this.capacity=capacity; } // The methods below are used by ThreadPoolExecutor /////////// /** * Drops elements if capacity has been reached. That's OK for the ThreadPoolExecutor as dropped messages * will get retransmitted * @param t * @return */ public boolean offer(T t) { boolean retval=size() < capacity && super.offer(t); if(waiting_takers.get() > 0) { lock.lock(); try { not_empty.signal(); } finally { lock.unlock(); } } return retval; } public T take() throws InterruptedException { T retval=null; for(;;) { retval=poll(); if(retval != null) return retval; while(size() == 0) { waiting_takers.incrementAndGet(); lock.lockInterruptibly(); try { not_empty.await(); } finally { lock.unlock(); waiting_takers.decrementAndGet(); } } } } public T poll() { return super.poll(); } public T poll(long timeout, TimeUnit unit) throws InterruptedException { long sleep_time_nanos=TimeUnit.NANOSECONDS.convert(timeout, unit); long target_time_nanos=System.nanoTime() + sleep_time_nanos; sleep_time_nanos/=5; T el=null; while(System.nanoTime() < target_time_nanos) { if((el=poll()) != null) return el; LockSupport.parkNanos(sleep_time_nanos); } return el; } public boolean remove(Object o) { return super.remove(o); } public int remainingCapacity() { return capacity - size(); } public int drainTo(Collection c) { int count=0; if(c == null) return count; for(;;) { T el=poll(); if(el == null) break; c.add(el); count++; } return count; } ///////////////////////////////////////////////////////////////// public void put(T t) throws InterruptedException { super.offer(t); } public boolean offer(T t, long timeout, TimeUnit unit) throws InterruptedException { return offer(t); } public int drainTo(Collection c, int maxElements) { return drainTo(c); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/util/CreditMap.java0000644000175000017500000002152311647260573025321 0ustar moellermoellerpackage org.jgroups.util; import org.jgroups.Address; import org.jgroups.annotations.GuardedBy; 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; /** * Maintains credits for senders, when credits fall below 0, a sender blocks until new credits have been received. * @author Bela Ban */ public class CreditMap { protected final long max_credits; @GuardedBy("lock") protected final Map credits=new HashMap(); protected long min_credits; protected long accumulated_credits=0; protected final Lock lock=new ReentrantLock(); protected final Condition credits_available=lock.newCondition(); protected int num_blockings=0; protected long total_block_time=0; public CreditMap(long max_credits) { this.max_credits=max_credits; min_credits=max_credits; } public long getAccumulatedCredits() { return accumulated_credits; } public long getMinCredits() { return min_credits; } public int getNumBlockings() { return num_blockings; } public long getTotalBlockTime() { return total_block_time; } public Set
                keys() { lock.lock(); try { return credits.keySet(); } finally { lock.unlock(); } } public Long get(Address member) { lock.lock(); try { return credits.get(member); } finally { lock.unlock(); } } public Long remove(Address key) { lock.lock(); try { Long retval=credits.remove(key); flushAccumulatedCredits(); long new_min=computeLowestCredit(); if(new_min > min_credits) { min_credits=new_min; credits_available.signalAll(); } return retval; } finally { lock.unlock(); } } public Long putIfAbsent(Address key) { lock.lock(); try { flushAccumulatedCredits(); Long val=credits.get(key); return val != null? val : credits.put(key, max_credits); } finally { lock.unlock(); } } public List
                getMembersWithInsufficientCredits(long credit_needed) { List
                retval=new LinkedList
                (); lock.lock(); try { if(credit_needed > min_credits) { flushAccumulatedCredits(); for(Map.Entry entry: credits.entrySet()) { if(entry.getValue().longValue() < credit_needed) retval.add(entry.getKey()); } } return retval; } finally { lock.unlock(); } } public List> getMembersWithCreditsLessThan(long min_credits) { List> retval=new LinkedList>(); lock.lock(); try { flushAccumulatedCredits(); for(Map.Entry entry: credits.entrySet()) { if(entry.getValue().longValue() <= min_credits ) retval.add(new Tuple(entry.getKey(), entry.getValue())); } return retval; } finally { lock.unlock(); } } /** * Decrements credits bytes from all. Returns true if successful, or false if not. Blocks for timeout ms * (if greater than 0). * * @param credits Number of bytes to decrement from all members * @param timeout Number of milliseconds to wait until more credits have been received * @return True if decrementing credits bytes succeeded, false otherwise */ public boolean decrement(long credits, long timeout) { lock.lock(); try { if(decrement(credits)) return true; if(timeout <= 0) return false; long start=System.currentTimeMillis(); try { // System.out.println("^^^^^^ blocking: credits: " + this.credits); credits_available.await(timeout, TimeUnit.MILLISECONDS); } catch(InterruptedException e) { } finally { total_block_time+=System.currentTimeMillis() - start; num_blockings++; } return decrement(credits); } finally { lock.unlock(); } } public void replenish(Address sender, long new_credits) { if(sender == null) return; lock.lock(); try { Long val=credits.get(sender); if(val == null) return; boolean potential_update=val.longValue() - accumulated_credits <= min_credits; decrementAndAdd(sender, new_credits); if(potential_update) { long new_min=computeLowestCredit(); if(new_min > min_credits) { min_credits=new_min; credits_available.signalAll(); } } } finally { lock.unlock(); } } public void replenishAll() { lock.lock(); try { flushAccumulatedCredits(); for(Map.Entry entry: credits.entrySet()) entry.setValue(max_credits); min_credits=computeLowestCredit(); credits_available.signalAll(); } finally { lock.unlock(); } } public void clear() { lock.lock(); try { num_blockings=0; total_block_time=0; credits.clear(); credits_available.signalAll(); } finally { lock.unlock(); } } public String toString() { StringBuilder sb=new StringBuilder(); lock.lock(); try { for(Map.Entry entry: credits.entrySet()) { sb.append(entry.getKey()).append(": ").append(entry.getValue()).append("\n"); } sb.append("min_credits=" + min_credits + ", accumulated=" + accumulated_credits); } finally { lock.unlock(); } return sb.toString(); } // need to be called with lock held protected boolean decrement(long credits) { if(credits <= min_credits) { accumulated_credits+=credits; min_credits-=credits; return true; } return false; } /** Needs to be called with lock held */ protected long computeLowestCredit() { long lowest=max_credits; for(long cred: credits.values()) lowest=Math.min(cred, lowest); return lowest; } public long computeLowestCreditWithAccumulated() { long lowest=max_credits; for(long cred: credits.values()) lowest=Math.min(cred, lowest); return lowest - accumulated_credits; } /** * Decrements credits bytes from all elements and add new_credits to member (if non null). * The lowest credit needs to be greater than min_credits. Needs to be called with lock held * @param member The member to which new_credits are added. NOP if null * @param new_credits Number of bytes to add to member. NOP if 0. */ protected void decrementAndAdd(Address member, long new_credits) { boolean replenish=member != null && new_credits > 0; if(accumulated_credits > 0) { for(Map.Entry entry: this.credits.entrySet()) { entry.setValue(Math.max(0, entry.getValue().longValue() - accumulated_credits)); if(replenish) { Address tmp=entry.getKey(); if(tmp.equals(member)) entry.setValue(Math.min(max_credits, entry.getValue().longValue() + new_credits)); } } accumulated_credits=0; } else { if(replenish) { Long val=this.credits.get(member); if(val != null) this.credits.put(member, Math.min(max_credits, val.longValue() + new_credits)); } } } // Called with lock held protected void flushAccumulatedCredits() { if(accumulated_credits > 0) { for(Map.Entry entry: this.credits.entrySet()) { entry.setValue(Math.max(0, entry.getValue().longValue() - accumulated_credits)); } accumulated_credits=0; } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/util/DefaultSocketFactory.java0000644000175000017500000000763011647260573027541 0ustar moellermoellerpackage org.jgroups.util; import java.net.*; import java.io.IOException; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** * Default implementation, ignores service names * @author Bela Ban */ public class DefaultSocketFactory implements SocketFactory { // Maintains information about open sockets protected final Map sockets=new ConcurrentHashMap(); public Socket createSocket(String service_name) throws IOException { return add(new Socket(), service_name); } public Socket createSocket(String service_name, String host, int port) throws IOException { return add(new Socket(host, port), service_name); } public Socket createSocket(String service_name, InetAddress address, int port) throws IOException { return add(new Socket(address, port), service_name); } public Socket createSocket(String service_name, String host, int port, InetAddress localAddr, int localPort) throws IOException { return add(new Socket(host, port, localAddr, localPort), service_name); } public Socket createSocket(String service_name, InetAddress address, int port, InetAddress localAddr, int localPort) throws IOException { return add(new Socket(address, port, localAddr, localPort), service_name); } public ServerSocket createServerSocket(String service_name) throws IOException { return add(new ServerSocket(), service_name); } public ServerSocket createServerSocket(String service_name, int port) throws IOException { return add(new ServerSocket(port), service_name); } public ServerSocket createServerSocket(String service_name, int port, int backlog) throws IOException { return add(new ServerSocket(port, backlog), service_name); } public ServerSocket createServerSocket(String service_name, int port, int backlog, InetAddress bindAddr) throws IOException { return add(new ServerSocket(port, backlog, bindAddr), service_name); } public DatagramSocket createDatagramSocket(String service_name) throws SocketException { return add(new DatagramSocket(), service_name); } public DatagramSocket createDatagramSocket(String service_name, SocketAddress bindaddr) throws SocketException { return add(new DatagramSocket(bindaddr), service_name); } public DatagramSocket createDatagramSocket(String service_name, int port) throws SocketException { return add(new DatagramSocket(port), service_name); } public DatagramSocket createDatagramSocket(String service_name, int port, InetAddress laddr) throws SocketException { return add(new DatagramSocket(port, laddr), service_name); } public MulticastSocket createMulticastSocket(String service_name) throws IOException { return add(new MulticastSocket(), service_name); } public MulticastSocket createMulticastSocket(String service_name, int port) throws IOException { return add(new MulticastSocket(port), service_name); } public MulticastSocket createMulticastSocket(String service_name, SocketAddress bindaddr) throws IOException { return add(new MulticastSocket(bindaddr), service_name); } public void close(Socket sock) throws IOException { remove(sock); Util.close(sock); } public void close(ServerSocket sock) throws IOException { remove(sock); Util.close(sock); } public void close(DatagramSocket sock) { remove(sock); Util.close(sock); } public Map getSockets() { return sockets; } protected T add(T sock, String service_name) { if(sock != null) { String tmp=service_name == null? "n/a" : service_name; sockets.put(sock, tmp); } return sock; } protected void remove(T sock) { if(sock != null) sockets.remove(sock); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/util/DefaultThreadFactory.java0000644000175000017500000001037311647260573027516 0ustar moellermoellerpackage 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 */ 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 addr, String cluster_name) { Thread retval=new Thread(group, r, name); retval.setDaemon(createDaemons); renameThread(retval, addr, 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 addr, 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(addr != null) sb.append(addr); else sb.append(this.address); } if(use_numbering || includeClusterName || includeLocalAddress) thread.setName(sb.toString()); } protected void renameThread(Thread thread, String addr, String cluster_name) { renameThread(null, thread, addr, cluster_name); } public void renameThread(Thread thread) { renameThread(null, thread); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/util/DefaultTimeScheduler.java0000644000175000017500000002055411647260573027516 0ustar moellermoeller package org.jgroups.util; import org.jgroups.logging.Log; import org.jgroups.logging.LogFactory; import org.jgroups.Global; import java.util.concurrent.*; /** * Implementation of {@link org.jgroups.util.TimeScheduler} by extending * {@link java.util.concurrent.ScheduledThreadPoolExecutor} to keep tasks sorted. Tasks will get executed in order * of execution time (by using a {@link java.util.concurrent.DelayQueue} internally. * * @author Bela Ban */ public class DefaultTimeScheduler extends ScheduledThreadPoolExecutor implements TimeScheduler { /** How many core threads */ private static int TIMER_DEFAULT_NUM_THREADS=3; protected static final Log log=LogFactory.getLog(DefaultTimeScheduler.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 DefaultTimeScheduler() { this(TIMER_DEFAULT_NUM_THREADS); } public DefaultTimeScheduler(ThreadFactory factory) { this(factory, TIMER_DEFAULT_NUM_THREADS); } public DefaultTimeScheduler(ThreadFactory factory, int max_threads) { super(max_threads, factory); setRejectedExecutionHandler(new ShutdownRejectedExecutionHandler(getRejectedExecutionHandler())); } public DefaultTimeScheduler(int corePoolSize) { super(corePoolSize); setRejectedExecutionHandler(new ShutdownRejectedExecutionHandler(getRejectedExecutionHandler())); } public void setThreadFactory(ThreadFactory factory) { super.setThreadFactory(factory); } public ThreadDecorator getThreadDecorator() { return threadDecorator; } public void setThreadDecorator(ThreadDecorator threadDecorator) { this.threadDecorator=threadDecorator; } public String dumpTimerTasks() { return getQueue().toString(); } public int getCurrentThreads() { return super.getPoolSize(); } public int getMinThreads() { return super.getCorePoolSize(); } public void setMinThreads(int size) { super.setCorePoolSize(size); } public int getMaxThreads() { return super.getMaximumPoolSize(); } public void setMaxThreads(int size) { super.setMaximumPoolSize(size); } public long getKeepAliveTime() { return super.getKeepAliveTime(TimeUnit.MILLISECONDS); } public void setKeepAliveTime(long time) { super.setKeepAliveTime(time, TimeUnit.MILLISECONDS); } /** * Schedule a task for execution at varying intervals. After execution, the task will get rescheduled after * {@link org.jgroups.util.DefaultTimeScheduler.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 Future 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() { java.util.List tasks=shutdownNow(); for(Runnable task: tasks) { if(task instanceof Future) { Future future=(Future)task; future.cancel(true); } } getQueue().clear(); try { awaitTermination(Global.THREADPOOL_SHUTDOWN_WAIT_TIME, TimeUnit.MILLISECONDS); } catch(InterruptedException e) { } } @Override protected void afterExecute(Runnable r, Throwable t) { try { super.afterExecute(r, t); } finally { if(threadDecorator != null) threadDecorator.threadReleased(Thread.currentThread()); } } public String toString() { return getClass().getSimpleName(); } /** * 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, Future { private final Task task; private volatile Future future; // cannot be null ! private volatile boolean cancelled=false; public TaskWrapper(Task task) { this.task=task; } public Future 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 boolean cancel(boolean mayInterruptIfRunning) { boolean retval=!isDone(); cancelled=true; if(future != null) future.cancel(mayInterruptIfRunning); return retval; } public boolean isCancelled() { return cancelled; } public boolean isDone() { return cancelled || (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; } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/util/Digest.java0000644000175000017500000004273211647260573024675 0ustar moellermoellerpackage org.jgroups.util; import org.jgroups.logging.Log; import org.jgroups.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 */ 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); private static final long serialVersionUID=6611464897656359215L; /** 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 "[]"; for(Map.Entry entry: senders.entrySet()) { Address key=entry.getKey(); Entry 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 toStringSorted() { StringBuilder sb=new StringBuilder(); boolean first=true; if(senders.isEmpty()) return "[]"; TreeMap copy=new TreeMap(senders); for(Map.Entry entry: copy.entrySet()) { Address key=entry.getKey(); Entry 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; TreeMap copy=new TreeMap(senders); for(Map.Entry entry: copy.entrySet()) { Address key=entry.getKey(); Entry 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; for(Map.Entry entry: senders.entrySet()) { Address key=entry.getKey(); Entry 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()); for(Map.Entry entry: senders.entrySet()) { Entry val=entry.getValue(); Util.writeAddress(entry.getKey(), 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=Util.size(addr); 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; private static final long serialVersionUID=-4468945932249281704L; 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 int hashCode() { return (int)(low_seqno + highest_delivered_seqno + 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 static 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 + ")"); } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/util/DirectExecutor.java0000644000175000017500000000033511647260573026400 0ustar moellermoellerpackage org.jgroups.util; import java.util.concurrent.Executor; /** * @author Bela Ban */ public class DirectExecutor implements Executor { public void execute(Runnable command) { command.run(); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/util/ExposedByteArrayInputStream.java0000644000175000017500000000502411647260573031075 0ustar moellermoellerpackage org.jgroups.util; import java.io.ByteArrayInputStream; import java.io.IOException; /** * @author Bela Ban */ 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; } public void close() throws IOException { buf=null; } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/util/ExposedByteArrayOutputStream.java0000644000175000017500000001556511647260573031311 0ustar moellermoellerpackage 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 */ 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 == null || buf.length > max_size) { buf=new byte[max_size]; } } public byte[] getRawBuffer() { return buf; } public Buffer getBuffer() { return new Buffer(buf, 0, count); } 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 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 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); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/util/ExposedDataOutputStream.java0000644000175000017500000000314111647260573030243 0ustar moellermoellerpackage org.jgroups.util; import java.io.DataOutputStream; import java.io.IOException; import java.io.OutputStream; /** * @author Bela Ban */ 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; } 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. * @throws 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; } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/util/FIFOMessageQueue.java0000644000175000017500000001002511647260573026501 0ustar moellermoellerpackage 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 * @deprecated Will be removed together with the Multiplexer in 3.0 */ @Deprecated 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(); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/util/FixedSizeBitSet.java0000644000175000017500000001624311647260573026461 0ustar moellermoellerpackage org.jgroups.util; /** * Class copied from {@link java.util.BitSet}. Changes are that the FixedSizeBitSet doesn't expand, so access to it * doesn't need to be synchronized, plus we only need a few methods so most methods of the old class have been removed. * @author Bela Ban */ public class FixedSizeBitSet { /* * BitSets are packed into arrays of "words." Currently a word is a long, which consists of 64 bits, requiring * 6 address bits. The choice of word size is determined purely by performance concerns. */ private final static int ADDRESS_BITS_PER_WORD=6; private final static int BITS_PER_WORD=1 << ADDRESS_BITS_PER_WORD; /* Used to shift left or right for a partial word mask */ private static final long WORD_MASK=0xffffffffffffffffL; private final long[] words; private final int size; /** * Creates a bit set whose initial size is the range 0 through * size-1. All bits are initially false. * @param size the initial size of the bit set (in bits). * @throws NegativeArraySizeException if the specified initial size is negative */ public FixedSizeBitSet(int size) { if(size < 0) // nbits can't be negative; size 0 is OK throw new NegativeArraySizeException("size < 0: " + size); this.size=size; words=new long[wordIndex(size - 1) + 1]; } /** * Sets the bit at the specified index to true. * @param index a bit index. * @throws IndexOutOfBoundsException if the specified index is negative. */ public void set(int index) { if(index < 0 || index >= size) throw new IndexOutOfBoundsException("index: " + index); int wordIndex=wordIndex(index); words[wordIndex]|=(1L << index); // Restores invariants } /** * Sets the bit specified by the index to false. * @param index the index of the bit to be cleared. * @throws IndexOutOfBoundsException if the specified index is negative. */ public void clear(int index) { if(index < 0 || index >= size) throw new IndexOutOfBoundsException("index: " + index); int wordIndex=wordIndex(index); words[wordIndex]&=~(1L << index); } /** * Returns the value of the bit with the specified index. The value * is true if the bit with the index index * is currently set in this bit set; otherwise, the result is false. * @param index the bit index. * @return the value of the bit with the specified index. * @throws IndexOutOfBoundsException if the specified index is negative. */ public boolean get(int index) { if(index < 0 || index >= size) throw new IndexOutOfBoundsException("index: " + index); int wordIndex=wordIndex(index); return (words[wordIndex] & (1L << index)) != 0; } /** * Returns the index of the first bit that is set to true that occurs on or after * the specified starting index. If no such bit exists then -1 is returned. *

                * To iterate over the true bits in a BitSet, * use the following loop: *

                *

                     * for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i+1)) {
                     *     // operate on index i here
                     * }
                * @param fromIndex the index to start checking from (inclusive). * @return the index of the next set bit. * @throws IndexOutOfBoundsException if the specified index is negative. */ public int nextSetBit(int fromIndex) { if(fromIndex < 0) throw new IndexOutOfBoundsException("fromIndex: " + fromIndex); if(fromIndex >= size) return -1; int u=wordIndex(fromIndex); long word=words[u] & (WORD_MASK << fromIndex); while(true) { if(word != 0) return (u * BITS_PER_WORD) + Long.numberOfTrailingZeros(word); if(++u == words.length) return -1; word=words[u]; } } /** * Returns the index of the first bit that is set to false * that occurs on or after the specified starting index. * @param fromIndex the index to start checking from (inclusive). * @return the index of the next clear bit. * @throws IndexOutOfBoundsException if the specified index is negative. */ public int nextClearBit(int fromIndex) { // Neither spec nor implementation handle bitsets of maximal length. // See 4816253. if(fromIndex < 0) throw new IndexOutOfBoundsException("fromIndex: " + fromIndex); if(fromIndex >= size) return -1; int u=wordIndex(fromIndex); if(u >= words.length) return fromIndex; long word=~words[u] & (WORD_MASK << fromIndex); while(true) { if(word != 0) return (u * BITS_PER_WORD) + Long.numberOfTrailingZeros(word); if(++u == words.length) return -1; word=~words[u]; } } /** * Returns the number of bits set to true in this bit set * @return the number of bits set to true in this bit set */ public int cardinality() { int sum=0; for(int i=0; i < words.length; i++) sum+=Long.bitCount(words[i]); return sum; } public int size() {return size;} /** * Returns a string representation of this bit set. For every index * for which this BitSet contains a bit in the set * state, the decimal representation of that index is included in * the result. Such indices are listed in order from lowest to * highest, separated by ", " (a comma and a space) and * surrounded by braces, resulting in the usual mathematical * notation for a set of integers.

                * Overrides the toString method of Object. *

                Example: *

                     * BitSet drPepper = new BitSet();
                * Now drPepper.toString() returns "{}".

                *

                     * drPepper.set(2);
                * Now drPepper.toString() returns "{2}".

                *

                     * drPepper.set(4);
                     * drPepper.set(10);
                * Now drPepper.toString() returns "{2, 4, 10}". * @return a string representation of this bit set. */ public String toString() { int numBits=(words.length > 128)? cardinality() : words.length * BITS_PER_WORD; StringBuilder b=new StringBuilder(6 * numBits + 2); b.append('{'); int i=nextSetBit(0); if(i != -1) { b.append(i); for(i=nextSetBit(i + 1); i >= 0; i=nextSetBit(i + 1)) { int endOfRun=nextClearBit(i); do { b.append(", ").append(i); } while(++i < endOfRun); } } b.append('}'); return b.toString(); } private static int wordIndex(int bitIndex) { return bitIndex >> ADDRESS_BITS_PER_WORD; } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/util/FutureListener.java0000644000175000017500000000125511647260573026431 0ustar moellermoellerpackage org.jgroups.util; import java.util.concurrent.Future; /** * A listener that is called back when a future is done. FutureListener instances are attached to {@link * NotifyingFuture}s by passing them in to {@link NotifyingFuture#setListener(FutureListener)} *

                * Note that the {@link #futureDone(Future)} callback is invoked when the future completes, regardless of how the future * completes (i.e., normally, due to an exception, or cancelled}. As such, implementations should check the future * passed in by calling future.get(). * * @author Manik Surtani * @since 2.9 */ public interface FutureListener { void futureDone(Future future); } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/util/GetNetworkInterfaces.java0000644000175000017500000000167311647260573027552 0ustar moellermoellerpackage 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 */ public class GetNetworkInterfaces { public static void main(String[] args) throws SocketException { Enumeration en=NetworkInterface.getNetworkInterfaces(); while(en.hasMoreElements()) { NetworkInterface i=en.nextElement(); System.out.println(i.getName() + ':'); System.out.println(" \t" + i.getDisplayName()); for(Enumeration en2=i.getInetAddresses(); en2.hasMoreElements();) { InetAddress addr=en2.nextElement(); System.out.println(" \t" + addr + " (" + addr.getHostName() + ')'); } System.out.println("---------------------"); } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/util/HashedTimingWheel.java0000644000175000017500000004070011647260573027000 0ustar moellermoellerpackage org.jgroups.util; import org.jgroups.Global; import org.jgroups.annotations.Experimental; import org.jgroups.annotations.Unsupported; import org.jgroups.logging.Log; import org.jgroups.logging.LogFactory; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.concurrent.*; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * Implementation of {@link TimeScheduler}. Uses a hashed timing wheel [1]. * * [1] http://www.cse.wustl.edu/~cdgill/courses/cs6874/TimingWheels.ppt * * @author Bela Ban */ @Experimental @Unsupported public class HashedTimingWheel implements TimeScheduler, Runnable { private final ThreadManagerThreadPoolExecutor pool; private Thread runner=null; private final Lock lock=new ReentrantLock(); protected volatile boolean running=false; protected static final Log log=LogFactory.getLog(HashedTimingWheel.class); protected ThreadDecorator threadDecorator=null; protected ThreadFactory timer_thread_factory=null; protected int wheel_size=200; // number of ticks on the timing wheel protected long tick_time=50L; // number of milliseconds a tick has protected final long ROTATION_TIME;// time for 1 lap protected final List[] wheel; protected int wheel_position=0; // current position of the wheel, run() advances it by one (every TICK_TIME ms) /** * Create a scheduler that executes tasks in dynamically adjustable intervals */ @SuppressWarnings("unchecked") public HashedTimingWheel() { ROTATION_TIME=wheel_size * tick_time; wheel=new List[wheel_size]; pool=new ThreadManagerThreadPoolExecutor(4, 10, 5000, TimeUnit.MILLISECONDS, new LinkedBlockingQueue(5000), Executors.defaultThreadFactory(), new ThreadPoolExecutor.CallerRunsPolicy()); init(); } @SuppressWarnings("unchecked") public HashedTimingWheel(ThreadFactory factory, int min_threads, int max_threads, long keep_alive_time, int max_queue_size, int wheel_size, long tick_time) { this.wheel_size=wheel_size; this.tick_time=tick_time; ROTATION_TIME=wheel_size * tick_time; wheel=new List[this.wheel_size]; timer_thread_factory=factory; pool=new ThreadManagerThreadPoolExecutor(min_threads, max_threads,keep_alive_time, TimeUnit.MILLISECONDS, new LinkedBlockingQueue(max_queue_size), factory, new ThreadPoolExecutor.CallerRunsPolicy()); init(); } @SuppressWarnings("unchecked") public HashedTimingWheel(int corePoolSize) { ROTATION_TIME=wheel_size * tick_time; wheel=(List[])new List[wheel_size]; pool=new ThreadManagerThreadPoolExecutor(corePoolSize, corePoolSize * 2, 5000, TimeUnit.MILLISECONDS, new LinkedBlockingQueue(5000), Executors.defaultThreadFactory(), new ThreadPoolExecutor.CallerRunsPolicy()); init(); } public ThreadDecorator getThreadDecorator() { return threadDecorator; } public void setThreadDecorator(ThreadDecorator threadDecorator) { this.threadDecorator=threadDecorator; pool.setThreadDecorator(threadDecorator); } public void setThreadFactory(ThreadFactory factory) { pool.setThreadFactory(factory); } public int getMinThreads() { return pool.getCorePoolSize(); } public void setMinThreads(int size) { pool.setCorePoolSize(size); } public int getMaxThreads() { return pool.getMaximumPoolSize(); } public void setMaxThreads(int size) { pool.setMaximumPoolSize(size); } public long getKeepAliveTime() { return pool.getKeepAliveTime(TimeUnit.MILLISECONDS); } public void setKeepAliveTime(long time) { pool.setKeepAliveTime(time, TimeUnit.MILLISECONDS); } public int getCurrentThreads() { return pool.getPoolSize(); } public int getQueueSize() { return pool.getQueue().size(); } public String dumpTimerTasks() { StringBuilder sb=new StringBuilder(); lock.lock(); try { for(List list: wheel) { if(!list.isEmpty()) { sb.append(list).append("\n"); } } } finally { lock.unlock(); } return sb.toString(); } public void execute(Runnable task) { schedule(task, 0, TimeUnit.MILLISECONDS); } public Future schedule(Runnable work, long delay, TimeUnit unit) { if(work == null) return null; MyTask retval=null; long time=TimeUnit.MILLISECONDS.convert(delay, unit); // execution time lock.lock(); try { int num_ticks=(int)Math.max(1, ((time % ROTATION_TIME) / tick_time)); int position=(wheel_position + num_ticks) % wheel_size; int rounds=(int)(time / ROTATION_TIME); List list=wheel[position]; retval=new MyTask(work, rounds); list.add(retval); } finally { lock.unlock(); } return retval; } public Future scheduleWithFixedDelay(Runnable task, long initial_delay, long delay, TimeUnit unit) { if(task == null) throw new NullPointerException(); if (isShutdown()) return null; RecurringTask wrapper=new FixedIntervalTask(task, delay); wrapper.doSchedule(initial_delay); return wrapper; } public Future scheduleAtFixedRate(Runnable task, long initial_delay, long delay, TimeUnit unit) { if(task == null) throw new NullPointerException(); if (isShutdown()) return null; RecurringTask wrapper=new FixedRateTask(task, delay); wrapper.doSchedule(initial_delay); return wrapper; } /** * Schedule a task for execution at varying intervals. After execution, the task will get rescheduled after * {@link org.jgroups.util.HashedTimingWheel.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 * 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* */ public Future scheduleWithDynamicInterval(Task task) { if(task == null) throw new NullPointerException(); if (isShutdown()) return null; RecurringTask task_wrapper=new DynamicIntervalTask(task); task_wrapper.doSchedule(); // calls schedule() in ScheduledThreadPoolExecutor return task_wrapper; } /** * Returns the number of tasks currently in the timer * @return The number of tasks currently in the timer */ public int size() { int retval=0; lock.lock(); try { for(List list: wheel) retval+=list.size(); return retval; } finally { lock.unlock(); } } public String toString() { return getClass().getSimpleName(); } /** * Stops the timer, cancelling all tasks * * @throws InterruptedException if interrupted while waiting for thread to return */ public void stop() { stopRunner(); List remaining_tasks=pool.shutdownNow(); for(Runnable task: remaining_tasks) { if(task instanceof Future) { Future future=(Future)task; future.cancel(true); } } pool.getQueue().clear(); try { pool.awaitTermination(Global.THREADPOOL_SHUTDOWN_WAIT_TIME, TimeUnit.MILLISECONDS); } catch(InterruptedException e) { } } public boolean isShutdown() { return pool.isShutdown(); } public void run() { final long base_time=System.currentTimeMillis(); long next_time, sleep_time; long cnt=0; while(running) { try { _run(); next_time=base_time + (++cnt * tick_time); sleep_time=Math.max(0, next_time - System.currentTimeMillis()); Util.sleep(sleep_time); } catch(Throwable t) { log.error("failed executing tasks(s)", t); } } } protected void _run() { lock.lock(); try { wheel_position=(wheel_position +1) % wheel_size; List list=wheel[wheel_position]; if(list.isEmpty()) return; for(Iterator it=list.iterator(); it.hasNext();) { MyTask tmp=it.next(); if(tmp.getAndDecrementRound() <= 0) { try { pool.execute(tmp); } catch(Throwable t) { log.error("failure submitting task to thread pool", t); } it.remove(); } } } finally { lock.unlock(); } } protected void init() { for(int i=0; i < wheel.length; i++) wheel[i]=new LinkedList(); if(threadDecorator != null) pool.setThreadDecorator(threadDecorator); // pool.allowCoreThreadTimeOut(true); startRunner(); } protected void startRunner() { running=true; runner=timer_thread_factory != null? timer_thread_factory.newThread(this, "Timer runner") : new Thread(this, "Timer runner"); runner.start(); } protected void stopRunner() { lock.lock(); try { running=false; for(List list: wheel) { if(!list.isEmpty()) { for(MyTask task: list) task.cancel(true); list.clear(); } } } finally { lock.unlock(); } } /** * Simple task wrapper, always executed by at most 1 thread. */ protected static class MyTask implements Future, Runnable { protected final Runnable task; protected volatile boolean cancelled=false; protected volatile boolean done=false; protected MyTask next; protected int round; public MyTask(Runnable task, int round) { this.task=task; this.round=round; } public int getRound() { return round; } public int getAndDecrementRound() { return round--; } public void setRound(int round) { this.round=round; } public boolean cancel(boolean mayInterruptIfRunning) { boolean retval=!isDone(); cancelled=true; return retval; } public boolean isCancelled() { return cancelled; } public boolean isDone() { return done || cancelled; } public Object get() throws InterruptedException, ExecutionException { return null; } public Object get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { return null; } public void run() { if(isDone()) return; try { task.run(); } catch(Throwable t) { log.error("failed executing task " + task, t); } finally { done=true; } } public String toString() { return task.toString(); } } /** * Task which executes multiple times. An instance of this class wraps the real task and intercepts run(): when * called, it forwards the call to task.run() and then schedules another execution (until cancelled). The * {@link #nextInterval()} method determines the time to wait until the next execution. * @param */ private abstract class RecurringTask implements Runnable, Future { protected final Runnable task; protected volatile Future future; // cannot be null ! protected volatile boolean cancelled=false; public RecurringTask(Runnable task) { this.task=task; } /** * The time to wait until the next execution * @return Number of milliseconds to wait until the next execution is scheduled */ protected abstract long nextInterval(); protected boolean rescheduleOnZeroDelay() {return false;} public void doSchedule() { long next_interval=nextInterval(); if(next_interval <= 0 && !rescheduleOnZeroDelay()) { if(log.isTraceEnabled()) log.trace("task will not get rescheduled as interval is " + next_interval); return; } future=schedule(this, next_interval, TimeUnit.MILLISECONDS); if(cancelled) future.cancel(true); } public void doSchedule(long next_interval) { future=schedule(this, next_interval, TimeUnit.MILLISECONDS); if(cancelled) future.cancel(true); } public void run() { if(cancelled) { if(future != null) future.cancel(true); return; } try { task.run(); } catch(Throwable t) { log.error("failed running task " + task, t); } if(!cancelled) doSchedule(); } public boolean cancel(boolean mayInterruptIfRunning) { boolean retval=!isDone(); cancelled=true; if(future != null) future.cancel(mayInterruptIfRunning); return retval; } public boolean isCancelled() { return cancelled; } public boolean isDone() { return cancelled || (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; } public String toString() { StringBuilder sb=new StringBuilder(); sb.append(getClass().getSimpleName() + ": task=" + task + ", cancelled=" + isCancelled()); return sb.toString(); } } private class FixedIntervalTask extends RecurringTask { final long interval; public FixedIntervalTask(Runnable task, long interval) { super(task); this.interval=interval; } protected long nextInterval() { return interval; } } private class FixedRateTask extends RecurringTask { final long interval; final long first_execution; int num_executions=0; public FixedRateTask(Runnable task, long interval) { super(task); this.interval=interval; this.first_execution=System.currentTimeMillis(); } protected long nextInterval() { long target_time=first_execution + (interval * ++num_executions); return target_time - System.currentTimeMillis(); } protected boolean rescheduleOnZeroDelay() {return true;} } private class DynamicIntervalTask extends RecurringTask { public DynamicIntervalTask(Task task) { super(task); } protected long nextInterval() { if(task instanceof Task) return ((Task)task).nextInterval(); return 0; } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/util/Headers.java0000644000175000017500000001434711647260573025032 0ustar moellermoellerpackage org.jgroups.util; import org.jgroups.Global; import org.jgroups.Header; import org.jgroups.conf.ClassConfigurator; 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 shorts (IDs) and the values Headers, and they're stored in 2 arrays: an ID array and a headers * array. The indices of the IDs array corespond with the headers array, e.g. *

                 * IDs:      id-1  | id-2  | id-3  | ... | id-n |
                 * Headers:  hdr-1 | hdr-2 | hdr-3 | ... | hdr-n |
                 * 
                * * The arrays are populated from left to right, and any 0 slots in 'ids' 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 */ public class Headers { private short[] ids; private Header[] hdrs; /** Add space for 3 new elements when resizing */ private static final int RESIZE_INCR=3; public Headers(int capacity) { ids=new short[capacity]; hdrs=new Header[capacity]; } public Headers(Headers other) { this(other.ids.length); System.arraycopy(other.ids, 0, this.ids, 0, other.ids.length); System.arraycopy(other.hdrs, 0, this.hdrs, 0, other.hdrs.length); } public short[] getRawIDs() { return ids; } public Header[] getRawHeaders() { return hdrs; } /** * Returns the header associated with an ID * @param id The ID * @return */ public Header getHeader(short id) { for(int i=0; i < ids.length; i++) { short current_id=ids[i]; if(current_id == 0) return null; if(current_id == id) return hdrs[i]; } return null; } public Map getHeaders() { Map retval=new HashMap(ids.length); for(int i=0; i < ids.length; i++) { if(ids[i] > 0) retval.put(ids[i], hdrs[i]); else break; } return retval; } public String printHeaders() { StringBuilder sb=new StringBuilder(); boolean first=true; for(int i=0; i < ids.length; i++) { if(ids[i] > 0) { if(first) first=false; else sb.append(", "); Class clazz=ClassConfigurator.getProtocol(ids[i]); String name=clazz != null? clazz.getSimpleName() : Short.toString(ids[i]); sb.append(name).append(": ").append(hdrs[i]); } else break; } return sb.toString(); } /** Puts a header given a key into the hashmap. Overwrites potential existing entry. */ public void putHeader(short id, Header hdr) { _putHeader(id, hdr, 0, true); } /** * Puts a header given a key into the map, only if the key doesn't exist yet * @param id * @param hdr * @return the previous value associated with the specified id, or * null if there was no mapping for the id. * (A null return can also indicate that the map * previously associated null with the id, * if the implementation supports null values.) */ public Header putHeaderIfAbsent(short id, Header hdr) { return _putHeader(id, hdr, 0, false); } /** * * @param id * @return the header associated 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(short id) { return getHeader(id); } public Headers copy() { return new Headers(this); } public int marshalledSize() { int retval=0; for(int i=0; i < ids.length; i++) { if(ids[i] > 0) { retval+=Global.SHORT_SIZE *2; // for protocol ID and magic number retval+=hdrs[i].size(); } else break; } return retval; } public int size() { int retval=0; for(int i=0; i < ids.length; i++) { if(ids[i] > 0) retval++; else break; } return retval; } public int capacity() { return ids.length; } public String printObjectHeaders() { StringBuilder sb=new StringBuilder(); for(int i=0; i < ids.length; i++) { if(ids[i] > 0) sb.append(ids[i]).append(": ").append(hdrs[i]).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_capacity=ids.length + RESIZE_INCR; short[] new_ids=new short[new_capacity]; Header[] new_hdrs=new Header[new_capacity]; System.arraycopy(ids, 0, new_ids, 0, ids.length); System.arraycopy(hdrs, 0, new_hdrs, 0, hdrs.length); ids=new_ids; hdrs=new_hdrs; } private Header _putHeader(short id, Header hdr, int start_index, boolean replace_if_present) { int i=start_index; while(i < ids.length) { if(ids[i] == 0) { ids[i]=id; hdrs[i]=hdr; return null; } if(ids[i] == id) { Header retval=hdrs[i]; if(replace_if_present) { hdrs[i]=hdr; } return retval; } i++; if(i >= ids.length) { resize(); } } throw new IllegalStateException("unable to add element " + id + ", index=" + i); // we should never come here } }libjgroups-java-2.12.2.Final.orig/src/org/jgroups/util/ImmutableReference.java0000644000175000017500000000333111647260573027204 0ustar moellermoeller/* * JBoss, Home of Professional Open Source. * Copyright 2010, Red Hat, Inc. and individual contributors * as indicated by the @author tags. See the copyright.txt file 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.util; /** * Simple class that holds an immutable reference to another object (or to * null). * * @author Brian Stansberry * */ public class ImmutableReference { private final T referent; /** * Create a new ImmutableReference. * * @param referent the object to refer to, or null */ public ImmutableReference(T referent) { this.referent = referent; } /** * Gets the wrapped object, if there is one. * * @return the object passed to the constructor, or null if * null was passed to the constructor */ public T get() { return referent; } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/util/JUnitXMLReporter.java0000644000175000017500000003647111647260573026616 0ustar moellermoellerpackage org.jgroups.util; import org.testng.ITestContext; import org.testng.ITestNGMethod; import org.testng.ITestResult; import org.testng.IInvokedMethod; import org.testng.IInvokedMethodListener; import org.testng.TestListenerAdapter; import org.testng.annotations.Test; import java.io.*; import java.lang.reflect.Method; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; /** * Listener generating XML output suitable to be processed by JUnitReport. * Copied from TestNG (www.testng.org) and modified * * @author Bela Ban */ public class JUnitXMLReporter extends TestListenerAdapter implements IInvokedMethodListener { private String output_dir=null; private String suffix=null; private static final String SUFFIX="test.suffix"; private static final String XML_DEF=""; private static final String CDATA="![CDATA["; private static final String LT="<"; private static final String GT=">"; private static final String SYSTEM_OUT="system-out"; private static final String SYSTEM_ERR="system-err"; private PrintStream old_stdout=System.out; private PrintStream old_stderr=System.err; private final ConcurrentMap,List> classes=new ConcurrentHashMap,List>(); /** Map to keep systemout and systemerr associated with a class */ private final ConcurrentMap,Tuple> outputs=new ConcurrentHashMap,Tuple>(); public static InheritableThreadLocal> local=new InheritableThreadLocal>(); class MyOutput extends PrintStream { final int type; public MyOutput(String fileName,int type) throws FileNotFoundException { super(fileName); this.type=type; if(type != 1 && type != 2) throw new IllegalArgumentException("index has to be 1 or 2"); } public void write(final byte[] b) { String s = new String(b) ; append(s.trim(), false) ; } public void write(final byte[] b, final int off, final int len) { String s = new String(b, off, len) ; append(s.trim(), false) ; } public void write(final int b) { Integer i = new Integer(b) ; append(i.toString(), false) ; } public void println(String s) { append(s, true); } public void print(String s) { append(s, false); } public void print(Object obj) { if(obj != null) append(obj.toString(), false); else append("null", false); } public void println(Object obj) { if(obj != null) append(obj.toString(), true); else append("null", true); } private synchronized void append(String x, boolean newline) { Class clazz=local.get(); if(clazz != null) { Tuple tuple=outputs.get(clazz); if(tuple != null) { StringBuffer sb=type == 1? tuple.getVal1() : tuple.getVal2(); if(sb.length() == 0) { sb.append("\n" + clazz.getName() + ":"); } sb.append("\n").append(x); return; } } PrintStream stream=type == 2? old_stderr : old_stdout; if(newline) stream.println(x); else stream.print(x); } } /** Invoked before any method (configuration or test) is invoked */ public void beforeInvocation(IInvokedMethod method, ITestResult tr) { Class real_class=tr.getTestClass().getRealClass(); local.set(real_class); List results=classes.get(real_class); if(results == null) { results=new LinkedList(); classes.putIfAbsent(real_class, results); } outputs.putIfAbsent(real_class, new Tuple(new StringBuffer(), new StringBuffer())) ; } /** Invoked after any method (configuration or test) is invoked */ public void afterInvocation(IInvokedMethod method, ITestResult tr) { } /* Moved code from onTestStart() to beforeInvocation() to avoid output leaks (JGRP-850) */ public void onTestStart(ITestResult result) { } /** Invoked each time a test succeeds */ public void onTestSuccess(ITestResult tr) { Class real_class=tr.getTestClass().getRealClass(); addTest(real_class, tr); print(old_stdout, "OK: ", real_class.getName(), tr.getName()); } public void onTestFailedButWithinSuccessPercentage(ITestResult tr) { Class real_class=tr.getTestClass().getRealClass(); addTest(tr.getTestClass().getRealClass(), tr); print(old_stdout, "OK: ", real_class.getName(), tr.getName()); } /** * Invoked each time a test fails. */ public void onTestFailure(ITestResult tr) { Class real_class=tr.getTestClass().getRealClass(); addTest(tr.getTestClass().getRealClass(), tr); print(old_stderr, "FAIL: ", real_class.getName(), tr.getName()); } /** * Invoked each time a test is skipped. */ public void onTestSkipped(ITestResult tr) { Class real_class=tr.getTestClass().getRealClass(); addTest(tr.getTestClass().getRealClass(), tr); print(old_stdout, "SKIP: ", real_class.getName(), tr.getName()); } private static void print(PrintStream out, String msg, String classname, String method_name) { out.println(msg + "[" + Thread.currentThread().getId() + "] " + classname + "." + method_name + "()"); } private void addTest(Class clazz, ITestResult result) { List results=classes.get(clazz); if(results == null) { results=new LinkedList(); classes.putIfAbsent(clazz, results); } results=classes.get(clazz); results.add(result); ITestNGMethod[] testMethods=result.getMethod().getTestClass().getTestMethods(); int enabledCount = enabledMethods(testMethods); boolean allTestsInClassCompleted = results.size() >= enabledCount; if(allTestsInClassCompleted){ try { generateReport(clazz, results); } catch(IOException e) { print(old_stderr, "Failed generating report: ", clazz.getName(), ""); } } } private int enabledMethods(ITestNGMethod[] testMethods) { int count = testMethods.length; for(ITestNGMethod testNGMethod:testMethods) { Method m = testNGMethod.getMethod(); if(m.isAnnotationPresent(Test.class)){ Test annotation=m.getAnnotation(Test.class); if(!annotation.enabled()){ count --; } } } return count; } /** * Invoked after the test class is instantiated and before any configuration * method is called. */ public void onStart(ITestContext context) { suffix=System.getProperty(SUFFIX); if(suffix != null) suffix=suffix.trim(); output_dir=context.getOutputDirectory(); // + File.separator + context.getName() + suffix + ".xml"; try { System.setOut(new MyOutput("/tmp/tmp.txt", 1)); } catch(FileNotFoundException e) { } try { System.setErr(new MyOutput("/tmp/tmp.txt", 2)); } catch(FileNotFoundException e) { } } /** * Invoked after all the tests have run and all their Configuration methods * have been called. */ public void onFinish(ITestContext context) { System.setOut(old_stdout); System.setErr(old_stderr); } /** * generate the XML report given what we know from all the test results */ protected void generateReport(Class clazz, List results) throws IOException { int num_failures=getFailures(results); int num_skips=getSkips(results); int num_errors=getErrors(results); long total_time=getTotalTime(results); String file_name=output_dir + File.separator + "TEST-" + clazz.getName(); if(suffix != null) file_name=file_name + "-" + suffix; file_name=file_name + ".xml"; FileWriter out=new FileWriter(file_name, false); // don't append, overwrite try { out.write(XML_DEF + "\n"); out.write("\n"); out.write("\n"); Properties props=System.getProperties(); for(Map.Entry tmp:props.entrySet()) { out.write("\n "); } out.write("\n\n"); for(ITestResult result:results) { if(result == null) continue; long time=result.getEndMillis() - result.getStartMillis(); out.write("\n "); Throwable ex=result.getThrowable(); switch(result.getStatus()) { case ITestResult.SUCCESS: case ITestResult.SUCCESS_PERCENTAGE_FAILURE: break; case ITestResult.FAILURE: writeFailure("failure", result.getMethod().getMethod(), ex, "exception", out); break; case ITestResult.SKIP: writeFailure("error", result.getMethod().getMethod(), ex, "SKIPPED", out); break; default: writeFailure("error", result.getMethod().getMethod(), ex, "exception", out); } out.write("\n"); } Tuple stdout=outputs.get(clazz); if(stdout != null) { StringBuffer system_out=stdout.getVal1(); StringBuffer system_err=stdout.getVal2(); writeOutput(out, system_out.toString(), 1); out.write("\n"); writeOutput(out, system_err.toString(), 2); } out.write("\n\n"); } finally { out.close(); } } private static void writeOutput(FileWriter out, String s, int type) throws IOException { if(s != null && s.length() > 0) { out.write("\n<" + (type == 2? SYSTEM_ERR : SYSTEM_OUT) + "><" + CDATA + "\n"); out.write(s); out.write("\n]]>"); out.write("\n"); } } private static void writeFailure(String type, Method method, Throwable ex, String msg, FileWriter out) throws IOException { Test annotation=method.getAnnotation(Test.class); if(annotation != null && ex != null) { Class[] expected_exceptions=annotation.expectedExceptions(); for(int i=0;i < expected_exceptions.length;i++) { Class expected_exception=expected_exceptions[i]; if(expected_exception.equals(ex.getClass())) { return; } } } out.write("\n<" + type + " type=\""); if(ex != null) { out.write(ex.getClass().getName() + "\" message=\"" + escape(ex.getMessage()) + "\">"); printException(ex, out); } else out.write("exception\" message=\"" + msg + "\">"); out.write("\n"); } private static void printException(Throwable ex, FileWriter out) throws IOException { if(ex == null) return; StackTraceElement[] stack_trace=ex.getStackTrace(); out.write("\n<" + CDATA + "\n"); out.write(ex.getClass().getName() + " \n"); for(int i=0;i < stack_trace.length;i++) { StackTraceElement frame=stack_trace[i]; try { out.write("at " + frame.toString() + " \n"); } catch(IOException e) { } } out.write("\n]]>"); } private static String escape(String message) { return message != null? message.replaceAll("<", LT).replaceAll(">", GT) : message; } private static long getTotalTime(List results) { long start=0, stop=0; for(ITestResult result:results) { if(result == null) continue; long tmp_start=result.getStartMillis(), tmp_stop=result.getEndMillis(); if(start == 0) start=tmp_start; else { start=Math.min(start, tmp_start); } if(stop == 0) stop=tmp_stop; else { stop=Math.max(stop, tmp_stop); } } return stop - start; } private static int getFailures(List results) { int retval=0; for(ITestResult result:results) { if(result != null && result.getStatus() == ITestResult.FAILURE) retval++; } return retval; } private static int getErrors(List results) { int retval=0; for(ITestResult result:results) { if(result != null && result.getStatus() != ITestResult.SUCCESS && result.getStatus() != ITestResult.SUCCESS_PERCENTAGE_FAILURE && result.getStatus() != ITestResult.FAILURE) retval++; } return retval; } private static int getSkips(List results) { int retval=0; for(ITestResult result:results) { if(result != null && result.getStatus() == ITestResult.SKIP) retval++; } return retval; } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/util/LazyThreadFactory.java0000644000175000017500000000540511647260573027051 0ustar moellermoellerpackage 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 */ 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; } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/util/MarshallerPool.java0000644000175000017500000000445011647260573026375 0ustar moellermoellerpackage org.jgroups.util; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * Provides a pool of output streams so we can do lock striping and have faster marshalling this way. * @author Bela Ban */ @Deprecated public class MarshallerPool { final int pool_size; // number of pools final int INITIAL_SIZE; // initial size of each pool final ExposedByteArrayOutputStream[] outs; final ExposedDataOutputStream[] outputs; final Lock[] locks; public MarshallerPool(int pool_size, int initial_size) { this.pool_size=pool_size; INITIAL_SIZE=initial_size; outs=new ExposedByteArrayOutputStream[pool_size]; for(int i=0; i < outs.length; i++) outs[i]=new ExposedByteArrayOutputStream(INITIAL_SIZE); outputs=new ExposedDataOutputStream[pool_size]; for(int i=0; i < outputs.length; i++) outputs[i]=new ExposedDataOutputStream(outs[i]); locks=new Lock[pool_size]; for(int i=0; i < locks.length; i++) locks[i]=new ReentrantLock(); } /** * Returns a random output stream. To use it, the lock needs to be acquired. * When done, it also needs to be released again. * @return */ public Triple getOutputStream() { int index=(int)Util.random(pool_size) -1; return new Triple(locks[index], outs[index], outputs[index]); } public int[] getCapacities() { int[] retval=new int[pool_size]; for(int i=0; i < outs.length; i++) retval[i]=outs[i].getCapacity(); return retval; } /** Closes all output streams. This releases the memory held by them */ public void close() { for(int i=0; i < pool_size; i++) { try { locks[i].tryLock(2000, TimeUnit.MILLISECONDS); Util.close(outputs[i]); } catch(InterruptedException e) { Thread.currentThread().interrupt(); } finally { if(((ReentrantLock)locks[i]).isHeldByCurrentThread()) locks[i].unlock(); } } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/util/MergeId.java0000644000175000017500000000272111647260573024764 0ustar moellermoellerpackage org.jgroups.util; import org.jgroups.Address; import org.jgroups.Global; import java.io.DataOutputStream; import java.io.IOException; import java.io.DataInputStream; /** ID to uniquely identify a merge * @author Bela Ban */ public class MergeId implements Streamable { private Address initiator; // must be non-null private int id; private static int LAST_ID=1; public MergeId() {} private MergeId(Address initiator, int id) { this.initiator=initiator; this.id=id; } public synchronized static MergeId create(Address addr) { int id=LAST_ID++; if(addr == null) throw new IllegalArgumentException("initiator has to be non null"); return new MergeId(addr, id); } public boolean equals(Object obj) { return obj instanceof MergeId && initiator.equals(((MergeId)obj).initiator) && id == ((MergeId)obj).id; } public int hashCode() { return initiator.hashCode() + id; } public int size() { return Util.size(initiator) + Global.INT_SIZE; } public void writeTo(DataOutputStream out) throws IOException { Util.writeAddress(initiator, out); out.writeInt(id); } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { initiator=Util.readAddress(in); id=in.readInt(); } public String toString() { return initiator + "::" + id; } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/util/Metronome.java0000644000175000017500000000556511647260573025426 0ustar moellermoellerpackage org.jgroups.util; import java.util.Set; import java.util.HashSet; import java.util.Arrays; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.Condition; /** * @author Bela Ban */ public class Metronome implements Runnable { private final Set threads=new HashSet(); private int tick=0; private volatile Thread worker=null; private int interval=10; private final Lock lock=new ReentrantLock(); private final Condition cond=lock.newCondition(); public Metronome(int interval) { this.interval=interval; } public void add(final Thread ... thread) { synchronized(threads) { threads.addAll(Arrays.asList(thread)); if(worker == null || !worker.isAlive()) { worker=new Thread(this, "MetronomeThread"); worker.setDaemon(true); worker.start(); } } } public void remove(final Thread thread) { synchronized(threads) { threads.remove(thread); } } public int getTick() { lock.lock(); try { return tick; } finally { lock.unlock(); } } public void waitFor(int tick) { add(Thread.currentThread()); while(true) { lock.lock(); try { if(tick > this.tick) { try { cond.await(); } catch(InterruptedException e) { Thread.currentThread().interrupt(); } } else return; } finally { lock.unlock(); } } } public void stop() { worker=null; synchronized(threads) { threads.clear(); } } public void run() { while(worker != null && Thread.currentThread().equals(worker)) { boolean all_blocked=true; synchronized(threads) { if(threads.isEmpty()) break; for(Thread tmp: threads) { Thread.State state=tmp.getState(); if(state != Thread.State.WAITING && state != Thread.State.BLOCKED) { all_blocked=false; // System.out.println("state of " + tmp + ": " + state); break; } } } if(all_blocked) { lock.lock(); try { tick++; cond.signalAll(); } finally { lock.unlock(); } } Util.sleep(interval); } worker=null; } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/util/MutableDigest.java0000644000175000017500000001446611647260573026212 0ustar moellermoellerpackage 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 */ 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(); senders.put(sender, 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 highest delivered seqno by 1 */ public void incrementHighestDeliveredSeqno(Address sender) { Entry entry=senders.get(sender); if(entry == null) return; checkSealed(); long new_highest_delivered=entry.getHighestDeliveredSeqno() +1; // highest_received must be >= highest_delivered, but not smaller ! long new_highest_received=Math.max(entry.getHighestReceivedSeqno(), new_highest_delivered); Entry new_entry=new Entry(entry.getLow(), new_highest_delivered, new_highest_received); 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"); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/util/MyReceiver.java0000644000175000017500000000163711647260573025527 0ustar moellermoellerpackage org.jgroups.util; import org.jgroups.ReceiverAdapter; import org.jgroups.Message; import org.jgroups.View; import java.util.List; import java.util.ArrayList; import java.util.Collection; import java.util.concurrent.ConcurrentLinkedQueue; /** * Simple receiver which buffers all messages * @author Bela Ban */ public class MyReceiver extends ReceiverAdapter { protected final Collection msgs=new ConcurrentLinkedQueue(); protected final String name; public MyReceiver(String name) { this.name=name; } public Collection getMsgs() { return msgs; } public void clear() {msgs.clear();} public void receive(Message msg) { System.out.println("[" + name + "] received message " + msg); msgs.add(msg); } public void viewAccepted(View new_view) { System.out.println("[" + name + "] view: " + new_view); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/util/NotifyingFuture.java0000644000175000017500000000117711647260573026615 0ustar moellermoellerpackage org.jgroups.util; import java.util.concurrent.Future; /** * A sub-interface of a Future, that allows for listeners to be attached so that observers can be notified when the * future completes. *

                * See {@link FutureListener} for more details. *

                * * @author Manik Surtani * @since 2.9 */ public interface NotifyingFuture extends Future { /** * Attaches a listener and returns the same future instance, to allow for 'building' futures. * * @param listener listener to attach * @return the same future instance */ NotifyingFuture setListener(FutureListener listener); } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/util/NullFuture.java0000644000175000017500000000167211647260573025561 0ustar moellermoellerpackage org.jgroups.util; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; /** * @author Bela Ban */ public class NullFuture implements NotifyingFuture { final T retval; public NullFuture(T retval) { this.retval=retval; } public boolean cancel(boolean mayInterruptIfRunning) { return true; } public boolean isCancelled() { return true; } public boolean isDone() { return true; } public T get() throws InterruptedException, ExecutionException { return retval; } public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { return retval; } public NotifyingFuture setListener(FutureListener listener) { if(listener != null) listener.futureDone(this); return this; } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/util/PayloadUUID.java0000644000175000017500000000514611647260573025534 0ustar moellermoellerpackage org.jgroups.util; import org.jgroups.Global; import java.io.*; import java.security.SecureRandom; /** * Subclass of {@link UUID} which adds a string as payload. An instance of this can be fed to * {@link org.jgroups.JChannel#setAddressGenerator(org.jgroups.stack.AddressGenerator)}, with the address generator * creating PayloadUUIDs. * @author Bela Ban */ public class PayloadUUID extends UUID { private static final long serialVersionUID=-7042544908572216601L; // don't need this as we already added PayloadUUID to jg-magic-map.xml // static { // ClassConfigurator.add((short)2222, PayloadUUID.class); // } protected String payload; public PayloadUUID() { } protected PayloadUUID(byte[] data, String payload) { super(data); this.payload=payload; } public static PayloadUUID randomUUID(String payload) { return new PayloadUUID(generateRandomBytes(), payload); } public static PayloadUUID randomUUID(String logical_name, String payload) { PayloadUUID retval=new PayloadUUID(generateRandomBytes(), payload); UUID.add(retval, logical_name); return retval; } public String getPayload() { return payload; } public void setPayload(String payload) { this.payload=payload; } protected static byte[] generateRandomBytes() { SecureRandom ng=numberGenerator; if(ng == null) numberGenerator=ng=new SecureRandom(); byte[] randomBytes=new byte[16]; ng.nextBytes(randomBytes); return randomBytes; } public int size() { int retval=super.size() + Global.BYTE_SIZE; if(payload != null) retval+=payload.length() +2; return retval; } public void writeTo(DataOutputStream out) throws IOException { super.writeTo(out); Util.writeString(payload, out); } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { super.readFrom(in); payload=Util.readString(in); } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { super.readExternal(in); payload=(String)in.readObject(); } public void writeExternal(ObjectOutput out) throws IOException { super.writeExternal(out); out.writeObject(payload); } public String toString() { if(print_uuids) return toStringLong() + (payload == null? "" : "(" + payload + ")"); return super.toString() + (payload == null? "" : "(" + payload + ")"); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/util/Promise.java0000644000175000017500000001010411647260573025060 0ustar moellermoeller package 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 */ public class Promise { private final Lock lock=new ReentrantLock(); private final Condition cond=lock.newCondition(); private T result=null; private volatile 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; } }libjgroups-java-2.12.2.Final.orig/src/org/jgroups/util/PropertiesToXML.java0000644000175000017500000003634211647260573026476 0ustar moellermoellerpackage org.jgroups.util; import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.Reader; import java.io.StringWriter; import java.io.Writer; import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.net.URL; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.TreeMap; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.OutputKeys; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import org.jgroups.annotations.Experimental; import org.jgroups.annotations.Property; import org.jgroups.annotations.Unsupported; import org.jgroups.logging.Log; import org.jgroups.logging.LogFactory; import org.jgroups.stack.Protocol; import org.w3c.dom.DOMImplementation; import org.w3c.dom.Document; import org.w3c.dom.Element; /** * Iterates over all concrete Protocol classes and creates tables with Protocol's properties. * These tables are in turn then merged into docbook. * * Iterates over unsupported and experimental classes and creates tables listing those classes. * These tables are in turn then merged into docbook. * * @author Vladimir Blagojevic * */ public class PropertiesToXML { protected static final Log log = LogFactory.getLog(PropertiesToXML.class); public static void main(String[] args) { String input = "doc/manual/en/modules/protocols.xml"; String input2 = "doc/manual/en/modules/installation.xml"; if (args.length != 1) { help(); return; } input = args[0]; String temp_file = input + ".tmp"; String temp_file2 = input2 + ".tmp"; try { // first copy protocols.xml file into protocols-temp.xml File f = new File(temp_file); copy(new FileReader(new File(input)), new FileWriter(f)); String s = fileToString(f); Set> classes = findClassesAssignableFrom("org.jgroups.protocols", Protocol.class); classes.addAll(findClassesAssignableFrom("org.jgroups.protocols.pbcast", Protocol.class)); Properties props = new Properties(); for (Class clazz : classes) { convertToDocbookTable(props, clazz); } String result = Util.replaceProperties(s, props); FileWriter fw = new FileWriter(f, false); fw.write(result); fw.flush(); fw.close(); // copy installation.xml file into installation-temp.xml f = new File(temp_file2); copy(new FileReader(new File(input2)), new FileWriter(f)); s = fileToString(f); props = new Properties(); List> unsupportedClasses = findClassesAnnotatedWith("org.jgroups",Unsupported.class); convertToDocbookTable(props, unsupportedClasses, "Unsupported"); List> experimentalClasses = findClassesAnnotatedWith("org.jgroups",Experimental.class); convertToDocbookTable(props, experimentalClasses, "Experimental"); result = Util.replaceProperties(s, props); fw = new FileWriter(f, false); fw.write(result); fw.flush(); fw.close(); } catch (Exception e) { e.printStackTrace(); } } static void help() { System.out.println("PropertiesToXML "); } private static Set> findClassesAssignableFrom(String packageName, Class assignableFrom) throws IOException, ClassNotFoundException { ClassLoader loader = Thread.currentThread().getContextClassLoader(); Set> classes = new HashSet>(); String path = packageName.replace('.', '/'); URL resource = loader.getResource(path); if (resource != null) { String filePath = resource.getFile(); if (filePath != null && new File(filePath).isDirectory()) { for (String file : new File(filePath).list()) { if (file.endsWith(".class")) { String name = packageName + '.' + file.substring(0, file.indexOf(".class")); Class clazz = Class.forName(name); if (assignableFrom.isAssignableFrom(clazz)) classes.add(clazz); } } } } return classes; } private static List> findClassesAnnotatedWith(String packageName, Class a) throws IOException, ClassNotFoundException { List> classes = new ArrayList>(); recurse(classes, packageName, a); return classes; } private static void recurse(List> classes, String packageName, Class a) throws ClassNotFoundException { ClassLoader loader = Thread.currentThread().getContextClassLoader(); String path = packageName.replace('.', '/'); URL resource = loader.getResource(path); if (resource != null) { String filePath = resource.getFile(); if (filePath != null && new File(filePath).isDirectory()) { for (String file : new File(filePath).list()) { if (file.endsWith(".class")) { String name = packageName + '.' + file.substring(0, file.indexOf(".class")); Class clazz = Class.forName(name); if (clazz.isAnnotationPresent(a)) classes.add(clazz); } else if (new File(filePath,file).isDirectory()) { recurse(classes, packageName + "." + file, a); } } } } } private static void convertToDocbookTable(Properties props, List> clazzes, String title) throws ParserConfigurationException, TransformerException { Document xmldoc = null; DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); DOMImplementation impl = builder.getDOMImplementation(); xmldoc = impl.createDocument(null, "table", null); Element tbody = createXMLTable(xmldoc, title); for (Class clazz : clazzes) { Element row = xmldoc.createElement("row"); Element entry = xmldoc.createElement("entry"); entry.setTextContent(clazz.getPackage().getName()); row.appendChild(entry); entry = xmldoc.createElement("entry"); entry.setTextContent(clazz.getSimpleName()); row.appendChild(entry); tbody.appendChild(row); } // do we have more than one property (superclass Protocol has only one property (stats)) if (clazzes != null && clazzes.size() > 1) { DOMSource domSource = new DOMSource(xmldoc); StringWriter sw = new StringWriter(); StreamResult streamResult = new StreamResult(sw); TransformerFactory tf = TransformerFactory.newInstance(); Transformer serializer = tf.newTransformer(); serializer.setOutputProperty(OutputKeys.ENCODING, "ISO-8859-1"); serializer.setOutputProperty(OutputKeys.INDENT, "yes"); serializer.transform(domSource, streamResult); StringBuffer buffer = sw.getBuffer(); buffer.delete(0, buffer.indexOf("table") - 1); props.put(title, buffer.toString()); } } private static void convertToDocbookTable(Properties props, Class clazz) throws ParserConfigurationException, TransformerException { boolean isConcreteClass = (clazz.getModifiers() & Modifier.ABSTRACT) == 0; boolean isExperimental = clazz.isAnnotationPresent(Experimental.class); boolean isUnsupported = clazz.isAnnotationPresent(Unsupported.class); // if(isConcreteClass && !isExperimental && !isUnsupported) { if (isConcreteClass && !isUnsupported) { Class protocol = clazz; Document xmldoc = null; DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); DOMImplementation impl = builder.getDOMImplementation(); xmldoc = impl.createDocument(null, "table", null); Element tbody = createXMLTree(xmldoc, isExperimental); Map nameToDescription = new TreeMap(); // iterate fields for (Class clazzInLoop = clazz; clazzInLoop != null; clazzInLoop = clazzInLoop .getSuperclass()) { Field[] fields = clazzInLoop.getDeclaredFields(); for (Field field : fields) { if (field.isAnnotationPresent(Property.class)) { String property = field.getName(); Property annotation = field.getAnnotation(Property.class); String desc = annotation.description(); nameToDescription.put(property, desc); } } } // iterate methods Method[] methods = clazz.getMethods(); for (Method method : methods) { if (method.isAnnotationPresent(Property.class) && method.getName().startsWith("set")) { Property annotation = method.getAnnotation(Property.class); String desc = annotation.description(); if (desc.length() > 0) { String name = annotation.name(); if (name.length() < 1) { name = Util.methodNameToAttributeName(method.getName()); } nameToDescription.put(name, desc); } } } // and write them out for (Map.Entry e : nameToDescription.entrySet()) { Element row = xmldoc.createElement("row"); Element entry = xmldoc.createElement("entry"); entry.setTextContent(e.getKey()); row.appendChild(entry); entry = xmldoc.createElement("entry"); entry.setTextContent(e.getValue()); row.appendChild(entry); tbody.appendChild(row); } // do we have more than one property (superclass Protocol has only one property (stats)) if (nameToDescription.size() > 1) { DOMSource domSource = new DOMSource(xmldoc); StringWriter sw = new StringWriter(); StreamResult streamResult = new StreamResult(sw); TransformerFactory tf = TransformerFactory.newInstance(); Transformer serializer = tf.newTransformer(); serializer.setOutputProperty(OutputKeys.ENCODING, "ISO-8859-1"); serializer.setOutputProperty(OutputKeys.INDENT, "yes"); serializer.transform(domSource, streamResult); StringBuffer buffer = sw.getBuffer(); buffer.delete(0, buffer.indexOf("table") - 1); props.put(protocol.getSimpleName(), buffer.toString()); } } } private static String fileToString(File f) throws Exception { StringWriter output = new StringWriter(); FileReader input = new FileReader(f); char[] buffer = new char[8 * 1024]; int count = 0; int n = 0; while (-1 != (n = input.read(buffer))) { output.write(buffer, 0, n); count += n; } return output.toString(); } public static int copy(Reader input, Writer output) throws IOException { char[] buffer = new char[8 * 1024]; int count = 0; int n = 0; try { while (-1 != (n = input.read(buffer))) { output.write(buffer, 0, n); count += n; } } finally { output.flush(); output.close(); } return count; } private static Element createXMLTree(Document xmldoc, boolean experimental) throws ParserConfigurationException { Element root = xmldoc.getDocumentElement(); Element title = xmldoc.createElement("title"); if (experimental) title.setTextContent("Properties (experimental)"); else title.setTextContent("Properties"); root.appendChild(title); Element tgroup = xmldoc.createElement("tgroup"); tgroup.setAttribute("cols", "2"); root.appendChild(tgroup); Element colspec = xmldoc.createElement("colspec"); colspec.setAttribute("align", "left"); tgroup.appendChild(colspec); Element thead = xmldoc.createElement("thead"); tgroup.appendChild(thead); Element row = xmldoc.createElement("row"); thead.appendChild(row); Element entry = xmldoc.createElement("entry"); entry.setAttribute("align", "center"); entry.setTextContent("Name"); row.appendChild(entry); entry = xmldoc.createElement("entry"); entry.setAttribute("align", "center"); entry.setTextContent("Description"); row.appendChild(entry); Element tbody = xmldoc.createElement("tbody"); tgroup.appendChild(tbody); return tbody; } private static Element createXMLTable(Document xmldoc, String titleContent) throws ParserConfigurationException { Element root = xmldoc.getDocumentElement(); Element title = xmldoc.createElement("title"); title.setTextContent(titleContent); root.appendChild(title); Element tgroup = xmldoc.createElement("tgroup"); tgroup.setAttribute("cols", "2"); root.appendChild(tgroup); Element colspec = xmldoc.createElement("colspec"); colspec.setAttribute("align", "left"); tgroup.appendChild(colspec); Element thead = xmldoc.createElement("thead"); tgroup.appendChild(thead); Element row = xmldoc.createElement("row"); thead.appendChild(row); Element entry = xmldoc.createElement("entry"); entry.setAttribute("align", "center"); entry.setTextContent("Package"); row.appendChild(entry); entry = xmldoc.createElement("entry"); entry.setAttribute("align", "center"); entry.setTextContent("Class"); row.appendChild(entry); Element tbody = xmldoc.createElement("tbody"); tgroup.appendChild(tbody); return tbody; } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/util/Proxy.java0000644000175000017500000007374711647260573024611 0ustar moellermoeller package org.jgroups.util; import org.jgroups.annotations.Unsupported; import org.jgroups.annotations.Experimental; 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.

                * Note that this class is exoerimental and is not supported ! * @todo Check whether SSLSocket.getChannel() or SSLServerSocket.getChannel() works. * @author Bela Ban */ @Unsupported @Experimental 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(); } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/util/Queue.java0000644000175000017500000004646111647260573024545 0ustar moellermoeller package org.jgroups.util; import org.jgroups.logging.Log; import org.jgroups.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 volatile boolean closed=false; /*current size of the queue*/ private volatile 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(); } } /** * 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 * (bela Aug 2009) Note that the semantics of remove(long timeout) are weird - the method waits until an element has * been added, but doesn't do so in a loop ! So if we have 10 threads waiting on an empty queue, and 1 thread * adds an element, all 10 threads will return (but only 1 will have the element), therefore 9 will throw * a TimeoutException ! If I change this to the 'correct' semantics, however (e.g. the method removeWait() below), * GMS.ViewHandler doesn't work correctly anymore. I won't change this now, as Queue will get removed anyway in 3.0. * @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; } } public Object removeWait(long timeout) throws QueueClosedException, TimeoutException { synchronized(mutex) { if(closed) throw new QueueClosedException(); final long end_time=System.currentTimeMillis() + timeout; long wait_time, current_time; /*if the queue size is zero, we want to wait until a new object is added*/ while(size == 0 && (current_time=System.currentTimeMillis()) < end_time) { if(closed) throw new QueueClosedException(); try { /*release the mutex lock and wait no more than timeout ms*/ wait_time=end_time - current_time; // guarnteed to be > 0 mutex.wait(wait_time); } catch(InterruptedException ex) { } } /*we either timed out, or got notified by the mutex lock object*/ if(closed) throw new QueueClosedException(); /*get the next value*/ Object retval=removeInternal(); /*null result means we timed out*/ if(retval == null) throw new TimeoutException("timeout=" + timeout + "ms"); 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 -------------------------------- */ } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/util/Queue.java.concurrent0000644000175000017500000000451611647260573026721 0ustar moellermoeller 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(); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/util/QueueClosedException.java0000644000175000017500000000074011647260573027544 0ustar moellermoeller 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"; } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/util/Range.java0000644000175000017500000000260311647260573024503 0ustar moellermoeller package org.jgroups.util; import java.io.*; public class Range implements Externalizable, Streamable, Comparable { 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 int compareTo(Range other) { if(low == other.low && high == other.high) return 0; return low < other.low? -1 : 1; } public int hashCode() { return (int)low; } public boolean equals(Object obj) { Range other=(Range)obj; return compareTo(other) == 0; } 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(); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/util/ResourceManager.java0000644000175000017500000001466411647260573026543 0ustar moellermoellerpackage org.jgroups.util; import org.jgroups.Global; import java.net.*; import java.rmi.server.UID; import java.util.ArrayList; import java.util.List; import java.util.StringTokenizer; /** * Manages resources such as multicast addresses and multicast ports, and TCP * ports. This class is mainly used for running unit tests in parallel (TestNG) * and preventing clusters intended to be separate from joining each other. * * @author Bela Ban */ public class ResourceManager { private static final IpAddressRep rep; private static short mcast_port; private static short tcp_port; private static SocketFactory socket_factory=new DefaultSocketFactory(); static { StackType type=Util.getIpStackType(); String tmp_addr = System.getProperty(Global.INITIAL_MCAST_ADDR, type == StackType.IPv6? "ff0e::9:9:9" : "230.1.1.1"); mcast_port = Short.valueOf(System.getProperty(Global.INITIAL_MCAST_PORT, "7000")); tcp_port = Short.valueOf(System.getProperty(Global.INITIAL_TCP_PORT, "10000")); try { InetAddress tmp = InetAddress.getByName(tmp_addr); if (!tmp.isMulticastAddress()) throw new IllegalArgumentException("initial multicast address " + tmp_addr + " is not a valid multicast address"); if (tmp instanceof Inet4Address) rep = new IPv4AddressRep(tmp_addr); else rep = new IPv6AddressRep(tmp_addr); } catch (UnknownHostException e) { throw new RuntimeException("initial multicast address " + tmp_addr + " is incorrect", e); } } private ResourceManager() { } /** * Returns the next available multicast address, e.g. "228.1.2.3". This * class is a JVM singleton * * @return */ public static String getNextMulticastAddress() { return rep.nextAddress(); } public static synchronized short getNextMulticastPort(InetAddress bind_addr) throws Exception { short port = mcast_port; try { DatagramSocket sock = Util.createDatagramSocket(socket_factory, "jgroups.temp.resourcemgr.mcast_sock", bind_addr, port); port = (short) sock.getLocalPort(); socket_factory.close(sock); return port; } finally { mcast_port = (short) (port + 1); } } public static synchronized List getNextTcpPorts(InetAddress bind_addr, int num_requested_ports) throws Exception { short port = tcp_port++; List retval = new ArrayList(num_requested_ports); for (int i = 0; i < num_requested_ports; i++) { ServerSocket sock = Util.createServerSocket(socket_factory, "jgroups.temp.resourcemgr.srv_sock", bind_addr, port); port = (short) sock.getLocalPort(); retval.add(port); tcp_port = ++port; socket_factory.close(sock); } return retval; } public static String getUniqueClusterName(String base_name) { return base_name != null ? base_name + "-" + new UID().toString() : new UID().toString(); } public static String getUniqueClusterName() { return getUniqueClusterName(null); } public static void main(String[] args) throws Exception { List ports = getNextTcpPorts(InetAddress.getByName("192.168.1.5"), 15); System.out.println("ports = " + ports); ports = getNextTcpPorts(InetAddress.getByName("192.168.1.5"), 5); System.out.println("ports = " + ports); } /* * Interface for IpAddress representations */ public interface IpAddressRep { public String nextAddress(); } /** Representation of an IPv4 address */ static class IPv4AddressRep implements IpAddressRep { short a = 225, b = MIN, c = MIN, d = MIN; private static final short MIN = 1, MAX = 250; private static final char DOT = '.'; IPv4AddressRep(String initial_addr) { StringTokenizer tok = new StringTokenizer(initial_addr, ".", false); a = Short.valueOf(tok.nextToken()); b = Short.valueOf(tok.nextToken()); c = Short.valueOf(tok.nextToken()); d = Short.valueOf(tok.nextToken()); } public synchronized String nextAddress() { StringBuilder sb = new StringBuilder(); sb.append(a).append(DOT).append(b).append(DOT).append(c) .append(DOT).append(d); increment(); return sb.toString(); } private void increment() { d++; if (d > MAX) { d = MIN; c++; if (c > MAX) { c = MIN; b++; if (b > MAX) { b = MIN; a++; if (a > MAX) a = 225; } } } } } /** Representation of an IPv6 address */ static class IPv6AddressRep implements IpAddressRep { byte[] bv = null; InetAddress address = null; private static boolean carry = false; IPv6AddressRep(String initial_addr) { try { address = InetAddress.getByName(initial_addr); } catch (UnknownHostException e) { // throw new RuntimeException ("Multicast address " + initial_addr + " has incorrect format", e) ; } catch (SecurityException e) { // throw new RuntimeException ("Security violation in accessing multicast address " + initial_addr, e) ; } // get the byte representation bv = address.getAddress(); } public synchronized String nextAddress() { // build the existing address, then create the next one try { address = InetAddress.getByAddress(bv); } catch (UnknownHostException e) { // throw new RuntimeException ("Multicast address has incorrect length", e) ; } increment(); // strings are returned as hostname/IP address, so remove the hostname prefix // before returning the string String addressWithHostname = address.toString(); return addressWithHostname.substring(addressWithHostname.indexOf('/')+1) ; } private void increment() { // process hex digits from right to left for (int i = bv.length - 1; i >= 0; i--) { // increment the ith byte bv[i] =incrementHexValue(bv[i]); // if carry, increment (i-1)th byte if (carry) { carry = false; continue; } // otherwise, we are done return ; } // if we reach here, incrementing no longer possible throw new RuntimeException ("Cannot increment multicast address ") ; } // increments a byte in hex and notes if carry req'd // these bytes contain 2's complement representations // of hex values "00" through "ff" private static byte incrementHexValue(byte b) { // "00" to "7e" if (b >= 0 && b < 127) return (byte) (b + (byte) 1); // "7f" else if (b == 127) { return (byte) -128; } // "80" to "fe" else if (b >= -128 && b < -1) { return (byte) (b + (byte) 1); } // "ff" else if (b == -1) { carry = true; return (byte) 0; } return 0; } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/util/ResponseCollector.java0000644000175000017500000001142411647260573027115 0ustar moellermoellerpackage org.jgroups.util; import org.jgroups.Address; import org.jgroups.annotations.GuardedBy; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.Collections; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.Condition; import java.util.concurrent.TimeUnit; /** Similar to AckCollector, but collects responses, not just acks. Null is not a valid key. * @author Bela Ban */ public class ResponseCollector { @GuardedBy("lock") private final Map responses; private final Lock lock=new ReentrantLock(false); private final Condition cond=lock.newCondition(); /** * * @param members List of members from which we expect responses */ public ResponseCollector(Collection
                members) { responses=members != null? new HashMap(members.size()) : new HashMap(); reset(members); } public ResponseCollector(Address ... members) { responses=members != null? new HashMap(members.length) : new HashMap(); reset(members); } public ResponseCollector() { responses=new HashMap(); } public void add(Address member, T data) { if(member == null) return; lock.lock(); try { if(responses.containsKey(member)) { responses.put(member, data); cond.signalAll(); } } finally { lock.unlock(); } } public void remove(Address member) { if(member == null) return; lock.lock(); try { responses.remove(member); cond.signalAll(); } finally { lock.unlock(); } } public void suspect(Address member) { if(member == null) return; lock.lock(); try { if(responses.remove(member) != null) cond.signalAll(); } finally { lock.unlock(); } } public boolean hasAllResponses() { lock.lock(); try { for(Map.Entry entry: responses.entrySet()) { if(entry.getValue() == null) return false; } return true; } finally { lock.unlock(); } } public Map getResults() { return Collections.unmodifiableMap(responses); } public int size() { lock.lock(); try { return responses.size(); } finally { lock.unlock(); } } /** * Waits until all responses have been received, or until a timeout has elapsed. * @param timeout Number of milliseconds to wait max. This value needs to be greater than 0, or else * it will be adjusted to 2000 * @return boolean True if all responses have been received within timeout ms, else false (e.g. if interrupted) */ public boolean waitForAllResponses(long timeout) { if(timeout <= 0) timeout=2000L; long end_time=System.currentTimeMillis() + timeout; long wait_time; lock.lock(); try { while(!hasAllResponses()) { wait_time=end_time - System.currentTimeMillis(); if(wait_time <= 0) return false; try { cond.await(wait_time, TimeUnit.MILLISECONDS); } catch(InterruptedException e) { Thread.currentThread().interrupt(); // set interrupt flag again return false; } } return true; } finally { lock.unlock(); } } public void reset() { reset((Collection
                )null); } public void reset(Collection
                members) { lock.lock(); try { responses.clear(); if(members != null) { for(Address mbr: members) responses.put(mbr, null); } cond.signalAll(); } finally { lock.unlock(); } } public void reset(Address ... members) { lock.lock(); try { responses.clear(); if(members != null) { for(Address mbr: members) responses.put(mbr, null); } cond.signalAll(); } finally { lock.unlock(); } } public String toString() { StringBuilder sb=new StringBuilder(); sb.append(responses).append(", complete=").append(hasAllResponses()); return sb.toString(); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/util/RetransmitTable.java0000644000175000017500000003476611647260573026566 0ustar moellermoellerpackage org.jgroups.util; import org.jgroups.Message; import org.jgroups.logging.Log; import org.jgroups.logging.LogFactory; import java.util.LinkedList; import java.util.List; /** * A store for messages to be retransmitted or delivered. Used on sender and receiver side, as a replacement for * HashMap. RetransmitTable should use less memory than HashMap, as HashMap.Entry has 4 fields, plus arrays for storage. *

                * RetransmitTable maintains a matrix (an array of arrays) of messages. Messages are stored in the matrix by mapping * their seqno to an index. E.g. when we have 10 rows of 1000 messages each, and first_seqno is 3000, then a message with * seqno=5600, will be stored in the 3rd row, at index 600. *

                * Rows are removed when all messages in that row have been received.

                * This class in not synchronized; the caller has to make sure access to it is synchronized * @author Bela Ban */ public class RetransmitTable { protected final int num_rows; protected final int msgs_per_row; protected final double resize_factor; protected Message[][] matrix; /** The first seqno, at matrix[0][0] */ protected long offset; protected int size=0; /** The highest seqno purged */ protected long highest_seqno_purged; /** The highest seqno in the table */ protected long highest_seqno; /** Time (in ms) after which a compaction should take place. 0 disables compaction */ protected long max_compaction_time=DEFAULT_MAX_COMPACTION_TIME; /** The time when the last compaction took place. If a {@link #compact()} takes place and sees that the * last compaction is more than max_compaction_time ms ago, a compaction will take place */ protected long last_compaction_timestamp=0; /** By default, rows are only nulled and highest_seqno_purged is adjusted when {@link #purge(long)} is called. * When automatic_purging is enabled (default is off), rows are purged and highest_seqno_purged is adjusted * on {@link #remove(long)} */ protected boolean automatic_purging; protected static final long DEFAULT_MAX_COMPACTION_TIME=2 * 60 * 1000L; protected static final double DEFAULT_RESIZE_FACTOR=1.2; protected static final Log log=LogFactory.getLog(RetransmitTable.class); public RetransmitTable() { this(5, 10000, 0, DEFAULT_RESIZE_FACTOR); } public RetransmitTable(int num_rows, int msgs_per_row, long offset) { this(num_rows, msgs_per_row, offset, DEFAULT_RESIZE_FACTOR); } public RetransmitTable(int num_rows, int msgs_per_row, long offset, double resize_factor) { this(num_rows, msgs_per_row, offset, resize_factor, DEFAULT_MAX_COMPACTION_TIME, false); } public RetransmitTable(int num_rows, int msgs_per_row, long offset, double resize_factor, long max_compaction_time, boolean automatic_purging) { this.num_rows=num_rows; this.msgs_per_row=msgs_per_row; this.resize_factor=resize_factor; this.max_compaction_time=max_compaction_time; this.automatic_purging=automatic_purging; this.offset=this.highest_seqno_purged=this.highest_seqno=offset; matrix=new Message[num_rows][]; if(resize_factor <= 1) throw new IllegalArgumentException("resize_factor needs to be > 1"); } public long getOffset() { return offset; } /** Returns the total capacity in the matrix */ public int capacity() {return matrix.length * msgs_per_row;} /** Returns the numbers of messages in the table */ public int size() {return size;} public boolean isEmpty() {return size <= 0;} public long getHighest() { return highest_seqno; } public long getHighestPurged() { return highest_seqno_purged; } public long getMaxCompactionTime() { return max_compaction_time; } public void setMaxCompactionTime(long max_compaction_time) { this.max_compaction_time=max_compaction_time; } public boolean isAutomaticPurging() { return automatic_purging; } public void setAutomaticPurging(boolean automatic_purging) { this.automatic_purging=automatic_purging; } /** Returns the ratio between size and capacity, as a percentage */ public double getFillFactor() { return size == 0? 0.0 : (int)(((double)size / capacity()) * 100); } /** * Adds a new message to the index computed as a function of seqno * @param seqno * @param msg * @return True if the element at the computed index was null, else false */ public boolean put(long seqno, Message msg) { return putIfAbsent(seqno, msg) == null; } /** * Adds a message if the element at the given index is null. Returns null if no message existed at the given index, * else returns the existing message and doesn't set the element. * @param seqno * @param msg * @return The existing message, or null if there wasn't any */ public Message putIfAbsent(long seqno, Message msg) { int row_index=computeRow(seqno); if(row_index >= matrix.length) { resize(seqno); row_index=computeRow(seqno); } Message[] row=getRow(row_index); int index=computeIndex(seqno); Message existing_msg=row[index]; if(existing_msg == null) { row[index]=msg; size++; if(seqno > highest_seqno) highest_seqno=seqno; return null; } else return existing_msg; } public Message get(long seqno) { int row_index=computeRow(seqno); if(row_index < 0 || row_index >= matrix.length) return null; Message[] row=matrix[row_index]; if(row == null) return null; int index=computeIndex(seqno); return index >= 0? row[index] : null; } public List get(long from, long to) { List retval=null; for(long seqno=from; seqno <= to; seqno++) { Message msg=get(seqno); if(msg != null) { if(retval == null) retval=new LinkedList(); retval.add(msg); } } return retval; } /** Removes the message with seqno from the table, nulls the index */ public Message remove(long seqno) { int row_index=computeRow(seqno); if(row_index < 0 || row_index >= matrix.length) return null; Message[] row=matrix[row_index]; if(row == null) return null; int index=computeIndex(seqno); if(index < 0) return null; Message existing_msg=row[index]; if(existing_msg != null) { row[index]=null; size=Math.max(size-1, 0); // cannot be < 0 (well that would be a bug, but let's have this 2nd line of defense !) if(automatic_purging) { if(seqno > highest_seqno_purged) highest_seqno_purged=seqno; } } return existing_msg; } /** Removes all elements. This method is usually called just before removing a retransmit table, so typically * it is not used anymore after returning */ public void clear() { matrix=new Message[num_rows][]; size=0; offset=highest_seqno_purged=highest_seqno=0; } /** * Removes all messages less than or equal to seqno from the table. Does this by nulling entire rows in the matrix * and nulling all elements < index(seqno) of the first row that cannot be removed * @param seqno */ public void purge(long seqno) { int num_rows_to_remove=(int)(seqno - offset) / msgs_per_row; for(int i=0; i < num_rows_to_remove; i++) // Null all rows which can be fully removed matrix[i]=null; int row_index=computeRow(seqno); if(row_index < 0 || row_index >= matrix.length) return; Message[] row=matrix[row_index]; if(row != null) { int index=computeIndex(seqno); for(int i=0; i <= index; i++) // null all messages up to and including seqno in the given row row[i]=null; } size=computeSize(); if(seqno > highest_seqno_purged) highest_seqno_purged=seqno; // see if compaction should be triggered if(max_compaction_time <= 0) return; long current_time=System.currentTimeMillis(); if(last_compaction_timestamp > 0) { if(current_time - last_compaction_timestamp >= max_compaction_time) { compact(); last_compaction_timestamp=current_time; } } else last_compaction_timestamp=current_time; } /** Moves rows down the matrix, by removing purged rows. If resizing to accommodate seqno is still needed, computes * a new size. Then either moves existing rows down, or copies them into a new array (if resizing took place) */ protected void resize(long seqno) { int num_rows_to_purge=(int)((highest_seqno_purged - offset) / msgs_per_row); int row_index=computeRow(seqno) - num_rows_to_purge; if(row_index < 0) return; int new_size=Math.max(row_index +1, matrix.length); if(new_size > matrix.length) { Message[][] new_matrix=new Message[new_size][]; System.arraycopy(matrix, num_rows_to_purge, new_matrix, 0, matrix.length - num_rows_to_purge); matrix=new_matrix; } else if(num_rows_to_purge > 0) { move(num_rows_to_purge); } offset+=(num_rows_to_purge * msgs_per_row); size=computeSize(); } /** Moves contents of matrix num_rows down. Avoids a System.arraycopy() */ protected void move(int num_rows) { if(num_rows <= 0 || num_rows > matrix.length) return; int target_index=0; for(int i=num_rows; i < matrix.length; i++) matrix[target_index++]=matrix[i]; for(int i=matrix.length - num_rows; i < matrix.length; i++) matrix[i]=null; } /** * Moves the contents of matrix down by the number of purged rows and resizes the matrix accordingly. The * capacity of the matrix should be size * resize_factor */ public void compact() { // This is the range we need to copy into the new matrix (including from and to) int from=computeRow(highest_seqno_purged), to=computeRow(highest_seqno); int range=to - from +1; // e.g. from=3, to=5, new_size has to be [3 .. 5] (=3) int new_size=(int)Math.max(range * resize_factor, range +1); new_size=Math.max(new_size, num_rows); // don't fall below the initial size defined if(new_size < matrix.length) { if(log.isTraceEnabled()) log.trace("compacting matrix from " + matrix.length + " rows to " + new_size + " rows"); Message[][] new_matrix=new Message[new_size][]; System.arraycopy(matrix, from, new_matrix, 0, range); matrix=new_matrix; offset+=from * msgs_per_row; size=computeSize(); } } /** Iterate from highest_seqno_purged to highest_seqno and add up non-null values */ public int computeSize() { int retval=0; int from=computeRow(highest_seqno_purged), to=computeRow(highest_seqno); for(int i=from; i <= to; i++) { Message[] row=matrix[i]; if(row == null) continue; for(int j=0; j < row.length; j++) { if(row[j] != null) retval++; } } return retval; } /** Returns the number of null elements up to 'to' */ public int getNullMessages(long to) { int retval=0; for(long i=offset; i <= to; i++) { int row_index=computeRow(i); if(row_index < 0 || row_index >= matrix.length) continue; Message[] row=matrix[row_index]; if(row != null && row[computeIndex(i)] == null) retval++; } return retval; } public String toString() { StringBuilder sb=new StringBuilder(); sb.append("size=" + size + ", capacity=" + capacity() + ", highest_purged=" + highest_seqno_purged + ", highest=" + highest_seqno); return sb.toString(); } /** Dumps the seqnos in the table as a list */ public String dump() { StringBuilder sb=new StringBuilder(); boolean first=true; for(int i=0; i < matrix.length; i++) { Message[] row=matrix[i]; if(row == null) continue; for(int j=0; j < row.length; j++) { if(row[j] != null) { long seqno=offset + (i * msgs_per_row) + j; if(first) first=false; else sb.append(", "); sb.append(seqno); } } } return sb.toString(); } /** Dumps the non-null in the table in a pseudo graphic way */ public String dumpMatrix() { StringBuilder sb=new StringBuilder(); for(int i=0; i < matrix.length; i++) { Message[] row=matrix[i]; sb.append(i + ": "); if(row == null) { sb.append("\n"); continue; } for(int j=0; j < row.length; j++) { if(row[j] != null) sb.append("* "); else sb.append(" "); } sb.append("\n"); } return sb.toString(); } /** * Returns a row. Creates a new row and inserts it at index if the row at index doesn't exist * @param index * @return A row */ protected Message[] getRow(int index) { Message[] row=matrix[index]; if(row == null) { row=new Message[msgs_per_row]; matrix[index]=row; } return row; } /** Computes and returns the row index for seqno */ protected int computeRow(long seqno) { int diff=(int)(seqno-offset); if(diff < 0) return diff; return diff / msgs_per_row; } /** Computes and returns the index within a row for seqno */ protected int computeIndex(long seqno) { int diff=(int)(seqno - offset); if(diff < 0) return diff; return diff % msgs_per_row; } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/util/RingBuffer.java0000644000175000017500000001130511647260573025477 0ustar moellermoellerpackage org.jgroups.util; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicStampedReference; import java.util.concurrent.locks.LockSupport; import java.util.concurrent.ConcurrentLinkedQueue; /** * * @author Bela Ban */ public class RingBuffer { private final AtomicStampedReference[] queue; private final int capacity; private final AtomicLong next_to_add=new AtomicLong(0); private final AtomicLong next_to_remove=new AtomicLong(0); /*private final AtomicInteger successful_adds=new AtomicInteger(0); private final AtomicInteger successful_removes=new AtomicInteger(0); private final AtomicInteger failed_adds=new AtomicInteger(0); private final AtomicInteger failed_removes=new AtomicInteger(0);*/ // private final ConcurrentLinkedQueue operations=new ConcurrentLinkedQueue(); @SuppressWarnings("unchecked") public RingBuffer(int capacity) { queue=new AtomicStampedReference[capacity]; this.capacity=capacity; for(int i=0; i < capacity; i++) queue[i]=new AtomicStampedReference(null, -1); /*Runtime.getRuntime().addShutdownHook(new Thread() { public void run() { System.out.println("next_to_add=" + next_to_add + ", next_to_remove=" + next_to_remove); System.out.println("successful_adds=" + successful_adds + ", successful_removes=" + successful_removes); System.out.println("failed_adds=" + failed_adds + ", failed_removes=" + failed_removes); // System.out.println("\nOperations"); // for(String op: operations) // System.out.println(op); } });*/ } /** * Adds an elements into the buffer. Blocks if full * @param el */ public void add(T el) { if(el == null) throw new IllegalArgumentException("null element"); int counter=0; long next=next_to_add.getAndIncrement(); int stamp=(int)(next / capacity); while(true) { if(next - next_to_remove.get() < capacity) { int index=(int)(next % capacity); AtomicStampedReference ref=queue[index]; if(ref.compareAndSet(null, el, -1, stamp)) { // operations.add("queue[" + index + "]=" + el + " (next=" + next + ", next_to_remove=" + next_to_remove + ")"); // successful_adds.incrementAndGet(); return; } else { // failed_adds.incrementAndGet(); // System.err.println("add(" + el + ") failed at index " + index + ": next_to_add=" + next_to_add + // ", next=" + next + ", next_to_remove=" + next_to_remove + // ", queue[" + index + "]=" + queue[index].getReference()); } } if(counter >= 5) LockSupport.parkNanos(10); // sleep a little after N attempts -- make configurable else counter++; } } public T remove() { long next=next_to_remove.get(); if(next >= next_to_add.get()) return null; int stamp=(int)(next / capacity); int index=(int)(next % capacity); AtomicStampedReference ref=queue[index]; T retval=ref.getReference(); if(retval != null && ref.compareAndSet(retval, null, stamp, -1)) { // operations.add("queue[" + index + "]: " + retval + " = null (next=" + next + ")"); // successful_removes.incrementAndGet(); next_to_remove.incrementAndGet(); return retval; } // else // failed_removes.incrementAndGet(); return null; } public String dumpNonNullElements() { StringBuilder sb=new StringBuilder(); for(AtomicStampedReference ref: queue) if(ref.getReference() != null) sb.append(ref.getReference() + " "); return sb.toString(); } public int size() { int size=0; int index=(int)(next_to_remove.get() % capacity); for(int i=index; i < index + capacity; i++) if(queue[i % capacity].getReference() != null) size++; return size; } public String toString() { StringBuilder sb=new StringBuilder(); sb.append(size() + " elements"); if(size() < 100) { sb.append(": ").append(dumpNonNullElements()); } return sb.toString(); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/util/Rsp.java0000644000175000017500000000373511647260573024222 0ustar moellermoeller 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; /* flag that represents whether the response was suspected */ boolean suspected; /* The sender of this response */ Address sender; /* the value from the response */ T retval; public Rsp(Address sender) { this.sender=sender; } public Rsp(Address sender, boolean suspected) { this.sender=sender; this.suspected=suspected; } public Rsp(Address sender, T 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 int hashCode() { return sender != null? sender.hashCode() : 0; } public T getValue() { return retval; } public void setValue(T 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 boolean setSuspected(boolean suspected) { boolean changed=!this.suspected && suspected; this.suspected=suspected; if(suspected) received=false; return changed; } public String toString() { return new StringBuilder("sender=").append(sender).append(", retval=").append(retval).append(", received="). append(received).append(", suspected=").append(suspected).toString(); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/util/RspList.java0000644000175000017500000001245511647260573025055 0ustar moellermoeller 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 { public static final RspList EMPTY_RSP_LIST=new RspList(); /** 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); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/util/Seqno.java0000644000175000017500000000424011647260573024533 0ustar moellermoellerpackage org.jgroups.util; import java.util.ArrayList; import java.util.Collection; import java.util.LinkedList; /** * Keeps track of a single message to retransmit * @author Bela Ban */ public class Seqno { final long low; byte flags=0; public static final byte DUMMY = 1 << 0; public static final byte RECEIVED = 1 << 1; public Seqno(long low) { this.low=low; } /** * Only used to compare a long against a range in a TreeSet / TreeMap. Used to find a range given a seqno * @param num * @param dummy */ public Seqno(long num, boolean dummy) { low=num; if(dummy) flags=Util.setFlag(flags, DUMMY); } public boolean isDummy() { return Util.isFlagSet(flags, DUMMY); } public long getLow() { return low; } public boolean contains(long num) { return low == num; } public boolean get(long num) { return low == num && received(); } public void set(long num) { if(low == num) flags=Util.setFlag(flags, RECEIVED); } public void clear(long num) { if(low == num) flags=Util.clearFlags(flags, RECEIVED); } public int getNumberOfReceivedMessages() { return received()? 1 : 0; } public int getNumberOfMissingMessages() { return received()? 0 : 1; } public int size() { return 1; } public Collection getMessagesToRetransmit() { final Collection retval=new ArrayList(1); if(!received()) retval.add(new Range(low, low)); return retval; } public int hashCode() { return (int)low; } public boolean equals(Object obj) { return obj instanceof Seqno && low == ((Seqno)obj).low; } public String toString() { if(isDummy()) return low + " (dummy)"; return Long.toString(low); } public String print() { if(isDummy()) return Long.toString(low); return Long.toString(low); } protected boolean received() { return Util.isFlagSet(flags, RECEIVED); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/util/SeqnoComparator.java0000644000175000017500000000225211647260573026564 0ustar moellermoellerpackage org.jgroups.util; import java.util.Comparator; /** * @author Bela Ban */ public class SeqnoComparator implements Comparator { public int compare(Seqno o1, Seqno o2) { // o1 and o2 are either Seqnos or SeqnoRanges, so we just compare on 'low' if(!o1.isDummy() && !o2.isDummy()) return o1.low == o2.low? 0 : o1.low < o2.low ? -1 : 1; // o2 must be a seqno or SeqnoRange; o1 must be a Seqno if(o1.isDummy()) { if(o2 instanceof SeqnoRange) return _compare2(o1, (SeqnoRange)o2); return _compare(o1, o2); } // o2 is dummy if(o1 instanceof SeqnoRange) return _compare3((SeqnoRange)o1, o2); return _compare(o1, o2); } private static int _compare(Seqno o1, Seqno o2) { return o1.low == o2.low? 0 : o1.low < o2.low? -1 : 1; } private static int _compare2(Seqno o1, SeqnoRange o2) { return o1.low >= o2.low && o1.low <= o2.high? 0 : o1.low < o2.low? -1 : 1; } private static int _compare3(SeqnoRange o1, Seqno o2) { return o2.low >= o1.low && o2.low <= o1.high? 0 : o1.low < o2.low ? -1 : 1; } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/util/SeqnoRange.java0000644000175000017500000000725211647260573025516 0ustar moellermoellerpackage org.jgroups.util; import java.util.ArrayList; import java.util.Collection; /** * Keeps track of a range of messages to be retransmitted. A bit set is used to represent missing messages. * Every non-received message has a corresponding bit set to 0, every received message is 1. * @author Bela Ban */ public class SeqnoRange extends Seqno { final long high; final FixedSizeBitSet bits; public SeqnoRange(long low, long high) { super(low); this.high=high; if(low > high) throw new IllegalArgumentException("low (" + low + ") must be <= high (" + high + ")"); int size=(int)((high - low) + 1); bits=new FixedSizeBitSet(size); // starts out with all bits set to 0 (false) } public long getHigh() { return high; } public boolean contains(long num) { return num >= low && num <= high; } public boolean get(long num) { return bits.get(getIndex((int)num)); } public void set(long num) { bits.set(getIndex((int)num)); } public void set(long ... nums) { if(nums != null) for(long num: nums) set(num); } public void clear(long num) { bits.clear(getIndex((int)num)); } public void clear(long ... nums) { if(nums != null) for(long num: nums) clear(num); } public int getNumberOfReceivedMessages() { return bits.cardinality(); } public int getNumberOfMissingMessages() { return size() - getNumberOfReceivedMessages(); } public int size() { return (int)((high - low) + 1); } public Collection getMessagesToRetransmit() { return getBits(false); } public String toString() { return low + "-" + high; } public String print() { return low + "-" + high + ", set=" + printBits(true) + ", cleared=" + printBits(false); } protected int getIndex(int num) { if(num < low || num > high) throw new IllegalArgumentException(num + " is outside the range " + toString()); return (int)(num - low); } public String printBits(boolean value) { Collection ranges=getBits(value); StringBuilder sb=new StringBuilder(); if(ranges != null && !ranges.isEmpty()) { boolean first=true; for(Range range: ranges) { if(first) first=false; else sb.append(", "); if(range.low == range.high) sb.append(range.low); else sb.append(range.low).append("-").append(range.high); } } return sb.toString(); } /** * Returns ranges of all bit set to value * @param value If true, returns all bits set to 1, else 0 * @return */ public Collection getBits(boolean value) { int index=0; int start_range=0, end_range=0; int size=(int)((high - low) + 1); final Collection retval=new ArrayList(size); while(index < size) { start_range=value? bits.nextSetBit(index) : bits.nextClearBit(index); if(start_range < 0 || start_range >= size) break; end_range=value? bits.nextClearBit(start_range) : bits.nextSetBit(start_range); if(end_range < 0 || end_range >= size) { retval.add(new Range(start_range + low, size-1+low)); break; } retval.add(new Range(start_range + low, end_range-1+low)); index=end_range; } return retval; } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/util/SeqnoTable.java0000644000175000017500000000577611647260573025522 0ustar moellermoellerpackage org.jgroups.util; import org.jgroups.Address; import java.util.Collection; import java.util.HashSet; import java.util.Set; import java.util.concurrent.ConcurrentMap; /** * Maintains the highest received and highest delivered seqno per member * @author Bela Ban */ public class SeqnoTable { private long next_to_receive=0; private final ConcurrentMap map=Util.createConcurrentMap(); 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(); } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/util/ShutdownRejectedExecutionHandler.java0000644000175000017500000000220411647260573032107 0ustar moellermoellerpackage org.jgroups.util; import java.util.concurrent.RejectedExecutionHandler; import java.util.concurrent.ThreadPoolExecutor; /** * 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 * 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()) { handler.rejectedExecution(r, executor); } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/util/SingletonAddress.java0000644000175000017500000000522311647260573026720 0ustar moellermoellerpackage org.jgroups.util; import org.jgroups.Address; import java.io.*; /** * Address with a cluster name. Used by TP.Bundler. * @author Bela Ban */ public class SingletonAddress implements Address { protected final String cluster_name; protected final Address addr; private static final long serialVersionUID=-7139682546627602986L; public SingletonAddress(String cluster_name, Address addr) { this.cluster_name=cluster_name; this.addr=addr; if(cluster_name == null) throw new NullPointerException("cluster_name must not be null"); } public SingletonAddress() { cluster_name=null; addr=null; } public Address getAddress() { return addr; } public String getClusterName() { return cluster_name; } public boolean isMulticastAddress() { return false; } public int size() { return 0; } public void writeExternal(ObjectOutput out) throws IOException { throw new UnsupportedOperationException(); } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { throw new UnsupportedOperationException(); } public void writeTo(DataOutputStream out) throws IOException { throw new UnsupportedOperationException(); } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { throw new UnsupportedOperationException(); } public int hashCode() { int retval=0; if(cluster_name != null) retval+=cluster_name.hashCode(); if(addr != null) retval+=addr.hashCode(); return retval; } public boolean equals(Object obj) { if(!(obj instanceof Address)) throw new IllegalArgumentException("argument is " + obj.getClass()); return compareTo((Address)obj) == 0; } public int compareTo(Address o) { SingletonAddress other=(SingletonAddress)o; if(this == other) return 0; if(other == null) return 1; int rc=cluster_name.compareTo(other.cluster_name); if(rc != 0) return rc; if(addr == null && other.addr == null) return 0; if(addr == null && other.addr != null) return -1; if(addr != null && other.addr == null) return 1; assert addr != null; // this is here to make the (incorrect) 'addr' NPE warning below disappear ! return addr.compareTo(other.addr); } public String toString() { return cluster_name + (addr != null? ":" + addr.toString() : ""); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/util/SocketFactory.java0000644000175000017500000000613011647260573026226 0ustar moellermoellerpackage org.jgroups.util; import java.net.*; import java.io.IOException; import java.util.Map; /** * Factory to create various types of sockets. For socket creation, a service name can be passed as argument: * an implementation could look up a service description (e.g. port) and create the socket, ignoring the passed port and * possibly also the bind address.

                * Ephemeral ports can be created by passing 0 as port, or (if the port is ignored), an implementation could pass in * a special service name (e.g. "EPHEMERAL"), this is implementation dependent.

                * The socket creation methods have the same parameter lists as the socket constructors, e.g. * {@link #createServerSocket(String, int, int)} is the same as {@link java.net.ServerSocket(int,int)}. * @author Bela Ban */ public interface SocketFactory { // todo: should we include NIO socket channels too ? // todo: how should service names be structured ? jgroups.udp.unicast_port ? // todo: should we really include creation of java.net.Sockets ? They don't listen on incoming ports. This would only // be for socket configuration.... Socket createSocket(String service_name) throws IOException; Socket createSocket(String service_name, String host, int port) throws IOException; Socket createSocket(String service_name, InetAddress address, int port) throws IOException; Socket createSocket(String service_name, String host, int port, InetAddress localAddr, int localPort) throws IOException; Socket createSocket(String service_name, InetAddress address, int port, InetAddress localAddr, int localPort) throws IOException; ServerSocket createServerSocket(String service_name) throws IOException; ServerSocket createServerSocket(String service_name, int port) throws IOException; ServerSocket createServerSocket(String service_name, int port, int backlog) throws IOException; ServerSocket createServerSocket(String service_name, int port, int backlog, InetAddress bindAddr) throws IOException; DatagramSocket createDatagramSocket(String service_name) throws SocketException; DatagramSocket createDatagramSocket(String service_name, SocketAddress bindaddr) throws SocketException; DatagramSocket createDatagramSocket(String service_name, int port) throws SocketException; DatagramSocket createDatagramSocket(String service_name, int port, InetAddress laddr) throws SocketException; MulticastSocket createMulticastSocket(String service_name) throws IOException; MulticastSocket createMulticastSocket(String service_name, int port) throws IOException; MulticastSocket createMulticastSocket(String service_name, SocketAddress bindaddr) throws IOException; void close(Socket sock) throws IOException; void close(ServerSocket sock) throws IOException; void close(DatagramSocket sock); /** * Returns all open sockets. This method can be used to list or close all open sockets. * @return A map of open sockets; keys are Sockets, ServerSockets, DatagramSockets or MulticastSockets, values are * the service names. */ Map getSockets(); } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/util/StackType.java0000644000175000017500000000015111647260573025352 0ustar moellermoellerpackage org.jgroups.util; /** * @author Bela Ban */ public enum StackType { IPv4, IPv6, Unknown } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/util/Streamable.java0000644000175000017500000000142311647260573025525 0ustar moellermoellerpackage 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 */ 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; } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/util/ThreadDecorator.java0000644000175000017500000000122311647260573026516 0ustar moellermoellerpackage 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 */ 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); } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/util/ThreadFactory.java0000644000175000017500000000071711647260573026212 0ustar moellermoellerpackage 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); } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/util/ThreadManager.java0000644000175000017500000000115611647260573026153 0ustar moellermoellerpackage 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 */ 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); } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/util/ThreadManagerThreadPoolExecutor.java0000644000175000017500000000426511647260573031660 0ustar moellermoellerpackage org.jgroups.util; import java.util.concurrent.*; import java.util.concurrent.ThreadFactory; /** * ThreadPoolExecutor subclass that implements @{link ThreadManager}. * @author Brian Stansberry */ 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} */ protected void afterExecute(Runnable r, Throwable t) { try { super.afterExecute(r, t); } finally { if(decorator != null) decorator.threadReleased(Thread.currentThread()); } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/util/TimeScheduler.java0000644000175000017500000001541311647260573026207 0ustar moellermoeller package org.jgroups.util; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; /** * Timer-like interface which allows for execution of tasks. Taks can be executed *

                  *
                • one time only *
                • at recurring time intervals. Intervals can be fixed-delay or fixed-rate, * see {@link java.util.concurrent.ScheduledExecutorService} for details *
                • dynamic; at the end of the task execution, a task is asked what the next execution time should be. To do this, * method {@link org.jgroups.util.TimeScheduler.Task#nextInterval()} needs to be implemented. *
                * * @author Bela Ban */ public interface TimeScheduler extends ThreadManager { /** The interface that dynamic tasks * ({@link TimeScheduler#scheduleWithDynamicInterval(org.jgroups.util.TimeScheduler.Task)}) must implement */ public interface Task extends Runnable { /** @return the next scheduled interval. If <= 0 the task will not be re-scheduled */ long nextInterval(); } /** * Executes command with zero required delay. This has effect equivalent to schedule(command, 0, anyUnit). * * @param command the task to execute * @throws java.util.concurrent.RejectedExecutionException at discretion of RejectedExecutionHandler, * if task cannot be accepted for execution because the executor has been shut down. * @throws NullPointerException if command is null */ public void execute(Runnable command); /** * Creates and executes a one-shot action that becomes enabled after the given delay. * * @param command the task to execute * @param delay the time from now to delay execution * @param unit the time unit of the delay parameter * @return a ScheduledFuture representing pending completion of the task and whose get() method * will return null upon completion * @throws java.util.concurrent.RejectedExecutionException if the task cannot be scheduled for execution * @throws NullPointerException if command is null */ public Future schedule(Runnable command, long delay, TimeUnit unit); /** * Creates and executes a periodic action that becomes enabled first after the given initial delay, and * subsequently with the given delay between the termination of one execution and the commencement of the next. * If any execution of the task encounters an exception, subsequent executions are suppressed. * Otherwise, the task will only terminate via cancellation or termination of the executor. * * @param command the task to execute * @param initialDelay the time to delay first execution * @param delay the delay between the termination of one execution and the commencement of the next * @param unit the time unit of the initialDelay and delay parameters * @return a ScheduledFuture representing pending completion of the task, and whose get() * method will throw an exception upon cancellation * @throws java.util.concurrent.RejectedExecutionException if the task cannot be scheduled for execution * @throws NullPointerException if command is null * @throws IllegalArgumentException if delay less than or equal to zero */ public Future scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit); /** * Creates and executes a periodic action that becomes enabled first * after the given initial delay, and subsequently with the given * period; that is executions will commence after * initialDelay then initialDelay+period, then * initialDelay + 2 * period, and so on. * If any execution of the task * encounters an exception, subsequent executions are suppressed. * Otherwise, the task will only terminate via cancellation or * termination of the executor. If any execution of this task * takes longer than its period, then subsequent executions * may start late, but will not concurrently execute. * * @param command the task to execute * @param initialDelay the time to delay first execution * @param period the period between successive executions * @param unit the time unit of the initialDelay and period parameters * @return a ScheduledFuture representing pending completion of * the task, and whose get() method will throw an * exception upon cancellation * @throws java.util.concurrent.RejectedExecutionException if the task cannot be * scheduled for execution * @throws NullPointerException if command is null * @throws IllegalArgumentException if period less than or equal to zero */ public Future scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit); /** * 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 never done until * nextInterval() return a value <= 0 or the task is cancelled. * @param task the task to execute */ public Future scheduleWithDynamicInterval(Task task); public void setThreadFactory(ThreadFactory factory); /** * Returns a list of tasks currently waiting for execution. If there are a lot of tasks, the returned string * should probably only return the number of tasks rather than a full dump. * @return */ public String dumpTimerTasks(); /** * Returns the configured core threads, or -1 if not applicable * @return */ public int getMinThreads(); /** Sets the core pool size. Can be ignored if not applicable */ public void setMinThreads(int size); /** * Returns the configured max threads, or -1 if not applicable * @return */ public int getMaxThreads(); /** Sets the max pool size. Can be ignored if not applicable */ public void setMaxThreads(int size); /** Returns the keep alive time (in ms) of the thread pool, or -1 if not applicable */ public long getKeepAliveTime(); /** Sets the keep alive time (in ms) of the thread pool. Can be ignored if not applicable */ public void setKeepAliveTime(long time); /** * Returns the current threads in the pool, or -1 if not applicable * @return */ public int getCurrentThreads(); /** * Returns the number of tasks currently in the queue. * @return The number of tasks currently in the queue. */ public int size(); /** * Stops the scheduler if running, cancelling all pending tasks */ public void stop(); /** Returns true if stop() has been called, false otherwise */ public boolean isShutdown(); } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/util/TimeScheduler2.java0000644000175000017500000004703011647260573026271 0ustar moellermoeller package org.jgroups.util; import org.jgroups.Global; import org.jgroups.annotations.Experimental; import org.jgroups.annotations.GuardedBy; import org.jgroups.logging.Log; import org.jgroups.logging.LogFactory; import java.util.Collection; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * Implementation of {@link org.jgroups.util.TimeScheduler}. Uses a thread pool and a single thread which waits for the * next task to be executed. When ready, it passes the task to the associated pool to get executed. When multiple tasks * are scheduled to get executed at the same time, they're collected in a queue associated with the task execution * time, and are executed together. * * @author Bela Ban */ @Experimental public class TimeScheduler2 implements TimeScheduler, Runnable { private final ThreadManagerThreadPoolExecutor pool; private final ConcurrentSkipListMap tasks=new ConcurrentSkipListMap(); private Thread runner=null; private final Lock lock=new ReentrantLock(); private final Condition tasks_available=lock.newCondition(); @GuardedBy("lock") private long next_execution_time=0; /** Needed to signal going from 0 tasks to non-zero (we cannot use tasks.isEmpty() here ...) */ protected final AtomicBoolean no_tasks=new AtomicBoolean(true); protected volatile boolean running=false; protected static final Log log=LogFactory.getLog(TimeScheduler2.class); protected ThreadDecorator threadDecorator=null; protected ThreadFactory timer_thread_factory=null; protected static final long SLEEP_TIME=10000; /** * Create a scheduler that executes tasks in dynamically adjustable intervals */ public TimeScheduler2() { pool=new ThreadManagerThreadPoolExecutor(4, 10, 5000, TimeUnit.MILLISECONDS, new LinkedBlockingQueue(5000), Executors.defaultThreadFactory(), new ThreadPoolExecutor.CallerRunsPolicy()); init(); } public TimeScheduler2(ThreadFactory factory, int min_threads, int max_threads, long keep_alive_time, int max_queue_size) { timer_thread_factory=factory; pool=new ThreadManagerThreadPoolExecutor(min_threads, max_threads,keep_alive_time, TimeUnit.MILLISECONDS, new LinkedBlockingQueue(max_queue_size), factory, new ThreadPoolExecutor.CallerRunsPolicy()); init(); } public TimeScheduler2(int corePoolSize) { pool=new ThreadManagerThreadPoolExecutor(corePoolSize, corePoolSize * 2, 5000, TimeUnit.MILLISECONDS, new LinkedBlockingQueue(5000), Executors.defaultThreadFactory(), new ThreadPoolExecutor.CallerRunsPolicy()); init(); } public ThreadDecorator getThreadDecorator() { return threadDecorator; } public void setThreadDecorator(ThreadDecorator threadDecorator) { this.threadDecorator=threadDecorator; pool.setThreadDecorator(threadDecorator); } public void setThreadFactory(ThreadFactory factory) { pool.setThreadFactory(factory); } public int getMinThreads() { return pool.getCorePoolSize(); } public void setMinThreads(int size) { pool.setCorePoolSize(size); } public int getMaxThreads() { return pool.getMaximumPoolSize(); } public void setMaxThreads(int size) { pool.setMaximumPoolSize(size); } public long getKeepAliveTime() { return pool.getKeepAliveTime(TimeUnit.MILLISECONDS); } public void setKeepAliveTime(long time) { pool.setKeepAliveTime(time, TimeUnit.MILLISECONDS); } public int getCurrentThreads() { return pool.getPoolSize(); } public int getQueueSize() { return pool.getQueue().size(); } public String dumpTimerTasks() { StringBuilder sb=new StringBuilder(); for(Entry entry: tasks.values()) { sb.append(entry.dump()).append("\n"); } return sb.toString(); } public void execute(Runnable task) { schedule(task, 0, TimeUnit.MILLISECONDS); } public Future schedule(Runnable work, long delay, TimeUnit unit) { if(work == null) return null; Future retval=null; long key=System.currentTimeMillis() + TimeUnit.MILLISECONDS.convert(delay, unit); // execution time Entry task=new Entry(work); while(!isShutdown()) { Entry existing=tasks.putIfAbsent(key, task); if(existing == null) { retval=task.getFuture(); break; // break out of the while loop } if((retval=existing.add(work)) != null) break; } if(key < next_execution_time || no_tasks.compareAndSet(true, false)) { if(key >= next_execution_time) key=0L; taskReady(key); } return retval; } public Future scheduleWithFixedDelay(Runnable task, long initial_delay, long delay, TimeUnit unit) { if(task == null) throw new NullPointerException(); if (isShutdown()) return null; RecurringTask wrapper=new FixedIntervalTask(task, delay); wrapper.doSchedule(initial_delay); return wrapper; } public Future scheduleAtFixedRate(Runnable task, long initial_delay, long delay, TimeUnit unit) { if(task == null) throw new NullPointerException(); if (isShutdown()) return null; RecurringTask wrapper=new FixedRateTask(task, delay); wrapper.doSchedule(initial_delay); return wrapper; } /** * Schedule a task for execution at varying intervals. After execution, the task will get rescheduled after * {@link org.jgroups.util.TimeScheduler2.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 * 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* */ public Future scheduleWithDynamicInterval(Task task) { if(task == null) throw new NullPointerException(); if (isShutdown()) return null; RecurringTask task_wrapper=new DynamicIntervalTask(task); task_wrapper.doSchedule(); // calls schedule() in ScheduledThreadPoolExecutor return task_wrapper; } /** * Returns the number of tasks currently in the timer * @return The number of tasks currently in the timer */ public int size() { int retval=0; Collection values=tasks.values(); for(Entry entry: values) retval+=entry.size(); return retval; } public String toString() { return getClass().getSimpleName(); } /** * Stops the timer, cancelling all tasks * * @throws InterruptedException if interrupted while waiting for thread to return */ public void stop() { stopRunner(); java.util.List remaining_tasks=pool.shutdownNow(); for(Runnable task: remaining_tasks) { if(task instanceof Future) { Future future=(Future)task; future.cancel(true); } } pool.getQueue().clear(); try { pool.awaitTermination(Global.THREADPOOL_SHUTDOWN_WAIT_TIME, TimeUnit.MILLISECONDS); } catch(InterruptedException e) { } for(Entry entry: tasks.values()) entry.cancel(); tasks.clear(); } public boolean isShutdown() { return pool.isShutdown(); } public void run() { while(running) { try { _run(); } catch(Throwable t) { log.error("failed executing tasks(s)", t); } } } protected void _run() { ConcurrentNavigableMap head_map; // head_map = entries which are <= curr time (ready to be executed) if(!(head_map=tasks.headMap(System.currentTimeMillis(), true)).isEmpty()) { final List keys=new LinkedList(); for(Map.Entry entry: head_map.entrySet()) { final Long key=entry.getKey(); final Entry val=entry.getValue(); pool.execute(new Runnable() { public void run() { val.execute(); } }); keys.add(key); } tasks.keySet().removeAll(keys); } if(tasks.isEmpty()) { no_tasks.compareAndSet(false, true); waitFor(); // sleeps until time elapses, or a task with a lower execution time is added } else waitUntilNextExecution(); // waits until next execution, or a task with a lower execution time is added } protected void init() { if(threadDecorator != null) pool.setThreadDecorator(threadDecorator); // pool.allowCoreThreadTimeOut(true); startRunner(); } /** * Sleeps until the next task in line is ready to be executed */ protected void waitUntilNextExecution() { lock.lock(); try { if(!running) return; next_execution_time=tasks.firstKey(); long sleep_time=next_execution_time - System.currentTimeMillis(); tasks_available.await(sleep_time, TimeUnit.MILLISECONDS); } catch(InterruptedException e) { } finally { lock.unlock(); } } protected void waitFor() { lock.lock(); try { if(!running) return; tasks_available.await(SLEEP_TIME, TimeUnit.MILLISECONDS); } catch(InterruptedException e) { } finally { lock.unlock(); } } /** * Signals that a task with a lower execution time than next_execution_time is ready */ protected void taskReady(long trigger_time) { lock.lock(); try { if(trigger_time > 0) next_execution_time=trigger_time; tasks_available.signal(); } finally { lock.unlock(); } } protected void startRunner() { running=true; runner=timer_thread_factory != null? timer_thread_factory.newThread(this, "Timer runner") : new Thread(this, "Timer runner"); runner.start(); } protected void stopRunner() { lock.lock(); try { running=false; tasks_available.signal(); } finally { lock.unlock(); } } private static class Entry { private final MyTask task; // the task (wrapper) to execute private MyTask last; // points to the last task private final Lock lock=new ReentrantLock(); @GuardedBy("lock") private boolean completed=false; // set to true when the task has been executed public Entry(Runnable task) { last=this.task=new MyTask(task); } Future getFuture() { return task; } Future add(Runnable task) { lock.lock(); try { if(completed) return null; MyTask retval=new MyTask(task); last.next=retval; last=last.next; return retval; } finally { lock.unlock(); } } void execute() { lock.lock(); try { if(completed) return; completed=true; for(MyTask tmp=task; tmp != null; tmp=tmp.next) { if(!(tmp.isCancelled() || tmp.isDone())) { try { tmp.run(); } catch(Throwable t) { log.error("task execution failed", t); } finally { tmp.done=true; } } } } finally { lock.unlock(); } } void cancel() { lock.lock(); try { if(completed) return; for(MyTask tmp=task; tmp != null; tmp=tmp.next) tmp.cancel(true); } finally { lock.unlock(); } } int size() { int retval=1; for(MyTask tmp=task.next; tmp != null; tmp=tmp.next) retval++; return retval; } public String toString() { return size() + " tasks"; } public String dump() { StringBuilder sb=new StringBuilder(); boolean first=true; for(MyTask tmp=task; tmp != null; tmp=tmp.next) { if(!first) sb.append(", "); else first=false; sb.append(tmp); } return sb.toString(); } } /** * Simple task wrapper, always executed by at most 1 thread. */ protected static class MyTask implements Future, Runnable { protected final Runnable task; protected volatile boolean cancelled=false; protected volatile boolean done=false; protected MyTask next; public MyTask(Runnable task) { this.task=task; } public boolean cancel(boolean mayInterruptIfRunning) { boolean retval=!isDone(); cancelled=true; return retval; } public boolean isCancelled() { return cancelled; } public boolean isDone() { return done || cancelled; } public Object get() throws InterruptedException, ExecutionException { return null; } public Object get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { return null; } public void run() { if(isDone()) return; try { task.run(); } catch(Throwable t) { log.error("failed executing task " + task, t); } finally { done=true; } } public String toString() { return task.toString(); } } /** * Task which executes multiple times. An instance of this class wraps the real task and intercepts run(): when * called, it forwards the call to task.run() and then schedules another execution (until cancelled). The * {@link #nextInterval()} method determines the time to wait until the next execution. * @param */ private abstract class RecurringTask implements Runnable, Future { protected final Runnable task; protected volatile Future future; // cannot be null ! protected volatile boolean cancelled=false; public RecurringTask(Runnable task) { this.task=task; } /** * The time to wait until the next execution * @return Number of milliseconds to wait until the next execution is scheduled */ protected abstract long nextInterval(); protected boolean rescheduleOnZeroDelay() {return false;} public void doSchedule() { long next_interval=nextInterval(); if(next_interval <= 0 && !rescheduleOnZeroDelay()) { if(log.isTraceEnabled()) log.trace("task will not get rescheduled as interval is " + next_interval); return; } future=schedule(this, next_interval, TimeUnit.MILLISECONDS); if(cancelled) future.cancel(true); } public void doSchedule(long next_interval) { future=schedule(this, next_interval, TimeUnit.MILLISECONDS); if(cancelled) future.cancel(true); } public void run() { if(cancelled) { if(future != null) future.cancel(true); return; } try { task.run(); } catch(Throwable t) { log.error("failed running task " + task, t); } if(!cancelled) doSchedule(); } public boolean cancel(boolean mayInterruptIfRunning) { boolean retval=!isDone(); cancelled=true; if(future != null) future.cancel(mayInterruptIfRunning); return retval; } public boolean isCancelled() { return cancelled; } public boolean isDone() { return cancelled || (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; } public String toString() { StringBuilder sb=new StringBuilder(); sb.append(getClass().getSimpleName() + ": task=" + task + ", cancelled=" + isCancelled()); return sb.toString(); } } private class FixedIntervalTask extends RecurringTask { final long interval; public FixedIntervalTask(Runnable task, long interval) { super(task); this.interval=interval; } protected long nextInterval() { return interval; } } private class FixedRateTask extends RecurringTask { final long interval; final long first_execution; int num_executions=0; public FixedRateTask(Runnable task, long interval) { super(task); this.interval=interval; this.first_execution=System.currentTimeMillis(); } protected long nextInterval() { long target_time=first_execution + (interval * ++num_executions); return target_time - System.currentTimeMillis(); } protected boolean rescheduleOnZeroDelay() {return true;} } private class DynamicIntervalTask extends RecurringTask { public DynamicIntervalTask(Task task) { super(task); } protected long nextInterval() { if(task instanceof Task) return ((Task)task).nextInterval(); return 0; } } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/util/TopologyUUID.java0000644000175000017500000001140011647260573025745 0ustar moellermoellerpackage org.jgroups.util; import org.jgroups.Global; import java.io.*; import java.security.SecureRandom; /** * Subclass of {@link org.jgroups.util.UUID} which adds 3 strings (siteId, rackId and machineId)as payload. * An instance of this can be fed to {@link org.jgroups.JChannel#setAddressGenerator(org.jgroups.stack.AddressGenerator)}, * with the address generator creating TopologyUUIDs.

                * Mainly used by TopologyAwareConsistentHash in Infinispan (www.infinispan.org). * @author Bela Ban */ public class TopologyUUID extends UUID { private static final long serialVersionUID = 6057688946243812544L; protected String site_id; protected String rack_id; protected String machine_id; public TopologyUUID() { } protected TopologyUUID(byte[] data, String site_id, String rack_id, String machine_id) { super(data); this.site_id=site_id; this.rack_id=rack_id; this.machine_id=machine_id; } public static TopologyUUID randomUUID(String site_id, String rack_id, String machine_id) { return new TopologyUUID(generateRandomBytes(), site_id, rack_id, machine_id); } public static TopologyUUID randomUUID(String logical_name, String site_id, String rack_id, String machine_id) { TopologyUUID retval=new TopologyUUID(generateRandomBytes(), site_id, rack_id, machine_id); UUID.add(retval, logical_name); return retval; } public String getSiteId() { return site_id; } public void setSiteId(String site_id) { this.site_id=site_id; } public String getRackId() { return rack_id; } public void setRackId(String rack_id) { this.rack_id=rack_id; } public String getMachineId() { return machine_id; } public void setMachineId(String machine_id) { this.machine_id=machine_id; } public boolean isSameSite(TopologyUUID addr) { return addr != null && ((site_id != null && site_id.equals(addr.getSiteId())) || (site_id == null && addr.getSiteId() == null)); } public boolean isSameRack(TopologyUUID addr) { return addr != null && ((rack_id != null && rack_id.equals(addr.getRackId())) || (rack_id == null && addr.getRackId() == null)); } public boolean isSameMachine(TopologyUUID addr) { return addr != null && ((machine_id != null && machine_id.equals(addr.getMachineId())) || (machine_id == null && addr.getMachineId() == null)); } public int size() { int retval=super.size() + 3 * Global.BYTE_SIZE; if(site_id != null) retval+= site_id.length() +2; if(rack_id != null) retval+=rack_id.length() +2; if(machine_id != null) retval+=machine_id.length() +2; return retval; } public void writeTo(DataOutputStream out) throws IOException { super.writeTo(out); Util.writeString(site_id, out); Util.writeString(rack_id, out); Util.writeString(machine_id, out); } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { super.readFrom(in); site_id=Util.readString(in); rack_id=Util.readString(in); machine_id=Util.readString(in); } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { super.readExternal(in); site_id=(String)in.readObject(); rack_id=(String)in.readObject(); machine_id=(String)in.readObject(); } public void writeExternal(ObjectOutput out) throws IOException { super.writeExternal(out); out.writeObject(site_id); out.writeObject(rack_id); out.writeObject(machine_id); } public String toString() { if(print_uuids) return toStringLong() + (site_id == null? "" : "(" + site_id + ")"); return super.toString() + (site_id == null? "" : "(" + site_id + ")"); } public String toStringDetailed() { if(print_uuids) return toStringLong() + "(" + printDetails() + ")"; return super.toString() + "(" + printDetails() + ")"; } protected static byte[] generateRandomBytes() { SecureRandom ng=numberGenerator; if(ng == null) numberGenerator=ng=new SecureRandom(); byte[] randomBytes=new byte[16]; ng.nextBytes(randomBytes); return randomBytes; } protected String printDetails() { StringBuilder sb=new StringBuilder(); if(site_id != null) sb.append(site_id); sb.append(":"); if(rack_id != null) sb.append(rack_id); sb.append(":"); if(machine_id != null) sb.append(machine_id); return sb.toString(); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/util/Triple.java0000644000175000017500000000164311647260573024711 0ustar moellermoellerpackage 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 */ 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; } }libjgroups-java-2.12.2.Final.orig/src/org/jgroups/util/Tuple.java0000644000175000017500000000134211647260573024537 0ustar moellermoellerpackage 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 */ 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; } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/util/UUID.java0000644000175000017500000002522411647260573024221 0ustar moellermoellerpackage org.jgroups.util; import org.jgroups.Address; import org.jgroups.Global; import org.jgroups.blocks.LazyRemovalCache; import java.io.*; import java.security.SecureRandom; import java.util.Collection; import java.util.Map; /** Logical address which is unique over space and time. *
                * Copied from java.util.UUID, but unneeded fields from the latter have been removed. UUIDs needs to * have a small memory footprint. * @author Bela Ban */ public class UUID implements Address, Streamable, Comparable

                { protected long mostSigBits; protected long leastSigBits; protected byte[] additional_data; /** The random number generator used by this class to create random based UUIDs */ protected static volatile SecureRandom numberGenerator=null; /** Keeps track of associations between logical addresses (UUIDs) and logical names */ protected static LazyRemovalCache cache; private static final long serialVersionUID=3972962439975931228L; protected static boolean print_uuids=false; protected static final int SIZE=Global.LONG_SIZE * 2 + Global.BYTE_SIZE; protected static final LazyRemovalCache.Printable print_function=new LazyRemovalCache.Printable() { public java.lang.String print(Address key, String val) { return val + ": " + (key instanceof UUID? ((UUID)key).toStringLong() : key) + "\n"; } }; static { String tmp; int max_elements=500; long max_age=5000L; try { tmp=Util.getProperty(new String[]{Global.UUID_CACHE_MAX_ELEMENTS}, null, null, false, "500"); if(tmp != null) max_elements=Integer.valueOf(tmp); } catch(Throwable t) { } try { tmp=Util.getProperty(new String[]{Global.UUID_CACHE_MAX_AGE}, null, null, false, "5000"); if(tmp != null) max_age=Long.valueOf(tmp); } catch(Throwable t) { } cache=new LazyRemovalCache(max_elements, max_age); /* Trying to get value of jgroups.print_uuids. PropertyPermission not granted if * running in an untrusted environment with JNLP */ try { tmp=Util.getProperty(new String[]{Global.PRINT_UUIDS}, null, null, false, "false"); print_uuids=Boolean.valueOf(tmp).booleanValue(); } catch (SecurityException ex){ } } public UUID() { } public UUID(long mostSigBits, long leastSigBits) { this.mostSigBits = mostSigBits; this.leastSigBits = leastSigBits; } /** Private constructor which uses a byte array to construct the new UUID */ protected UUID(byte[] data) { long msb = 0; long lsb = 0; if(data.length != 16) throw new RuntimeException("UUID needs a 16-byte array"); for (int i=0; i<8; i++) msb = (msb << 8) | (data[i] & 0xff); for (int i=8; i<16; i++) lsb = (lsb << 8) | (data[i] & 0xff); this.mostSigBits = msb; this.leastSigBits = lsb; } public static void add(Address uuid, String logical_name) { cache.add(uuid, logical_name); // overwrite existing entry } public static void add(Map map) { if(map == null) return; for(Map.Entry entry: map.entrySet()) add(entry.getKey(), entry.getValue()); } public static String get(Address logical_addr) { return cache.get(logical_addr); } /** Returns a copy of the cache's contents */ public static Map getContents() { return cache.contents(); } public static void remove(Address addr) { cache.remove(addr); } public static void removeAll(Collection
                mbrs) { cache.removeAll(mbrs); } public static void retainAll(Collection
                logical_addrs) { cache.retainAll(logical_addrs); } public static String printCache() { return cache.printCache(print_function); } /** * Returns the additional_data. * @return byte[] * @since 2.8 * @deprecated Will be removed in 3.0. This was only added to be backwards compatible with 2.7 */ public final byte[] getAdditionalData() { return additional_data; } /** * Sets the additional_data. * @param additional_data The additional_data to set * @since 2.8 * @deprecated Will be removed in 3.0. This was only added to be backwards compatible with 2.7 */ public final void setAdditionalData(byte[] additional_data) { this.additional_data=additional_data; } /** * Static factory to retrieve a type 4 (pseudo randomly generated) UUID. * The {@code UUID} is generated using a cryptographically strong pseudo * random number generator. * @return A randomly generated {@code UUID} */ public static UUID randomUUID() { SecureRandom ng=numberGenerator; if(ng == null) numberGenerator=ng=new SecureRandom(); byte[] randomBytes=new byte[16]; ng.nextBytes(randomBytes); return new UUID(randomBytes); } public long getLeastSignificantBits() { return leastSigBits; } /** * Returns the most significant 64 bits of this UUID's 128 bit value. * @return The most significant 64 bits of this UUID's 128 bit value */ public long getMostSignificantBits() { return mostSigBits; } public String toString() { if(print_uuids) return toStringLong(); String val=cache.get(this); return val != null? val : toStringLong(); } /** * Returns a {@code String} object representing this {@code UUID}. * *

                The UUID string representation is as described by this BNF: *

                     * {@code
                     * UUID                   =  "-"  "-"
                     *                           "-"
                     *                           "-"
                     *                          
                     * time_low               = 4*
                     * time_mid               = 2*
                     * time_high_and_version  = 2*
                     * variant_and_sequence   = 2*
                     * node                   = 6*
                     * hexOctet               = 
                     * hexDigit               =
                     *       "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
                     *       | "a" | "b" | "c" | "d" | "e" | "f"
                     *       | "A" | "B" | "C" | "D" | "E" | "F"
                     * }
                * * @return A string representation of this {@code UUID} */ public String toStringLong() { return (digits(mostSigBits >> 32, 8) + "-" + digits(mostSigBits >> 16, 4) + "-" + digits(mostSigBits, 4) + "-" + digits(leastSigBits >> 48, 4) + "-" + digits(leastSigBits, 12)); } /** Returns val represented by the specified number of hex digits. */ protected static String digits(long val, int digits) { long hi = 1L << (digits * 4); return Long.toHexString(hi | (val & (hi - 1))).substring(1); } /** * Returns a hash code for this {@code UUID}. * @return A hash code value for this {@code UUID} */ public int hashCode() { return (int)((mostSigBits >> 32) ^ mostSigBits ^ (leastSigBits >> 32) ^ leastSigBits); } /** * Compares this object to the specified object. The result is {@code * true} if and only if the argument is not {@code null}, is a {@code UUID} * object, has the same variant, and contains the same value, bit for bit, * as this {@code UUID}. * @param obj The object to be compared * @return {@code true} if the objects are the same; {@code false} otherwise */ public boolean equals(Object obj) { if (!(obj instanceof UUID)) return false; UUID id = (UUID)obj; return this == id || (mostSigBits == id.mostSigBits && leastSigBits == id.leastSigBits); } /** * Compares this UUID with the specified UUID. *

                The first of two UUIDs is greater than the second if the most * significant field in which the UUIDs differ is greater for the first UUID. * @param other {@code UUID} to which this {@code UUID} is to be compared * @return -1, 0 or 1 as this {@code UUID} is less than, equal to, or greater than {@code val} */ public int compareTo(Address other) { UUID val=(UUID)other; if(this == val) return 0; return (this.mostSigBits < val.mostSigBits ? -1 : (this.mostSigBits > val.mostSigBits ? 1 : (this.leastSigBits < val.leastSigBits ? -1 : (this.leastSigBits > val.leastSigBits ? 1 : 0)))); } public void writeTo(DataOutputStream out) throws IOException { out.writeLong(leastSigBits); out.writeLong(mostSigBits); 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, IllegalAccessException, InstantiationException { leastSigBits=in.readLong(); mostSigBits=in.readLong(); if(in.readBoolean() == false) return; int len=in.readUnsignedShort(); if(len > 0) { additional_data=new byte[len]; in.readFully(additional_data, 0, additional_data.length); } } public boolean isMulticastAddress() { return false; } public int size() { int retval=SIZE; if(additional_data != null) retval+=additional_data.length + Global.SHORT_SIZE; return retval; } public Object clone() throws CloneNotSupportedException { UUID ret=new UUID(mostSigBits, leastSigBits); 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; } public void writeExternal(ObjectOutput out) throws IOException { out.writeLong(leastSigBits); out.writeLong(mostSigBits); } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { leastSigBits=in.readLong(); mostSigBits=in.readLong(); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/util/UnmodifiableVector.java0000644000175000017500000001472411647260573027237 0ustar moellermoellerpackage org.jgroups.util; import java.util.*; /** * Vector which cannot be modified * @author Bela Ban */ 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(); } }; } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/util/Util.java0000644000175000017500000041761711647260573024403 0ustar moellermoellerpackage org.jgroups.util; import org.jgroups.*; import org.jgroups.auth.AuthToken; import org.jgroups.blocks.Connection; import org.jgroups.conf.ClassConfigurator; import org.jgroups.jmx.JmxConfigurator; import org.jgroups.logging.Log; import org.jgroups.logging.LogFactory; import org.jgroups.protocols.*; import org.jgroups.protocols.pbcast.FLUSH; import org.jgroups.protocols.pbcast.GMS; import org.jgroups.stack.IpAddress; import org.jgroups.stack.ProtocolStack; import javax.management.MBeanServer; import javax.management.MBeanServerFactory; import java.io.*; import java.lang.annotation.Annotation; import java.lang.management.ManagementFactory; import java.lang.management.ThreadInfo; import java.lang.management.ThreadMXBean; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.net.*; import java.nio.ByteBuffer; import java.nio.channels.WritableByteChannel; import java.security.MessageDigest; import java.text.NumberFormat; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.TimeUnit; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Collection of various utility routines that can not be assigned to other classes. * @author Bela Ban */ public class Util { private static NumberFormat f; private static Map PRIMITIVE_TYPES=new HashMap(15); 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; private static final byte TYPE_BYTEARRAY = 19; // constants public static final int MAX_PORT=65535; // highest port allocatable static boolean resolve_dns=false; private static short COUNTER=1; private static Pattern METHOD_NAME_TO_ATTR_NAME_PATTERN=Pattern.compile("[A-Z]+"); private static Pattern ATTR_NAME_TO_METHOD_NAME_PATTERN=Pattern.compile("_."); protected static int CCHM_INITIAL_CAPACITY=16; protected static float CCHM_LOAD_FACTOR=0.75f; protected static int CCHM_CONCURRENCY_LEVEL=16; /** * 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); final ThreadGroup tgParent = getParent(); if(tgParent != null) { tgParent.uncaughtException(t,e); } } }; public static ThreadGroup getGlobalThreadGroup() { return GLOBAL_GROUP; } public static enum AddressScope {GLOBAL, SITE_LOCAL, LINK_LOCAL, LOOPBACK, NON_LOOPBACK}; private static StackType ip_stack_type=_getIpStackType(); 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.setMinimumFractionDigits(2); f.setMaximumFractionDigits(2); 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)); PRIMITIVE_TYPES.put(byte[].class, new Byte(TYPE_BYTEARRAY)); if(ip_stack_type == StackType.Unknown) ip_stack_type=StackType.IPv6; try { String cchm_initial_capacity=System.getProperty(Global.CCHM_INITIAL_CAPACITY); if(cchm_initial_capacity != null) CCHM_INITIAL_CAPACITY=Integer.valueOf(cchm_initial_capacity); } catch(SecurityException ex) {} try { String cchm_load_factor=System.getProperty(Global.CCHM_LOAD_FACTOR); if(cchm_load_factor != null) CCHM_LOAD_FACTOR=Float.valueOf(cchm_load_factor); } catch(SecurityException ex) {} try { String cchm_concurrency_level=System.getProperty(Global.CCHM_CONCURRENCY_LEVEL); if(cchm_concurrency_level != null) CCHM_CONCURRENCY_LEVEL=Integer.valueOf(cchm_concurrency_level); } catch(SecurityException ex) {} } public static void assertTrue(boolean condition) { assert condition; } public static void assertTrue(String message, boolean condition) { if(message != null) assert condition : message; else assert condition; } public static void assertFalse(boolean condition) { assertFalse(null, condition); } public static void assertFalse(String message, boolean condition) { if(message != null) assert !condition : message; else assert !condition; } public static void assertEquals(String message, Object val1, Object val2) { if(message != null) { assert val1.equals(val2) : message; } else { assert val1.equals(val2); } } public static void assertEquals(Object val1, Object val2) { assertEquals(null, val1, val2); } public static void assertNotNull(String message, Object val) { if(message != null) assert val != null : message; else assert val != null; } public static void assertNotNull(Object val) { assertNotNull(null, val); } public static void assertNull(String message, Object val) { if(message != null) assert val == null : message; else assert val == null; } /** * Blocks until all channels have the same view * @param timeout How long to wait (max in ms) * @param interval Check every interval ms * @param channels The channels which should form the view. The expected view size is channels.length. * Must be non-null */ public static void blockUntilViewsReceived(long timeout, long interval, Channel ... channels) throws TimeoutException { final int expected_size=channels.length; if(interval > timeout) throw new IllegalArgumentException("interval needs to be smaller than timeout"); final long end_time=System.currentTimeMillis() + timeout; while(System.currentTimeMillis() < end_time) { boolean all_ok=true; for(Channel ch: channels) { View view=ch.getView(); if(view == null || view.size() != expected_size) { all_ok=false; break; } } if(all_ok) return; Util.sleep(interval); } throw new TimeoutException(); } public static void addFlush(Channel ch, FLUSH flush) { if(ch == null || flush == null) throw new IllegalArgumentException("ch and flush have to be non-null"); ProtocolStack stack=ch.getProtocolStack(); stack.insertProtocolAtTop(flush); } public static void setScope(Message msg, short scope) { SCOPE.ScopeHeader hdr=SCOPE.ScopeHeader.createMessageHeader(scope); msg.putHeader(Global.SCOPE_ID, hdr); msg.setFlag(Message.SCOPED); } public static short getScope(Message msg) { SCOPE.ScopeHeader hdr=(SCOPE.ScopeHeader)msg.getHeader(Global.SCOPE_ID); return hdr != null? hdr.getScope() : 0; } public static SCOPE.ScopeHeader getScopeHeader(Message msg) { return (SCOPE.ScopeHeader)msg.getHeader(Global.SCOPE_ID); } /** * Utility method. If the dest address is IPv6, convert scoped link-local addrs into unscoped ones * @param sock * @param dest * @param sock_conn_timeout * @throws IOException */ public static void connect(Socket sock, SocketAddress dest, int sock_conn_timeout) throws IOException { if(dest instanceof InetSocketAddress) { InetAddress addr=((InetSocketAddress)dest).getAddress(); if(addr instanceof Inet6Address) { Inet6Address tmp=(Inet6Address)addr; if(tmp.getScopeId() != 0) { dest=new InetSocketAddress(InetAddress.getByAddress(tmp.getAddress()), ((InetSocketAddress)dest).getPort()); } } } sock.connect(dest, sock_conn_timeout); } 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(ServerSocket 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); } } public static void close(Connection conn) { if(conn != null) { try {conn.close();} catch(Throwable t) {} } } /** Drops messages to/from other members and then closes the channel. Note that this member won't get excluded from * the view until failure detection has kicked in and the new coord installed the new view */ public static void shutdown(Channel ch) throws Exception { DISCARD discard=new DISCARD(); discard.setLocalAddress(ch.getAddress()); discard.setDiscardAll(true); ProtocolStack stack=ch.getProtocolStack(); TP transport=stack.getTransport(); stack.insertProtocol(discard, ProtocolStack.ABOVE, transport.getClass()); //abruptly shutdown FD_SOCK just as in real life when member gets killed non gracefully FD_SOCK fd = (FD_SOCK) ch.getProtocolStack().findProtocol("FD_SOCK"); if(fd != null) fd.stopServerSocket(false); View view=ch.getView(); if (view != null) { ViewId vid = view.getViewId(); List

                members = Arrays.asList(ch.getAddress()); ViewId new_vid = new ViewId(ch.getAddress(), vid.getId() + 1); View new_view = new View(new_vid, members); // inject view in which the shut down member is the only element GMS gms = (GMS) stack.findProtocol(GMS.class); gms.installView(new_view); } Util.close(ch); } public static byte setFlag(byte bits, byte flag) { return bits |= flag; } public static boolean isFlagSet(byte bits, byte flag) { return (bits & flag) == flag; } public static byte clearFlags(byte bits, byte flag) { return bits &= ~flag; } /** * Creates an object from a byte buffer */ public static Object objectFromByteBuffer(byte[] buffer) throws Exception { if(buffer == null) return null; return objectFromByteBuffer(buffer, 0, buffer.length); } public static Object objectFromByteBuffer(byte[] buffer, int offset, int length) throws Exception { if(buffer == null) return null; Object retval=null; byte type=buffer[offset]; switch(type) { case TYPE_NULL: return null; case TYPE_STREAMABLE: ByteArrayInputStream in_stream=new ExposedByteArrayInputStream(buffer, offset+1, length-1); InputStream in=new DataInputStream(in_stream); retval=readGenericStreamable((DataInputStream)in); break; case TYPE_SERIALIZABLE: // the object is Externalizable or Serializable in_stream=new ExposedByteArrayInputStream(buffer, offset+1, length-1); in=new ObjectInputStream(in_stream); // changed Nov 29 2004 (bela) try { retval=((ObjectInputStream)in).readObject(); } finally { Util.close(in); } break; case TYPE_BOOLEAN: return ByteBuffer.wrap(buffer, offset + 1, length - 1).get() == 1; case TYPE_BYTE: return ByteBuffer.wrap(buffer, offset + 1, length - 1).get(); case TYPE_CHAR: return ByteBuffer.wrap(buffer, offset + 1, length - 1).getChar(); case TYPE_DOUBLE: return ByteBuffer.wrap(buffer, offset + 1, length - 1).getDouble(); case TYPE_FLOAT: return ByteBuffer.wrap(buffer, offset + 1, length - 1).getFloat(); case TYPE_INT: return ByteBuffer.wrap(buffer, offset + 1, length - 1).getInt(); case TYPE_LONG: return ByteBuffer.wrap(buffer, offset + 1, length - 1).getLong(); case TYPE_SHORT: return ByteBuffer.wrap(buffer, offset + 1, length - 1).getShort(); case TYPE_STRING: byte[] tmp=new byte[length -1]; System.arraycopy(buffer, offset +1, tmp, 0, length -1); return new String(tmp); case TYPE_BYTEARRAY: tmp=new byte[length -1]; System.arraycopy(buffer, offset +1, tmp, 0, length -1); return tmp; default: throw new IllegalArgumentException("type " + type + " is invalid"); } return retval; } /** * Serializes/Streams an object into a byte buffer. * The object has to implement interface Serializable or Externalizable or Streamable. */ public static byte[] objectToByteBuffer(Object obj) throws Exception { if(obj == null) return ByteBuffer.allocate(Global.BYTE_SIZE).put(TYPE_NULL).array(); if(obj instanceof Streamable) { final ExposedByteArrayOutputStream out_stream=new ExposedByteArrayOutputStream(128); final ExposedDataOutputStream out=new ExposedDataOutputStream(out_stream); out_stream.write(TYPE_STREAMABLE); writeGenericStreamable((Streamable)obj, out); return out_stream.toByteArray(); } Byte type=PRIMITIVE_TYPES.get(obj.getClass()); if(type == null) { // will throw an exception if object is not serializable final ExposedByteArrayOutputStream out_stream=new ExposedByteArrayOutputStream(128); out_stream.write(TYPE_SERIALIZABLE); ObjectOutputStream out=new ObjectOutputStream(out_stream); out.writeObject(obj); out.close(); return out_stream.toByteArray(); } switch(type.byteValue()) { case TYPE_BOOLEAN: return ByteBuffer.allocate(Global.BYTE_SIZE * 2).put(TYPE_BOOLEAN) .put(((Boolean)obj).booleanValue()? (byte)1 : (byte)0).array(); case TYPE_BYTE: return ByteBuffer.allocate(Global.BYTE_SIZE *2).put(TYPE_BYTE).put(((Byte)obj).byteValue()).array(); case TYPE_CHAR: return ByteBuffer.allocate(Global.BYTE_SIZE *3).put(TYPE_CHAR).putChar(((Character)obj).charValue()).array(); case TYPE_DOUBLE: return ByteBuffer.allocate(Global.BYTE_SIZE + Global.DOUBLE_SIZE).put(TYPE_DOUBLE) .putDouble(((Double)obj).doubleValue()).array(); case TYPE_FLOAT: return ByteBuffer.allocate(Global.BYTE_SIZE + Global.FLOAT_SIZE).put(TYPE_FLOAT) .putFloat(((Float)obj).floatValue()).array(); case TYPE_INT: return ByteBuffer.allocate(Global.BYTE_SIZE + Global.INT_SIZE).put(TYPE_INT) .putInt(((Integer)obj).intValue()).array(); case TYPE_LONG: return ByteBuffer.allocate(Global.BYTE_SIZE + Global.LONG_SIZE).put(TYPE_LONG) .putLong(((Long)obj).longValue()).array(); case TYPE_SHORT: return ByteBuffer.allocate(Global.BYTE_SIZE + Global.SHORT_SIZE).put(TYPE_SHORT) .putShort(((Short)obj).shortValue()).array(); case TYPE_STRING: String str=(String)obj; byte[] buf=new byte[str.length()]; for(int i=0; i < buf.length; i++) buf[i]=(byte)str.charAt(i); return ByteBuffer.allocate(Global.BYTE_SIZE + buf.length).put(TYPE_STRING).put(buf, 0, buf.length).array(); case TYPE_BYTEARRAY: buf=(byte[])obj; return ByteBuffer.allocate(Global.BYTE_SIZE + buf.length).put(TYPE_BYTEARRAY) .put(buf, 0, buf.length).array(); default: throw new IllegalArgumentException("type " + type + " is invalid"); } } /* public static Buffer objectToBuffer(Object obj) throws Exception { final ExposedByteArrayOutputStream out_stream=new ExposedByteArrayOutputStream(512); if(obj == null) { out_stream.write(TYPE_NULL); out_stream.flush(); return out_stream.getBuffer(); } OutputStream out=null; Byte type; try { if(obj instanceof Streamable) { // use Streamable if we can out_stream.write(TYPE_STREAMABLE); out=new ExposedDataOutputStream(out_stream); writeGenericStreamable((Streamable)obj, (DataOutputStream)out); } else if((type=PRIMITIVE_TYPES.get(obj.getClass())) != null) { out_stream.write(type.byteValue()); out=new ExposedDataOutputStream(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; case TYPE_BYTEARRAY: byte[] buf=(byte[])obj; ((DataOutputStream)out).writeInt(buf.length); out.write(buf, 0, buf.length); 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); } return out_stream.getBuffer(); }*/ public static void objectToStream(Object obj, DataOutputStream out) throws Exception { if(obj == null) { out.write(TYPE_NULL); return; } Byte type; try { if(obj instanceof Streamable) { // use Streamable if we can out.write(TYPE_STREAMABLE); writeGenericStreamable((Streamable)obj, out); } else if((type=PRIMITIVE_TYPES.get(obj.getClass())) != null) { out.write(type.byteValue()); switch(type.byteValue()) { case TYPE_BOOLEAN: out.writeBoolean(((Boolean)obj).booleanValue()); break; case TYPE_BYTE: out.writeByte(((Byte)obj).byteValue()); break; case TYPE_CHAR: out.writeChar(((Character)obj).charValue()); break; case TYPE_DOUBLE: out.writeDouble(((Double)obj).doubleValue()); break; case TYPE_FLOAT: out.writeFloat(((Float)obj).floatValue()); break; case TYPE_INT: out.writeInt(((Integer)obj).intValue()); break; case TYPE_LONG: out.writeLong(((Long)obj).longValue()); break; case TYPE_SHORT: out.writeShort(((Short)obj).shortValue()); break; case TYPE_STRING: String str=(String)obj; if(str.length() > Short.MAX_VALUE) { out.writeBoolean(true); ObjectOutputStream oos=new ObjectOutputStream(out); try { oos.writeObject(str); } finally { oos.close(); } } else { out.writeBoolean(false); out.writeUTF(str); } break; case TYPE_BYTEARRAY: byte[] buf=(byte[])obj; out.writeInt(buf.length); out.write(buf, 0, buf.length); break; default: throw new IllegalArgumentException("type " + type + " is invalid"); } } else { // will throw an exception if object is not serializable out.write(TYPE_SERIALIZABLE); ObjectOutputStream tmp=new ObjectOutputStream(out); tmp.writeObject(obj); } } finally { Util.close(out); } } public static Object objectFromStream(DataInputStream in) throws Exception { if(in == null) return null; Object retval=null; byte b=(byte)in.read(); switch(b) { case TYPE_NULL: return null; case TYPE_STREAMABLE: retval=readGenericStreamable(in); break; case TYPE_SERIALIZABLE: // the object is Externalizable or Serializable ObjectInputStream tmp=new ObjectInputStream(in); retval=tmp.readObject(); break; case TYPE_BOOLEAN: retval=Boolean.valueOf(in.readBoolean()); break; case TYPE_BYTE: retval=Byte.valueOf(in.readByte()); break; case TYPE_CHAR: retval=Character.valueOf(in.readChar()); break; case TYPE_DOUBLE: retval=Double.valueOf(in.readDouble()); break; case TYPE_FLOAT: retval=Float.valueOf(in.readFloat()); break; case TYPE_INT: retval=Integer.valueOf(in.readInt()); break; case TYPE_LONG: retval=Long.valueOf(in.readLong()); break; case TYPE_SHORT: retval=Short.valueOf(in.readShort()); break; case TYPE_STRING: if(in.readBoolean()) { // large string ObjectInputStream ois=new ObjectInputStream(in); try { retval=ois.readObject(); } finally { ois.close(); } } else { retval=in.readUTF(); } break; case TYPE_BYTEARRAY: int len=in.readInt(); byte[] tmpbuf=new byte[len]; in.readFully(tmpbuf, 0, tmpbuf.length); retval=tmpbuf; break; default: throw new IllegalArgumentException("type " + b + " is invalid"); } return retval; } public static Streamable streamableFromByteBuffer(Class cl, byte[] buffer) throws Exception { if(buffer == null) return null; Streamable retval=null; ByteArrayInputStream in_stream=new ExposedByteArrayInputStream(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 ExposedByteArrayInputStream(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 ExposedByteArrayOutputStream(512); DataOutputStream out=new ExposedDataOutputStream(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 ExposedByteArrayOutputStream(512); DataOutputStream out=new ExposedDataOutputStream(out_stream); Util.writeAddresses(c, out); result=out_stream.toByteArray(); out.close(); return result; } 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 writeView(View view, DataOutputStream out) throws IOException { if(view == null) { out.writeBoolean(false); return; } out.writeBoolean(true); out.writeBoolean(view instanceof MergeView); view.writeTo(out); } public static View readView(DataInputStream in) throws IOException, InstantiationException, IllegalAccessException { if(in.readBoolean() == false) return null; boolean isMergeView=in.readBoolean(); View view; if(isMergeView) view=new MergeView(); else view=new View(); view.readFrom(in); return view; } public static void writeViewId(ViewId vid, DataOutputStream out) throws IOException { if(vid == null) { out.writeBoolean(false); return; } out.writeBoolean(true); vid.writeTo(out); } public static ViewId readViewId(DataInputStream in) throws IOException, InstantiationException, IllegalAccessException { if(in.readBoolean() == false) return null; ViewId retval=new ViewId(); retval.readFrom(in); return retval; } public static void writeAddress(Address addr, DataOutputStream out) throws IOException { byte flags=0; boolean streamable_addr=true; if(addr == null) { flags=Util.setFlag(flags, Address.NULL); out.writeByte(flags); return; } Class clazz=addr.getClass(); if(clazz.equals(UUID.class)) { flags=Util.setFlag(flags, Address.UUID_ADDR); } else if(clazz.equals(IpAddress.class)) { flags=Util.setFlag(flags, Address.IP_ADDR); } else { streamable_addr=false; } out.writeByte(flags); if(streamable_addr) addr.writeTo(out); else writeOtherAddress(addr, out); } public static Address readAddress(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { byte flags=in.readByte(); if(Util.isFlagSet(flags, Address.NULL)) return null; Address addr; if(Util.isFlagSet(flags, Address.UUID_ADDR)) { addr=new UUID(); addr.readFrom(in); } else if(Util.isFlagSet(flags, Address.IP_ADDR)) { addr=new IpAddress(); addr.readFrom(in); } else { addr=readOtherAddress(in); } return addr; } public static int size(Address addr) { int retval=Global.BYTE_SIZE; // flags if(addr != null) { if(addr instanceof UUID || addr instanceof IpAddress) retval+=addr.size(); else { retval+=Global.SHORT_SIZE; // magic number retval+=addr.size(); } } return retval; } public static int size(View view) { int retval=Global.BYTE_SIZE; // presence if(view != null) retval+=view.serializedSize() + Global.BYTE_SIZE; // merge view or regular view return retval; } public static int size(ViewId vid) { int retval=Global.BYTE_SIZE; // presence if(vid != null) retval+=vid.serializedSize(); return retval; } private static Address readOtherAddress(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { short magic_number=in.readShort(); Class cl=ClassConfigurator.get(magic_number); if(cl == null) throw new RuntimeException("class for magic number " + magic_number + " not found"); Address addr=(Address)cl.newInstance(); addr.readFrom(in); return addr; } private static void writeOtherAddress(Address addr, DataOutputStream out) throws IOException { short magic_number=ClassConfigurator.getMagicNumber(addr.getClass()); // write the class info if(magic_number == -1) throw new RuntimeException("magic number " + magic_number + " not found"); out.writeShort(magic_number); 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()); for(Address addr: v) { 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=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; } out.write(1); magic_number=ClassConfigurator.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); } 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.get(magic_number); if (clazz==null) { throw new ClassNotFoundException("Class for magic number "+magic_number+" cannot be found."); } } else { classname=in.readUTF(); clazz=ClassConfigurator.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 writeClass(Class classObject, DataOutputStream out) throws IOException { short magic_number=ClassConfigurator.getMagicNumber(classObject); // write the magic number or the class name if(magic_number == -1) { out.writeBoolean(false); out.writeUTF(classObject.getName()); } else { out.writeBoolean(true); out.writeShort(magic_number); } } public static Class readClass(DataInputStream in) throws IOException, ClassNotFoundException { Class clazz; boolean use_magic_number = in.readBoolean(); if(use_magic_number) { short magic_number=in.readShort(); clazz=ClassConfigurator.get(magic_number); if (clazz==null) { throw new ClassNotFoundException("Class for magic number "+magic_number+" cannot be found."); } } else { String classname=in.readUTF(); clazz=ClassConfigurator.get(classname); if (clazz==null) { throw new ClassNotFoundException(classname); } } return clazz; } public static void writeObject(Object obj, DataOutputStream out) throws Exception { if(obj instanceof Streamable) { out.writeInt(-1); writeGenericStreamable((Streamable)obj, out); } else { byte[] buf=objectToByteBuffer(obj); out.writeInt(buf.length); out.write(buf, 0, buf.length); } } public static Object readObject(DataInputStream in) throws Exception { int len=in.readInt(); if(len == -1) return readGenericStreamable(in); byte[] buf=new byte[len]; in.readFully(buf, 0, len); return objectFromByteBuffer(buf); } 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 writeAsciiString(String str, DataOutputStream out) throws IOException { if(str == null) { out.write(-1); return; } int length=str.length(); if(length > Byte.MAX_VALUE) throw new IllegalArgumentException("string is > " + Byte.MAX_VALUE); out.write(length); out.writeBytes(str); } public static String readAsciiString(DataInputStream in) throws IOException { byte length=(byte)in.read(); if(length == -1) return null; byte[] tmp=new byte[length]; in.readFully(tmp, 0, tmp.length); return new String(tmp, 0, tmp.length); } public static String parseString(DataInputStream in) { return parseString(in, false); } public static String parseString(DataInputStream in, boolean break_on_newline) { StringBuilder sb=new StringBuilder(); int ch; // read white space while(true) { try { ch=in.read(); if(ch == -1) { return null; // eof } if(Character.isWhitespace(ch)) { if(break_on_newline && ch == '\n') return null; } else { sb.append((char)ch); break; } } catch(IOException e) { break; } } while(true) { try { ch=in.read(); if(ch == -1) break; if(Character.isWhitespace(ch)) break; else { sb.append((char)ch); } } catch(IOException e) { break; } } return sb.toString(); } public static String readStringFromStdin(String message) throws Exception { System.out.print(message); System.out.flush(); System.in.skip(System.in.available()); BufferedReader reader=new BufferedReader(new InputStreamReader(System.in)); return reader.readLine().trim(); } public static long readLongFromStdin(String message) throws Exception { String tmp=readStringFromStdin(message); return Long.parseLong(tmp); } public static double readDoubleFromStdin(String message) throws Exception { String tmp=readStringFromStdin(message); return Double.parseDouble(tmp); } public static int readIntFromStdin(String message) throws Exception { String tmp=readStringFromStdin(message); return Integer.parseInt(tmp); } public static void writeByteBuffer(byte[] buf, DataOutputStream out) throws IOException { writeByteBuffer(buf, 0, buf.length, out); } public static void writeByteBuffer(byte[] buf, int offset, int length, DataOutputStream out) throws IOException { if(buf != null) { out.write(1); out.writeInt(length); out.write(buf, offset, 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 ExposedDataOutputStream(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 ExposedByteArrayInputStream(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 ExposedDataOutputStream(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 ExposedByteArrayInputStream(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 sameViewId(ViewId one, ViewId two) { return one.getId() == two.getId() && one.getCoordAddress().equals(two.getCoordAddress()); } 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(InterruptedException e) { Thread.currentThread().interrupt(); } } public static void sleep(long timeout, int nanos) { try { Thread.sleep(timeout,nanos); } catch(InterruptedException e) { Thread.currentThread().interrupt(); } } /** * 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(); } } public static int keyPress(String msg) { System.out.println(msg); try { int ret=System.in.read(); System.in.skip(System.in.available()); return ret; } catch(IOException e) { return 0; } } /** 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(Object o: values) { 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(Map.Entry entry: headers.entrySet()) { short id=entry.getKey(); Header value=entry.getValue(); String headerToString=null; if(value instanceof FD.FdHeader) { headerToString=value.toString(); } else if(value instanceof PingHeader) { headerToString=ClassConfigurator.getProtocol(id) + "-"; 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=ClassConfigurator.getProtocol(id) + "-" + (value == null ? "null" : value.toString()); } s+=headerToString; 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(); } /** * MByte nowadays doesn't mean 1024 * 1024 bytes, but 1 million bytes, see http://en.wikipedia.org/wiki/Megabyte * @param bytes * @return */ 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 printTime(long time, TimeUnit unit) { long ns=TimeUnit.NANOSECONDS.convert(time, unit); long us=TimeUnit.MICROSECONDS.convert(time, unit); long ms=TimeUnit.MILLISECONDS.convert(time, unit); long secs=TimeUnit.SECONDS.convert(time, unit); if(secs > 0) return secs + "s"; if(ms > 0) return ms + "ms"; if(us > 0) return us + " us"; return ns + "ns"; } public static String format(double value) { return f.format(value); } public static long readBytesLong(String input) { Tuple tuple=readBytes(input); double num=Double.parseDouble(tuple.getVal1()); return (long)(num * tuple.getVal2()); } public static int readBytesInteger(String input) { Tuple tuple=readBytes(input); double num=Double.parseDouble(tuple.getVal1()); return (int)(num * tuple.getVal2()); } public static double readBytesDouble(String input) { Tuple tuple=readBytes(input); double num=Double.parseDouble(tuple.getVal1()); return num * tuple.getVal2(); } private static Tuple readBytes(String input) { input=input.trim().toLowerCase(); int index=-1; long factor=1; if((index=input.indexOf("k")) != -1) factor=1000; else if((index=input.indexOf("kb")) != -1) factor=1000; else if((index=input.indexOf("m")) != -1) factor=1000000; else if((index=input.indexOf("mb")) != -1) factor=1000000; else if((index=input.indexOf("g")) != -1) factor=1000000000; else if((index=input.indexOf("gb")) != -1) factor=1000000000; String str=index != -1? input.substring(0, index) : input; return new Tuple(str, factor); } 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"; } } public static List split(String input, int separator) { List retval=new ArrayList(); if(input == null) return retval; int index=0, end; while(true) { index=input.indexOf(separator, index); if(index == -1) break; index++; end=input.indexOf(separator, index); if(end == -1) retval.add(input.substring(index)); else retval.add(input.substring(index, end)); } return retval; } /* public static String[] components(String path, String separator) { if(path == null || path.length() == 0) return null; String[] tmp=path.split(separator + "+"); // multiple separators could be present if(tmp == null) return null; if(tmp.length == 0) return null; if(tmp[0].length() == 0) { tmp[0]=separator; if(tmp.length > 1) { String[] retval=new String[tmp.length -1]; retval[0]=tmp[0] + tmp[1]; System.arraycopy(tmp, 2, retval, 1, tmp.length-2); return retval; } return tmp; } return tmp; }*/ public static String[] components(String path, String separator) { if(path == null || path.length() == 0) return null; String[] tmp=path.split(separator + "+"); // multiple separators could be present if(tmp == null) return null; if(tmp.length == 0) return null; if(tmp[0].length() == 0) tmp[0]=separator; return tmp; } /** 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 List computeFragOffsets(int offset, int length, int frag_size) { 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 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(); } public static String printMapWithDelimiter(Map map, String delimiter) { boolean first=true; StringBuilder sb=new StringBuilder(); for(Map.Entry entry: map.entrySet()) { if(first) first=false; else sb.append(delimiter); sb.append(entry.getKey()).append("=").append(entry.getValue()); } return sb.toString(); } 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) { return Arrays.toString(array); } /** 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; } /** * Returns a list of members which left from view one to two * @param one * @param two * @return */ public static List
                leftMembers(View one, View two) { if(one == null || two == null) return null; List
                retval=new ArrayList
                (one.getMembers()); retval.removeAll(two.getMembers()); return retval; } public static List
                leftMembers(Collection
                old_list, Collection
                new_list) { if(old_list == null || new_list == null) return null; List
                retval=new ArrayList
                (old_list); retval.removeAll(new_list); return retval; } public static List
                newMembers(List
                old_list, List
                new_list) { if(old_list == null || new_list == null) return null; List
                retval=new ArrayList
                (new_list); retval.removeAll(old_list); return retval; } /** * 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 boolean containsViewId(Collection views, ViewId vid) { for(View view: views) { ViewId tmp=view.getVid(); if(Util.sameViewId(vid, tmp)) return true; } return false; } /** * Determines the members which take part in a merge. The resulting list consists of all merge coordinators * plus members outside a merge partition, e.g. for views A={B,A,C}, B={B,C} and C={B,C}, the merge coordinator * is B, but the merge participants are B and A. * @param map * @return */ public static Collection
                determineMergeParticipants(Map map) { Set
                coords=new HashSet
                (); Set
                all_addrs=new HashSet
                (); if(map == null) return Collections.emptyList(); for(View view: map.values()) all_addrs.addAll(view.getMembers()); for(View view: map.values()) { Address coord=view.getCreator(); if(coord != null) coords.add(coord); } for(Address coord: coords) { View view=map.get(coord); Collection
                mbrs=view != null? view.getMembers() : null; if(mbrs != null) all_addrs.removeAll(mbrs); } coords.addAll(all_addrs); return coords; } /** * This is the same or a subset of {@link #determineMergeParticipants(java.util.Map)} and contains only members * which are currently sub-partition coordinators. * @param map * @return */ public static Collection
                determineMergeCoords(Map map) { Set
                retval=new HashSet
                (); if(map != null) { for(View view: map.values()) { Address coord=view.getCreator(); if(coord != null) retval.add(coord); } } return retval; } /** * Returns the rank of a member in a given view * @param view The view * @param addr The address of a member * @return A value between 1 and view.size(). The first member has rank 1, the second 2 and so on. If the * member is not found, 0 is returned */ public static int getRank(View view, Address addr) { if(view == null || addr == null) return 0; List
                members=view.getMembers(); for(int i=0; i < members.size(); i++) { Address mbr=members.get(i); if(mbr.equals(addr)) return i+1; } return 0; } 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 the object next to element in list * @param list * @param obj * @param * @return */ public static T pickNext(List list, T obj) { if(list == null || obj == null) return null; Object[] array=list.toArray(); for(int i=0; i < array.length; i++) { T tmp=(T)array[i]; if(tmp != null && tmp.equals(obj)) return (T)array[(i+1) % array.length]; } return null; } /** Returns the next min(N,list.size()) elements after obj */ public static List pickNext(List list, T obj, int num) { List retval=new ArrayList(); if(list == null || list.size() < 2) return retval; int index=list.indexOf(obj); if(index != -1) { for(int i=1; i <= num && i < list.size(); i++) { T tmp=list.get((index +i) % list.size()); if(!retval.contains(tmp)) retval.add(tmp); } } return retval; } public static View createView(Address coord, long id, Address ... members) { Vector
                mbrs=new Vector
                (); mbrs.addAll(Arrays.asList(members)); return new View(coord, id, mbrs); } public static Address createRandomAddress() { return createRandomAddress(generateLocalName()); } public static Address createRandomAddress(String name) { UUID retval=UUID.randomUUID(); UUID.add(retval, name); return retval; } public static Object[][] createTimer() { return new Object[][] { {new DefaultTimeScheduler(5)}, {new TimeScheduler2()}, {new HashedTimingWheel(5)} }; } /** * 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(List
                old_mbrs, List
                new_mbrs) { Vector
                retval=new Vector
                (); if(old_mbrs == null || new_mbrs == null) return retval; for(int i=0; i < old_mbrs.size(); i++) { Address mbr=old_mbrs.get(i); if(!new_mbrs.contains(mbr)) retval.add(mbr); } return retval; } /** * Returns the members which joined between 2 subsequent views * @param old_mbrs * @param new_mbrs * @return */ public static List
                determineNewMembers(List
                old_mbrs, List
                new_mbrs) { if(old_mbrs == null || new_mbrs == null) return new ArrayList
                (); List
                retval=new ArrayList
                (new_mbrs); retval.removeAll(old_mbrs); return retval; } public static String printViews(Collection views) { StringBuilder sb=new StringBuilder(); boolean first=true; for(View view: views) { if(first) first=false; else sb.append(", "); sb.append(view.getVid()); } return sb.toString(); } public static String print(Collection objs) { StringBuilder sb=new StringBuilder(); boolean first=true; for(T obj: objs) { if(first) first=false; else sb.append(", "); sb.append(obj); } return sb.toString(); } public static String print(Map map) { StringBuilder sb=new StringBuilder(); boolean first=true; for(Map.Entry entry: map.entrySet()) { if(first) first=false; else sb.append(", "); sb.append(entry.getKey()).append("=").append(entry.getValue()); } return sb.toString(); } public static String printPingData(List rsps) { StringBuilder sb=new StringBuilder(); if(rsps != null) { int total=rsps.size(); int servers=0, clients=0, coords=0; for(PingData 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); } } 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) { try { ByteArrayOutputStream output=new ExposedByteArrayOutputStream(); DataOutputStream out=new ExposedDataOutputStream(output); inst.writeTo(out); out.flush(); byte[] 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 Field[] getAllDeclaredFields(final Class clazz) { return getAllDeclaredFieldsWithAnnotations(clazz); } public static Field[] getAllDeclaredFieldsWithAnnotations(final Class clazz, Class ... annotations) { List list=new ArrayList(30); for(Class curr=clazz; curr != null; curr=curr.getSuperclass()) { Field[] fields=curr.getDeclaredFields(); if(fields != null) { for(Field field: fields) { if(annotations != null && annotations.length > 0) { for(Class annotation: annotations) { if(field.isAnnotationPresent(annotation)) list.add(field); } } else list.add(field); } } } Field[] retval=new Field[list.size()]; for(int i=0; i < list.size(); i++) retval[i]=list.get(i); return retval; } public static Method[] getAllDeclaredMethods(final Class clazz) { return getAllDeclaredMethodsWithAnnotations(clazz); } public static Method[] getAllDeclaredMethodsWithAnnotations(final Class clazz, Class ... annotations) { List list=new ArrayList(30); for(Class curr=clazz; curr != null; curr=curr.getSuperclass()) { Method[] methods=curr.getDeclaredMethods(); if(methods != null) { for(Method method: methods) { if(annotations != null && annotations.length > 0) { for(Class annotation: annotations) { if(method.isAnnotationPresent(annotation)) list.add(method); } } else list.add(method); } } } Method[] retval=new Method[list.size()]; for(int i=0; i < list.size(); i++) retval[i]=list.get(i); return retval; } public static Field getField(final Class clazz, String field_name) { if(clazz == null || field_name == null) return null; Field field=null; for(Class curr=clazz; curr != null; curr=curr.getSuperclass()) { try { return curr.getDeclaredField(field_name); } catch(NoSuchFieldException e) { } } return field; } 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]=v.elementAt(i).longValue(); return retval; } /** e.g. "bela,jeannette,michelle" --> List{"bela", "jeannette", "michelle"} */ public static List parseCommaDelimitedStrings(String l) { return parseStringList(l, ","); } /** * Input is "daddy[8880],sindhu[8880],camille[5555]. Returns a list of IpAddresses */ public static List parseCommaDelimitedHosts(String hosts, int port_range) throws UnknownHostException { StringTokenizer tok=new StringTokenizer(hosts, ","); String t; IpAddress addr; Set retval=new HashSet(); while(tok.hasMoreTokens()) { 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); } } return Collections.unmodifiableList(new LinkedList(retval)); } /** * Input is "daddy[8880],sindhu[8880],camille[5555]. Return List of * InetSocketAddress */ public static List parseCommaDelimitedHosts2(String hosts, int port_range) throws UnknownHostException { StringTokenizer tok=new StringTokenizer(hosts, ","); String t; InetSocketAddress addr; Set retval=new HashSet(); while(tok.hasMoreTokens()) { 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 InetSocketAddress(host, i); retval.add(addr); } } return Collections.unmodifiableList(new LinkedList(retval)); } 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 String parseString(ByteBuffer buf) { return parseString(buf, true); } public static String parseString(ByteBuffer buf, boolean discard_whitespace) { StringBuilder sb=new StringBuilder(); char ch; // read white space while(buf.remaining() > 0) { ch=(char)buf.get(); if(!Character.isWhitespace(ch)) { buf.position(buf.position() -1); break; } } if(buf.remaining() == 0) return null; while(buf.remaining() > 0) { ch=(char)buf.get(); if(!Character.isWhitespace(ch)) { sb.append(ch); } else break; } // read white space if(discard_whitespace) { while(buf.remaining() > 0) { ch=(char)buf.get(); if(!Character.isWhitespace(ch)) { buf.position(buf.position() -1); break; } } } return sb.toString(); } public static int readNewLine(ByteBuffer buf) { char ch; int num=0; while(buf.remaining() > 0) { ch=(char)buf.get(); num++; if(ch == '\n') break; } return num; } /** * Reads and discards all characters from the input stream until a \r\n or EOF is encountered * @param in * @return */ public static int discardUntilNewLine(InputStream in) { int ch; int num=0; while(true) { try { ch=in.read(); if(ch == -1) break; num++; if(ch == '\n') break; } catch(IOException e) { break; } } return num; } /** * Reads a line of text. A line is considered to be terminated by any one * of a line feed ('\n'), a carriage return ('\r'), or a carriage return * followed immediately by a linefeed. * * @return A String containing the contents of the line, not including * any line-termination characters, or null if the end of the * stream has been reached * * @exception IOException If an I/O error occurs */ public static String readLine(InputStream in) throws IOException { StringBuilder sb=new StringBuilder(35); int ch; while(true) { ch=in.read(); if(ch == -1) return null; if(ch == '\r') { ; } else { if(ch == '\n') break; else { sb.append((char)ch); } } } return sb.toString(); } public static void writeString(ByteBuffer buf, String s) { for(int i=0; i < s.length(); i++) buf.put((byte)s.charAt(i)); } /** * * @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) { if(hostname == null) return null; int index=hostname.indexOf('.'); if(index > 0 && !Character.isDigit(hostname.charAt(0))) return hostname.substring(0, index); else return hostname; } 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); } 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 String generateLocalName() { String retval=null; try { retval=shortName(InetAddress.getLocalHost().getHostName()); } catch(UnknownHostException e) { retval="localhost"; } long counter=Util.random(Short.MAX_VALUE *2); return retval + "-" + counter; } public synchronized static short incrCounter() { short retval=COUNTER++; if(COUNTER >= Short.MAX_VALUE) COUNTER=1; return retval; } public static ConcurrentMap createConcurrentMap(int initial_capacity, float load_factor, int concurrency_level) { return new ConcurrentHashMap(initial_capacity, load_factor, concurrency_level); } public static ConcurrentMap createConcurrentMap(int initial_capacity) { return new ConcurrentHashMap(initial_capacity); } public static ConcurrentMap createConcurrentMap() { return new ConcurrentHashMap(CCHM_INITIAL_CAPACITY, CCHM_LOAD_FACTOR, CCHM_CONCURRENCY_LEVEL); } public static Map createHashMap() { return new HashMap(CCHM_INITIAL_CAPACITY, CCHM_LOAD_FACTOR); } /** Finds first available port starting at start_port and returns server socket */ public static ServerSocket createServerSocket(SocketFactory factory, String service_name, int start_port) { ServerSocket ret=null; while(true) { try { ret=factory.createServerSocket(service_name, start_port); } catch(BindException bind_ex) { start_port++; continue; } catch(IOException io_ex) { } break; } return ret; } public static ServerSocket createServerSocket(SocketFactory factory, String service_name, InetAddress bind_addr, int start_port) { ServerSocket ret=null; while(true) { try { ret=factory.createServerSocket(service_name, start_port, 50, bind_addr); } catch(BindException bind_ex) { start_port++; continue; } catch(IOException io_ex) { } break; } return ret; } /** * Finds first available port starting at start_port and returns server * socket. Will not bind to port >end_port. Sets srv_port */ public static ServerSocket createServerSocket(SocketFactory factory, String service_name, InetAddress bind_addr, int start_port, int end_port) throws Exception { ServerSocket ret=null; int original_start_port=start_port; while(true) { try { if(bind_addr == null) ret=factory.createServerSocket(service_name, start_port); else { // changed (bela Sept 7 2007): we accept connections on all NICs ret=factory.createServerSocket(service_name, start_port, 50, bind_addr); } } catch(SocketException bind_ex) { if(start_port == end_port) throw new BindException("No available port to bind to in range [" + original_start_port + " .. " + end_port + "]"); 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: " + bind_ex); } start_port++; continue; } break; } 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(SocketFactory factory, String service_name, InetAddress addr, int port) throws Exception { DatagramSocket sock=null; if(addr == null) { if(port == 0) { return factory.createDatagramSocket(service_name); } else { while(port < MAX_PORT) { try { return factory.createDatagramSocket(service_name, port); } catch(BindException bind_ex) { // port already used port++; } } } } else { if(port == 0) port=1024; while(port < MAX_PORT) { try { return factory.createDatagramSocket(service_name, port, addr); } catch(BindException bind_ex) { // port already used port++; } } } return sock; // will never be reached, but the stupid compiler didn't figure it out... } public static MulticastSocket createMulticastSocket(SocketFactory factory, String service_name, InetAddress mcast_addr, int port, Log log) throws IOException { if(mcast_addr != null && !mcast_addr.isMulticastAddress()) throw new IllegalArgumentException("mcast_addr (" + mcast_addr + ") is not a valid multicast address"); SocketAddress saddr=new InetSocketAddress(mcast_addr, port); MulticastSocket retval=null; try { retval=factory.createMulticastSocket(service_name, 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.org/community/docs/DOC-9469 for details). "); sb.append("\nException was: " + ex); log.warn(sb.toString()); } } if(retval == null) retval=factory.createMulticastSocket(service_name, 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 { // determine the desired values for bind_addr_str and bind_interface_str boolean ignore_systemprops=Util.isBindAddressPropertyIgnored(); String bind_addr_str =Util.getProperty(new String[]{Global.BIND_ADDR, Global.BIND_ADDR_OLD}, props, "bind_addr", ignore_systemprops, null); String bind_interface_str =Util.getProperty(new String[]{Global.BIND_INTERFACE, null}, props, "bind_interface", ignore_systemprops, null); InetAddress bind_addr=null; NetworkInterface bind_intf=null; StackType ip_version=Util.getIpStackType(); // 1. if bind_addr_str specified, get bind_addr and check version if(bind_addr_str != null) { bind_addr=InetAddress.getByName(bind_addr_str); // check that bind_addr_host has correct IP version boolean hasCorrectVersion = ((bind_addr instanceof Inet4Address && ip_version == StackType.IPv4) || (bind_addr instanceof Inet6Address && ip_version == StackType.IPv6)) ; if (!hasCorrectVersion) throw new IllegalArgumentException("bind_addr " + bind_addr_str + " has incorrect IP version") ; } // 2. if bind_interface_str specified, get interface and check that it has correct version if(bind_interface_str != null) { bind_intf=NetworkInterface.getByName(bind_interface_str); if(bind_intf != null) { // check that the interface supports the IP version boolean supportsVersion = interfaceHasIPAddresses(bind_intf, ip_version) ; if (!supportsVersion) throw new IllegalArgumentException("bind_interface " + bind_interface_str + " has incorrect IP version") ; } else { // (bind_intf == null) throw new UnknownHostException("network interface " + bind_interface_str + " not found"); } } // 3. intf and bind_addr are both are specified, bind_addr needs to be on intf if (bind_intf != null && bind_addr != null) { boolean hasAddress = false ; // get all the InetAddresses defined on the interface Enumeration addresses = bind_intf.getInetAddresses() ; while (addresses != null && addresses.hasMoreElements()) { // get the next InetAddress for the current interface InetAddress address = (InetAddress) addresses.nextElement() ; // check if address is on interface if (bind_addr.equals(address)) { hasAddress = true ; break ; } } if (!hasAddress) { throw new IllegalArgumentException("network interface " + bind_interface_str + " does not contain address " + bind_addr_str); } } // 4. if only interface is specified, get first non-loopback address on that interface, else if (bind_intf != null) { bind_addr = getAddress(bind_intf, AddressScope.NON_LOOPBACK) ; } // 5. if neither bind address nor bind interface is specified, get the first non-loopback // address on any interface else if (bind_addr == null) { bind_addr = getNonLoopbackAddress() ; } // if we reach here, if bind_addr == null, we have tried to obtain a bind_addr but were not successful // in such a case, using a loopback address of the correct version is our only option boolean localhost = false; if (bind_addr == null) { bind_addr = getLocalhost(ip_version); localhost = true; } //http://jira.jboss.org/jira/browse/JGRP-739 //check all bind_address against NetworkInterface.getByInetAddress() to see if it exists on the machine //in some Linux setups NetworkInterface.getByInetAddress(InetAddress.getLocalHost()) returns null, so skip //the check in that case if(!localhost && NetworkInterface.getByInetAddress(bind_addr) == null) { throw new UnknownHostException("Invalid bind address " + bind_addr); } if(props != null) { props.remove("bind_addr"); props.remove("bind_interface"); } return bind_addr; } /** * Method used by PropertyConverters.BindInterface to check that a bind_address is * consistent with a specified interface * * Idea: * 1. We are passed a bind_addr, which may be null * 2. If non-null, check that bind_addr is on bind_interface - if not, throw exception, * otherwise, return the original bind_addr * 3. If null, get first non-loopback address on bind_interface, using stack preference to * get the IP version. If no non-loopback address, then just return null (i.e. the * bind_interface did not influence the decision). * */ public static InetAddress validateBindAddressFromInterface(InetAddress bind_addr, String bind_interface_str) throws UnknownHostException, SocketException { NetworkInterface bind_intf=null; if(bind_addr != null && bind_addr.isLoopbackAddress()) return bind_addr; // 1. if bind_interface_str is null, or empty, no constraint on bind_addr if (bind_interface_str == null || bind_interface_str.trim().length() == 0) return bind_addr; // 2. get the preferred IP version for the JVM - it will be IPv4 or IPv6 StackType ip_version = getIpStackType(); // 3. if bind_interface_str specified, get interface and check that it has correct version bind_intf=NetworkInterface.getByName(bind_interface_str); if(bind_intf != null) { // check that the interface supports the IP version boolean supportsVersion = interfaceHasIPAddresses(bind_intf, ip_version) ; if (!supportsVersion) throw new IllegalArgumentException("bind_interface " + bind_interface_str + " has incorrect IP version") ; } else { // (bind_intf == null) throw new UnknownHostException("network interface " + bind_interface_str + " not found"); } // 3. intf and bind_addr are both are specified, bind_addr needs to be on intf if (bind_addr != null) { boolean hasAddress = false ; // get all the InetAddresses defined on the interface Enumeration addresses = bind_intf.getInetAddresses() ; while (addresses != null && addresses.hasMoreElements()) { // get the next InetAddress for the current interface InetAddress address = (InetAddress) addresses.nextElement() ; // check if address is on interface if (bind_addr.equals(address)) { hasAddress = true ; break ; } } if (!hasAddress) { String bind_addr_str = bind_addr.getHostAddress(); throw new IllegalArgumentException("network interface " + bind_interface_str + " does not contain address " + bind_addr_str); } } // 4. if only interface is specified, get first non-loopback address on that interface, else { bind_addr = getAddress(bind_intf, AddressScope.NON_LOOPBACK) ; } //http://jira.jboss.org/jira/browse/JGRP-739 //check all bind_address against NetworkInterface.getByInetAddress() to see if it exists on the machine //in some Linux setups NetworkInterface.getByInetAddress(InetAddress.getLocalHost()) returns null, so skip //the check in that case if(bind_addr != null && NetworkInterface.getByInetAddress(bind_addr) == null) { throw new UnknownHostException("Invalid bind address " + bind_addr); } // if bind_addr == null, we have tried to obtain a bind_addr but were not successful // in such a case, return the original value of null so the default will be applied return bind_addr; } 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"); } public static boolean checkForMac() { return checkForPresence("os.name", "mac"); } 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 Vector unmodifiableVector(Vector v) { if(v == null) return null; return new UnmodifiableVector(v); } /** IP related utilities */ public static InetAddress getLocalhost(StackType ip_version) throws UnknownHostException { if (ip_version == StackType.IPv4) return InetAddress.getByName("127.0.0.1") ; else return InetAddress.getByName("::1") ; } /** * Returns the first non-loopback address on any interface on the current host. */ public static InetAddress getNonLoopbackAddress() throws SocketException { return getAddress(AddressScope.NON_LOOPBACK); } /** * Returns the first address on any interface of the current host, which satisfies scope */ public static InetAddress getAddress(AddressScope scope) throws SocketException { InetAddress address=null ; Enumeration intfs=NetworkInterface.getNetworkInterfaces(); while(intfs.hasMoreElements()) { NetworkInterface intf=(NetworkInterface)intfs.nextElement(); try { if(intf.isUp()) { address=getAddress(intf, scope) ; if(address != null) return address; } } catch (SocketException e) { } } return null ; } /** * Returns the first address on the given interface on the current host, which satisfies scope * * @param intf the interface to be checked */ public static InetAddress getAddress(NetworkInterface intf, AddressScope scope) throws SocketException { StackType ip_version=Util.getIpStackType(); for(Enumeration addresses=intf.getInetAddresses(); addresses.hasMoreElements();) { InetAddress addr=(InetAddress)addresses.nextElement(); boolean match; switch(scope) { case GLOBAL: match=!addr.isLoopbackAddress() && !addr.isLinkLocalAddress() && !addr.isSiteLocalAddress(); break; case SITE_LOCAL: match=addr.isSiteLocalAddress(); break; case LINK_LOCAL: match=addr.isLinkLocalAddress(); break; case LOOPBACK: match=addr.isLoopbackAddress(); break; case NON_LOOPBACK: match=!addr.isLoopbackAddress(); break; default: throw new IllegalArgumentException("scope " + scope + " is unknown"); } if(match) { if((addr instanceof Inet4Address && ip_version == StackType.IPv4) || (addr instanceof Inet6Address && ip_version == StackType.IPv6)) return addr; } } return null ; } /** * A function to check if an interface supports an IP version (i.e has addresses * defined for that IP version). * * @param intf * @return */ public static boolean interfaceHasIPAddresses(NetworkInterface intf, StackType ip_version) throws SocketException, UnknownHostException { boolean supportsVersion = false ; if (intf != null) { // get all the InetAddresses defined on the interface Enumeration addresses = intf.getInetAddresses() ; while (addresses != null && addresses.hasMoreElements()) { // get the next InetAddress for the current interface InetAddress address = (InetAddress) addresses.nextElement() ; // check if we find an address of correct version if ((address instanceof Inet4Address && (ip_version == StackType.IPv4)) || (address instanceof Inet6Address && (ip_version == StackType.IPv6))) { supportsVersion = true ; break ; } } } else { throw new UnknownHostException("network interface " + intf + " not found") ; } return supportsVersion ; } public static StackType getIpStackType() { return ip_stack_type; } /** * Tries to determine the type of IP stack from the available interfaces and their addresses and from the * system properties (java.net.preferIPv4Stack and java.net.preferIPv6Addresses) * @return StackType.IPv4 for an IPv4 only stack, StackYTypeIPv6 for an IPv6 only stack, and StackType.Unknown * if the type cannot be detected */ private static StackType _getIpStackType() { boolean isIPv4StackAvailable = isStackAvailable(true) ; boolean isIPv6StackAvailable = isStackAvailable(false) ; // if only IPv4 stack available if (isIPv4StackAvailable && !isIPv6StackAvailable) { return StackType.IPv4; } // if only IPv6 stack available else if (isIPv6StackAvailable && !isIPv4StackAvailable) { return StackType.IPv6; } // if dual stack else if (isIPv4StackAvailable && isIPv6StackAvailable) { // get the System property which records user preference for a stack on a dual stack machine if(Boolean.getBoolean(Global.IPv4)) // has preference over java.net.preferIPv6Addresses return StackType.IPv4; if(Boolean.getBoolean(Global.IPv6)) return StackType.IPv6; return StackType.IPv6; } return StackType.Unknown; } public static boolean isStackAvailable(boolean ipv4) { Collection all_addrs=getAllAvailableAddresses(); for(InetAddress addr: all_addrs) if(ipv4 && addr instanceof Inet4Address || (!ipv4 && addr instanceof Inet6Address)) return true; return false; } 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; } public static Collection getAllAvailableAddresses() { Set retval=new HashSet(); Enumeration en; try { en=NetworkInterface.getNetworkInterfaces(); if(en == null) return retval; while(en.hasMoreElements()) { NetworkInterface intf=(NetworkInterface)en.nextElement(); Enumeration addrs=intf.getInetAddresses(); while(addrs.hasMoreElements()) retval.add(addrs.nextElement()); } } catch(SocketException e) { e.printStackTrace(); } return retval; } public static void checkIfValidAddress(InetAddress bind_addr, String prot_name) throws Exception { if(bind_addr.isAnyLocalAddress() || bind_addr.isLoopbackAddress()) return; Collection addrs=getAllAvailableAddresses(); for(InetAddress addr: addrs) { if(addr.equals(bind_addr)) return; } throw new BindException("[" + prot_name + "] " + bind_addr + " is not a valid address on any local network interface"); } /** * 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 boolean isCoordinator(JChannel ch) { return isCoordinator(ch.getView(), ch.getAddress()); } public static boolean isCoordinator(View view, Address local_addr) { if(view == null || local_addr == null) return false; Vector
                mbrs=view.getMembers(); return !(mbrs == null || mbrs.isEmpty()) && local_addr.equals(mbrs.firstElement()); } 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, create a default return MBeanServerFactory.createMBeanServer(); } } public static void registerChannel(JChannel channel, String name) { MBeanServer server=Util.getMBeanServer(); if(server != null) { try { JmxConfigurator.registerChannel(channel, server, (name != null? name : "jgroups"), channel.getClusterName(), true); } catch(Exception e) { e.printStackTrace(); } } } 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(); } /** * Go through the input string and replace any occurance of ${p} with the * props.getProperty(p) value. If there is no such property p defined, then * the ${p} reference will remain unchanged. * * If the property reference is of the form ${p:v} and there is no such * property p, then the default value v will be returned. * * If the property reference is of the form ${p1,p2} or ${p1,p2:v} then the * primary and the secondary properties will be tried in turn, before * returning either the unchanged input, or the default value. * * The property ${/} is replaced with System.getProperty("file.separator") * value and the property ${:} is replaced with * System.getProperty("path.separator"). * * @param string - * the string with possible ${} references * @param props - * the source for ${x} property ref values, null means use * System.getProperty() * @return the input string with all property references replaced if any. If * there are no valid references the input string will be returned. * @throws {@link java.security.AccessControlException} * when not authorised to retrieved system properties */ public static String replaceProperties(final String string, final Properties props) { /** File separator value */ final String FILE_SEPARATOR=File.separator; /** Path separator value */ final String PATH_SEPARATOR=File.pathSeparator; /** File separator alias */ final String FILE_SEPARATOR_ALIAS="/"; /** Path separator alias */ final String PATH_SEPARATOR_ALIAS=":"; // States used in property parsing final int NORMAL=0; final int SEEN_DOLLAR=1; final int IN_BRACKET=2; final char[] chars=string.toCharArray(); StringBuilder buffer=new StringBuilder(); boolean properties=false; int state=NORMAL; int start=0; for(int i=0;i < chars.length;++i) { char c=chars[i]; // Dollar sign outside brackets if(c == '$' && state != IN_BRACKET) state=SEEN_DOLLAR; // Open bracket immediatley after dollar else if(c == '{' && state == SEEN_DOLLAR) { buffer.append(string.substring(start, i - 1)); state=IN_BRACKET; start=i - 1; } // No open bracket after dollar else if(state == SEEN_DOLLAR) state=NORMAL; // Closed bracket after open bracket else if(c == '}' && state == IN_BRACKET) { // No content if(start + 2 == i) { buffer.append("${}"); // REVIEW: Correct? } else // Collect the system property { String value=null; String key=string.substring(start + 2, i); // check for alias if(FILE_SEPARATOR_ALIAS.equals(key)) { value=FILE_SEPARATOR; } else if(PATH_SEPARATOR_ALIAS.equals(key)) { value=PATH_SEPARATOR; } else { // check from the properties if(props != null) value=props.getProperty(key); else value=System.getProperty(key); if(value == null) { // Check for a default value ${key:default} int colon=key.indexOf(':'); if(colon > 0) { String realKey=key.substring(0, colon); if(props != null) value=props.getProperty(realKey); else value=System.getProperty(realKey); if(value == null) { // Check for a composite key, "key1,key2" value=resolveCompositeKey(realKey, props); // Not a composite key either, use the specified default if(value == null) value=key.substring(colon + 1); } } else { // No default, check for a composite key, "key1,key2" value=resolveCompositeKey(key, props); } } } if(value != null) { properties=true; buffer.append(value); } } start=i + 1; state=NORMAL; } } // No properties if(properties == false) return string; // Collect the trailing characters if(start != chars.length) buffer.append(string.substring(start, chars.length)); // Done return buffer.toString(); } /** * Try to resolve a "key" from the provided properties by checking if it is * actually a "key1,key2", in which case try first "key1", then "key2". If * all fails, return null. * * It also accepts "key1," and ",key2". * * @param key * the key to resolve * @param props * the properties to use * @return the resolved key or null */ private static String resolveCompositeKey(String key, Properties props) { String value=null; // Look for the comma int comma=key.indexOf(','); if(comma > -1) { // If we have a first part, try resolve it if(comma > 0) { // Check the first part String key1=key.substring(0, comma); if(props != null) value=props.getProperty(key1); else value=System.getProperty(key1); } // Check the second part, if there is one and first lookup failed if(value == null && comma < key.length() - 1) { String key2=key.substring(comma + 1); if(props != null) value=props.getProperty(key2); else value=System.getProperty(key2); } } // Return whatever we've found or null return value; } // /** // * Replaces variables with values from system properties. If a system property is not found, the property is // * removed from the output string // * @param input // * @return // */ // public static String substituteVariables(String input) throws Exception { // Collection configs=Configurator.parseConfigurations(input); // for(Configurator.ProtocolConfiguration config: configs) { // for(Iterator> it=config.getProperties().entrySet().iterator(); it.hasNext();) { // Map.Entry entry=it.next(); // // // } // } // // // return null; // } /** * 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(); } public 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); if(default_val != null && default_val.length() > 0) default_val=default_val.trim(); // retval=System.getProperty(var, default_val); retval=_getProperty(var, default_val); } else { var=s; // retval=System.getProperty(var); retval=_getProperty(var, null); } return retval; } /** * Parses a var which might be comma delimited, e.g. bla,foo:1000: if 'bla' is set, return its value. Else, * if 'foo' is set, return its value, else return "1000" * @param var * @param default_value * @return */ private static String _getProperty(String var, String default_value) { if(var == null) return null; List list=parseCommaDelimitedStrings(var); if(list == null || list.isEmpty()) { list=new ArrayList(1); list.add(var); } String retval=null; for(String prop: list) { try { retval=System.getProperty(prop); if(retval != null) return retval; } catch(Throwable e) { } } return default_value; } /** * 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; } } public static String methodNameToAttributeName(String methodName) { methodName=methodName.startsWith("get") || methodName.startsWith("set")? methodName.substring(3): methodName; methodName=methodName.startsWith("is")? methodName.substring(2) : methodName; // Pattern p=Pattern.compile("[A-Z]+"); Matcher m=METHOD_NAME_TO_ATTR_NAME_PATTERN.matcher(methodName); StringBuffer sb=new StringBuffer(); while(m.find()) { int start=m.start(), end=m.end(); String str=methodName.substring(start, end).toLowerCase(); if(str.length() > 1) { String tmp1=str.substring(0, str.length() -1); String tmp2=str.substring(str.length() -1); str=tmp1 + "_" + tmp2; } if(start == 0) { m.appendReplacement(sb, str); } else m.appendReplacement(sb, "_" + str); } m.appendTail(sb); return sb.toString(); } public static String attributeNameToMethodName(String attr_name) { if(attr_name.contains("_")) { // Pattern p=Pattern.compile("_."); Matcher m=ATTR_NAME_TO_METHOD_NAME_PATTERN.matcher(attr_name); StringBuffer sb=new StringBuffer(); while(m.find()) { m.appendReplacement(sb, attr_name.substring(m.end() - 1, m.end()).toUpperCase()); } m.appendTail(sb); char first=sb.charAt(0); if(Character.isLowerCase(first)) { sb.setCharAt(0, Character.toUpperCase(first)); } return sb.toString(); } else { if(Character.isLowerCase(attr_name.charAt(0))) { return attr_name.substring(0, 1).toUpperCase() + attr_name.substring(1); } else { return attr_name; } } } /** * Runs a task on a separate thread * @param task * @param factory * @param group * @param thread_name */ public static void runAsync(Runnable task, ThreadFactory factory, ThreadGroup group, String thread_name) { Thread thread=factory.newThread(group, task, thread_name); thread.start(); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/util/XMLSchemaGenerator.java0000644000175000017500000002106211647260573027077 0ustar moellermoellerpackage org.jgroups.util; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.net.URL; import java.util.HashSet; import java.util.Set; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.transform.OutputKeys; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import org.jgroups.Version; import org.jgroups.annotations.Property; import org.jgroups.stack.Configurator; import org.jgroups.stack.Protocol; import org.w3c.dom.DOMImplementation; import org.w3c.dom.Document; import org.w3c.dom.Element; /** * Iterates over all concrete Protocol classes and creates XML schema used for validation of * configuration files. * * https://jira.jboss.org/jira/browse/JGRP-448 * * @author Vladimir Blagojevic * */ public class XMLSchemaGenerator { public static void main(String[] args) { String outputDir = "./"; for (int i = 0; i < args.length; i++) { String arg = args[i]; if ("-o".equals(arg)) { outputDir = args[++i]; continue; } else { System.out.println("XMLSchemaGenerator -o "); return; } } File f = new File(outputDir, "JGroups-" + Version.major + "." + Version.minor + ".xsd"); try { FileWriter fw = new FileWriter(f, false); DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); DOMImplementation impl = builder.getDOMImplementation(); Document xmldoc = impl.createDocument("http://www.w3.org/2001/XMLSchema", "xs:schema", null); xmldoc.getDocumentElement().setAttribute("targetNamespace", "urn:org:jgroups"); xmldoc.getDocumentElement().setAttribute("elementFormDefault", "qualified"); Element xsElement = xmldoc.createElement("xs:element"); xsElement.setAttribute("name", "config"); xmldoc.getDocumentElement().appendChild(xsElement); Element complexType = xmldoc.createElement("xs:complexType"); xsElement.appendChild(complexType); Element allType = xmldoc.createElement("xs:choice"); allType.setAttribute("maxOccurs", "unbounded"); complexType.appendChild(allType); Set> classes = getClasses("org.jgroups.protocols", Protocol.class); for (Class clazz : classes) { classToXML(xmldoc, allType, clazz, ""); } classes = getClasses("org.jgroups.protocols.pbcast", Protocol.class); for (Class clazz : classes) { classToXML(xmldoc, allType, clazz, "pbcast."); } DOMSource domSource = new DOMSource(xmldoc); StreamResult streamResult = new StreamResult(fw); TransformerFactory tf = TransformerFactory.newInstance(); Transformer serializer = tf.newTransformer(); serializer.setOutputProperty(OutputKeys.METHOD, "xml"); serializer.setOutputProperty(OutputKeys.INDENT, "yes"); serializer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "1"); serializer.transform(domSource, streamResult); fw.flush(); fw.close(); } catch (Exception e) { e.printStackTrace(); } } private static Set> getClasses(String packageName, Class assignableFrom) throws IOException, ClassNotFoundException { ClassLoader loader = Thread.currentThread().getContextClassLoader(); Set> classes = new HashSet>(); String path = packageName.replace('.', '/'); URL resource = loader.getResource(path); if (resource != null) { String filePath = resource.getFile(); if (filePath != null && new File(filePath).isDirectory()) { for (String file : new File(filePath).list()) { if (file.endsWith(".class")) { String name = packageName + '.' + file.substring(0, file.indexOf(".class")); Class clazz = Class.forName(name); if (assignableFrom.isAssignableFrom(clazz)) classes.add(clazz); } } } } return classes; } private static void classToXML(Document xmldoc, Element parent, Class clazz, String preAppendToSimpleClassName) throws Exception { boolean isConcreteClass = (clazz.getModifiers() & Modifier.ABSTRACT) == 0; if (isConcreteClass && !clazz.isAnonymousClass()) { parent.appendChild(createXMLTree(xmldoc, clazz, preAppendToSimpleClassName)); } } private static Element createXMLTree(Document xmldoc, Class clazz, String preAppendToSimpleClassName) throws Exception { Element classElement = xmldoc.createElement("xs:element"); String elementName = preAppendToSimpleClassName + clazz.getSimpleName(); if(elementName == null || elementName.length()==0) { throw new IllegalArgumentException("Cannot create empty attribute name for element xs:element, class is " + clazz); } classElement.setAttribute("name",elementName); Element complexType = xmldoc.createElement("xs:complexType"); classElement.appendChild(complexType); // iterate fields for (Class clazzInLoop = clazz; clazzInLoop != null; clazzInLoop = clazzInLoop.getSuperclass()) { Field[] fields = clazzInLoop.getDeclaredFields(); for (Field field : fields) { if (field.isAnnotationPresent(Property.class)) { String property = field.getName(); Property r = field.getAnnotation(Property.class); boolean annotationRedefinesName = r.name().length() > 0 && r.deprecatedMessage().length() == 0; if (annotationRedefinesName) { property = r.name(); } if(property == null || property.length()==0) { throw new IllegalArgumentException("Cannot create empty attribute name for element xs:attribute, field is " + field); } Element attributeElement = xmldoc.createElement("xs:attribute"); attributeElement.setAttribute("name", property); // Agreement with Bela Ban on Jan-20-2009 (Go Obama!!!) to treat all types as // xs:string since we do not know where users are going to use // replacement tokens in configuration files. Therefore, the type becomes // indeterminate. // attributeElement.setAttribute("type", fieldToXMLSchemaAttributeType(field)); attributeElement.setAttribute("type", "xs:string"); complexType.appendChild(attributeElement); Element annotationElement = xmldoc.createElement("xs:annotation"); attributeElement.appendChild(annotationElement); Element documentationElement = xmldoc.createElement("xs:documentation"); documentationElement.setTextContent(r.description()); annotationElement.appendChild(documentationElement); } } } // iterate methods Method[] methods = clazz.getMethods(); for (Method method : methods) { if (method.isAnnotationPresent(Property.class) && method.getName().startsWith("set")) { Property annotation = method.getAnnotation(Property.class); String name = annotation.name(); if (name.length() < 1) { name = Util.methodNameToAttributeName(method.getName()); } Element attributeElement = xmldoc.createElement("xs:attribute"); attributeElement.setAttribute("name", name); attributeElement.setAttribute("type", "xs:string"); complexType.appendChild(attributeElement); String desc = annotation.description(); if (desc.length() > 0) { Element annotationElement = xmldoc.createElement("xs:annotation"); attributeElement.appendChild(annotationElement); Element documentationElement = xmldoc.createElement("xs:documentation"); documentationElement.setTextContent(annotation.description()); annotationElement.appendChild(documentationElement); } } } return classElement; } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/util/obsolete/0000755000175000017500000000000011647260573024417 5ustar moellermoellerlibjgroups-java-2.12.2.Final.orig/src/org/jgroups/util/obsolete/CondVar.java.txt0000644000175000017500000000727511647260573027447 0ustar moellermoellerpackage 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 */ 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; } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/util/obsolete/List.java.txt0000644000175000017500000002664411647260573027027 0ustar moellermoeller 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"); } } libjgroups-java-2.12.2.Final.orig/src/org/jgroups/util/package.html0000644000175000017500000000017111647260573025063 0ustar moellermoeller Provides useful functionality which cannot be assigned to any particular other package. libjgroups-java-2.12.2.Final.orig/src/org/jgroups/util/todo.txt0000644000175000017500000000606211647260573024315 0ustar moellermoeller Todo List ========= 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. libjgroups-java-2.12.2.Final.orig/tests/0000755000175000017500000000000011647260573017721 5ustar moellermoellerlibjgroups-java-2.12.2.Final.orig/tests/junit-functional/0000755000175000017500000000000011647260573023212 5ustar moellermoellerlibjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/0000755000175000017500000000000011647260573024001 5ustar moellermoellerlibjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/0000755000175000017500000000000011647260573025472 5ustar moellermoellerlibjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/blocks/0000755000175000017500000000000011647260573026747 5ustar moellermoellerlibjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/blocks/ConnectionMapTest.java0000644000175000017500000001764211647260573033221 0ustar moellermoellerpackage org.jgroups.blocks; import org.jgroups.Address; import org.jgroups.Global; import org.jgroups.stack.IpAddress; import org.jgroups.util.DefaultThreadFactory; import org.jgroups.util.ResourceManager; import org.jgroups.util.StackType; import org.jgroups.util.Util; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.List; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; /** * Tests ConnectionMap * @author Bela Ban */ @Test(groups=Global.FUNCTIONAL,sequential=true) public class ConnectionMapTest { private TCPConnectionMap ct1, ct2; static final InetAddress loopback_addr; static { try { StackType type=Util.getIpStackType(); String tmp=type == StackType.IPv6? "::1" : "127.0.0.1"; loopback_addr=InetAddress.getByName(tmp); } catch(UnknownHostException e) { throw new RuntimeException("failed initializing loopback_addr", e); } } static byte[] data=new byte[]{'b', 'e', 'l', 'a'}; protected int PORT1, PORT2; protected Address addr1, addr2; @BeforeMethod protected void init() throws Exception { List ports=ResourceManager.getNextTcpPorts(loopback_addr, 2); PORT1=ports.get(0); PORT2=ports.get(1); addr1=new IpAddress(loopback_addr, PORT1); addr2=new IpAddress(loopback_addr, PORT2); } @AfterMethod protected void tearDown() throws Exception { if(ct2 != null) { ct2.stop(); ct2=null; } if(ct1 != null) { ct1.stop(); ct1=null; } } /** * 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.

                * Turned concurrent test into a simple sequential test. We're going to replace this code with NIO2 soon anyway... */ public void testReuseOfConnection() throws Exception { TCPConnectionMap.Receiver dummy=new TCPConnectionMap.Receiver() { public void receive(Address sender, byte[] data, int offset, int length) {} }; ct1=new TCPConnectionMap("ConnectionMapTest1", new DefaultThreadFactory(Util.getGlobalThreadGroup(), "ConnectionMapTest", true), null, dummy, loopback_addr, null, PORT1, PORT1); ct1.start(); ct2=new TCPConnectionMap("ConnectionMapTest2", new DefaultThreadFactory(Util.getGlobalThreadGroup(), "ConnectionMapTest", true), null, dummy, loopback_addr, null, PORT2, PORT2); ct2.start(); int num_conns; num_conns=ct1.getNumConnections(); assert num_conns == 0; num_conns=ct2.getNumConnections(); assert num_conns == 0; ct1.send(addr2, data, 0, data.length); ct2.send(addr1, data, 0, data.length); String msg="ct1: " + ct1 + "\nct2: " + ct2; System.out.println(msg); num_conns=ct1.getNumConnections(); assert num_conns == 1 : "num_conns for ct1 is " + num_conns + ", " + msg; num_conns=ct2.getNumConnections(); assert num_conns == 1 : "num_conns for ct2 is " + num_conns + ", " + msg; assert ct1.connectionEstablishedTo(addr2) : "valid connection to peer"; assert ct2.connectionEstablishedTo(addr1) : "valid connection to peer"; } public static 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); assert !(taker.isAlive()) : "taker: " + taker; } public void testStopConnectionMapNoSendQueues() throws Exception { ct1=new TCPConnectionMap("ConnectionMapTest1", new DefaultThreadFactory(Util.getGlobalThreadGroup(), "ConnectionMapTest", true), new DummyReceiver(), loopback_addr, null, PORT1, PORT1, 60000, 120000); ct1.setUseSendQueues(false); ct1.start(); ct2=new TCPConnectionMap("ConnectionMapTest2", new DefaultThreadFactory(Util.getGlobalThreadGroup(), "ConnectionMapTest", true), new DummyReceiver(), loopback_addr, null, PORT2, PORT2, 60000, 120000); ct2.setUseSendQueues(false); ct2.start(); _testStop(ct1, ct2); } public void testStopConnectionMapWithSendQueues() throws Exception { ct1=new TCPConnectionMap("ConnectionMapTest1", new DefaultThreadFactory(Util.getGlobalThreadGroup(), "ConnectionMapTest", true), new DummyReceiver(), loopback_addr, null, PORT1, PORT1, 60000, 120000); ct1.start(); ct2=new TCPConnectionMap("ConnectionMapTest2", new DefaultThreadFactory(Util.getGlobalThreadGroup(), "ConnectionMapTest", true), new DummyReceiver(), loopback_addr, null, PORT2, PORT2, 60000, 120000); ct2.start(); _testStop(ct1, ct2); } /* public void testStopConnectionMapNIONoSendQueues() throws Exception { ct1=new ConnectionTableNIO(new DummyReceiver(), loopback_addr, null, PORT1, PORT1, 60000, 120000, false); ct1.setUseSendQueues(false); ct2=new ConnectionTableNIO(new DummyReceiver(), loopback_addr, null, PORT2, PORT2, 60000, 120000, false); ct2.setUseSendQueues(false); ct1.start(); ct2.start(); _testStop(ct1, ct2); } public void testStopConnectionMapNIOWithSendQueues() throws Exception { ct1=new ConnectionTableNIO(new DummyReceiver(), loopback_addr, null, PORT1, PORT1, 60000, 120000, false); ct2=new ConnectionTableNIO(new DummyReceiver(), loopback_addr, null, PORT2, PORT2, 60000, 120000, false); ct1.start(); ct2.start(); _testStop(ct1, ct2); }*/ private void _testStop(TCPConnectionMap table1, TCPConnectionMap table2) throws Exception { table1.send(addr1, data, 0, data.length); // send to self assert table1.getNumConnections() == 0; 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); int num_conns_table1=table1.getNumConnections(), num_conns_table2=table2.getNumConnections(); assert num_conns_table1 == 1 : "table1 should have 1 connection, but has " + num_conns_table1 + ": " + table1; assert num_conns_table2 == 1 : "table2 should have 1 connection, but has " + num_conns_table2 + ": " + table2; table2.stop(); table1.stop(); assert table1.getNumConnections() == 0 : "table1 should have 0 connections: " + table1; assert table2.getNumConnections() == 0 : "table2 should have 0 connections: " + table2; } static class DummyReceiver implements TCPConnectionMap.Receiver { public void receive(Address sender, byte[] data, int offset, int length) { System.out.println("-- received " + length + " bytes from " + sender); } } } libjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/blocks/GroupRequestTest.java0000644000175000017500000003276011647260573033127 0ustar moellermoeller package org.jgroups.blocks; import org.jgroups.*; import org.jgroups.util.RspList; import org.jgroups.util.Util; import org.testng.Assert; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeClass; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.net.UnknownHostException; import java.util.Arrays; import java.util.Vector; /** * @author Bela Ban */ @Test(groups=Global.FUNCTIONAL,sequential=true) public class GroupRequestTest { Address a1, a2, a3; Vector

                dests=null; @BeforeClass void init() throws UnknownHostException { a1=Util.createRandomAddress(); a2=Util.createRandomAddress(); a3=Util.createRandomAddress(); } @BeforeMethod protected void setUp() throws Exception { dests=new Vector
                (Arrays.asList(a1, a2)); dests.add(a1); dests.add(a2); } @AfterMethod protected void tearDown() throws Exception { dests.clear(); } @Test(groups=Global.FUNCTIONAL) public void testMessageTimeout() throws Exception { _testMessageTimeout(true); } @Test(groups=Global.FUNCTIONAL) public void testMessageReception() throws Exception { _testMessageReception(true); _testMessageReception(false); } @Test(groups=Global.FUNCTIONAL) public void testMessageReceptionWithSuspect() throws Exception { _testMessageReceptionWithSuspect(true); _testMessageReceptionWithSuspect(false); } @Test(groups=Global.FUNCTIONAL) public void testMessageReceptionWithViewChange() throws Exception { _testMessageReceptionWithViewChange(true); _testMessageReceptionWithViewChange(false); } @Test(groups=Global.FUNCTIONAL) public void testMessageReceptionWithViewChangeMemberLeft() throws Exception { _testMessageReceptionWithViewChangeMemberLeft(true); _testMessageReceptionWithViewChangeMemberLeft(false); } @Test(groups=Global.FUNCTIONAL) 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, new RequestOptions(Request.GET_FIRST, 0)); 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); assert rc; assert req.isDone(); RspList results=req.getResults(); Assert.assertEquals(3, results.size()); Assert.assertEquals(1, results.numReceived()); } @Test(groups=Global.FUNCTIONAL) 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, new RequestOptions(Request.GET_ALL, 0)); 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); assert rc; assert req.isDone(); RspList results=req.getResults(); Assert.assertEquals(3, results.size()); Assert.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 * 1000; // 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 = Util.createRandomAddress(); 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, new RequestOptions(Request.GET_ALL, timeout)); tp.setGroupRequest(req); boolean rc = req.execute(); System.out.println("group request is " + req); assert rc; assert req.isDone(); RspList results = req.getResults(); Assert.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, new RequestOptions(Request.GET_ALL, 0)); transport.setGroupRequest(req); boolean rc=req.execute(); System.out.println("group request is " + req); assert rc; assert req.isDone(); RspList results=req.getResults(); Assert.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, new RequestOptions(Request.GET_ALL, 0)); transport.setGroupRequest(req); boolean rc=req.execute(); System.out.println("group request is " + req); assert rc; assert req.isDone(); RspList results=req.getResults(); assert results.size() == 2; } private void _testMessageReceptionWithViewChange(boolean async) throws Exception { Vector
                new_dests=new Vector
                (); new_dests.add(a1); new_dests.add(a2); new_dests.add(a1); Object[] responses=new Object[]{new Message(null, a1, new Long(1)), new View(Util.createRandomAddress(), 322649, new_dests), new Message(null, a2, new Long(2))}; MyTransport transport=new MyTransport(async, responses); GroupRequest req=new GroupRequest(new Message(), transport, dests, new RequestOptions(Request.GET_ALL, 0)); transport.setGroupRequest(req); boolean rc=req.execute(); System.out.println("group request is " + req); assert rc; assert req.isDone(); RspList results=req.getResults(); Assert.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(Util.createRandomAddress(), 322649, new_dests)}; MyTransport transport=new MyTransport(async, responses); GroupRequest req=new GroupRequest(new Message(), transport, dests, new RequestOptions(Request.GET_ALL, 0)); transport.setGroupRequest(req); System.out.println("group request before execution: " + req); boolean rc=req.execute(); System.out.println("group request after execution: " + req); assert rc; assert req.isDone(); RspList results=req.getResults(); Assert.assertEquals(2, results.size()); } 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"); } } } } } ././@LongLink0000000000000000000000000000014600000000000011566 Lustar rootrootlibjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/blocks/LazyRemovalCacheTest.javalibjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/blocks/LazyRemovalCacheTest.jav0000644000175000017500000001144211647260573033504 0ustar moellermoellerpackage org.jgroups.blocks; import org.testng.annotations.Test; import org.jgroups.Global; import org.jgroups.util.UUID; import org.jgroups.util.Util; import java.util.Arrays; import java.util.List; /** * @author Bela Ban */ @Test(groups=Global.FUNCTIONAL,sequential=false) public class LazyRemovalCacheTest { public static void testAdd() { LazyRemovalCache cache=new LazyRemovalCache(); UUID uuid=UUID.randomUUID(); cache.add(uuid, "node-1"); System.out.println("cache = " + cache); assert 1 == cache.size(); String val=cache.get(uuid); assert val != null && val.equals("node-1"); cache.remove(uuid); System.out.println("cache = " + cache); } public static void testRemoveAll() { LazyRemovalCache cache=new LazyRemovalCache(10, 0); List list=Arrays.asList(UUID.randomUUID(), UUID.randomUUID(), UUID.randomUUID()); int cnt=1; for(UUID uuid: list) cache.add(uuid, "node-" + cnt++); UUID uuid1=UUID.randomUUID(); UUID uuid2=UUID.randomUUID(); cache.add(uuid1, "foo"); cache.add(uuid2, "bar"); System.out.println("cache = " + cache); assert cache.size() == 5; System.out.println("removing " + list); cache.removeAll(list); System.out.println("cache = " + cache); assert cache.size() == 5; assert cache.get(uuid1).equals("foo"); assert cache.get(uuid2).equals("bar"); cache.removeMarkedElements(); System.out.println("cache = " + cache); assert cache.size() == 2; assert cache.get(uuid1).equals("foo"); assert cache.get(uuid2).equals("bar"); } public static void testRetainAll() { LazyRemovalCache cache=new LazyRemovalCache(10, 0); List list=Arrays.asList(UUID.randomUUID(), UUID.randomUUID(), UUID.randomUUID()); int cnt=1; for(UUID uuid: list) cache.add(uuid, "node-" + cnt++); UUID uuid1=UUID.randomUUID(); UUID uuid2=UUID.randomUUID(); cache.add(uuid1, "foo"); cache.add(uuid2, "bar"); System.out.println("cache = " + cache); assert cache.size() == 5; List retain=Arrays.asList(uuid1, uuid2); System.out.println("retaining " + retain); cache.retainAll(retain); System.out.println("cache = " + cache); assert cache.size() == 5; assert cache.get(uuid1).equals("foo"); assert cache.get(uuid2).equals("bar"); cache.removeMarkedElements(); System.out.println("cache = " + cache); assert cache.size() == 2; assert cache.get(uuid1).equals("foo"); assert cache.get(uuid2).equals("bar"); } public static void testRemovalOnExceedingMaxSize() { LazyRemovalCache cache=new LazyRemovalCache(2, 0); UUID u1=UUID.randomUUID(), u2=UUID.randomUUID(), u3=UUID.randomUUID(), u4=UUID.randomUUID(); cache.add(u1, "u1"); cache.add(u2, "u2"); assert cache.size() == 2; cache.add(u3, "u3"); cache.add(u4, "u4"); System.out.println("cache = " + cache); assert cache.size() == 4; cache.remove(u3); System.out.println("cache = " + cache); assert cache.size() == 3; cache.remove(u1); System.out.println("cache = " + cache); assert cache.size() == 2; cache.remove(u4); System.out.println("cache = " + cache); assert cache.size() == 2; cache.removeMarkedElements(); System.out.println("cache = " + cache); assert cache.size() == 1; } public static void testRemovalOnExceedingMaxSizeAndMaxTime() { LazyRemovalCache cache=new LazyRemovalCache(2, 1000); UUID u1=UUID.randomUUID(), u2=UUID.randomUUID(), u3=UUID.randomUUID(), u4=UUID.randomUUID(); cache.add(u1, "u1"); cache.add(u2, "u2"); assert cache.size() == 2; cache.add(u3, "u3"); cache.add(u4, "u4"); System.out.println("cache = " + cache); assert cache.size() == 4; cache.remove(u3); System.out.println("cache = " + cache); assert cache.size() == 4; cache.remove(u1); System.out.println("cache = " + cache); assert cache.size() == 4; cache.remove(u4); System.out.println("cache = " + cache); assert cache.size() == 4; cache.removeMarkedElements(); System.out.println("cache = " + cache); assert cache.size() == 4; System.out.println("sleeping for 1 sec"); Util.sleep(1100); cache.remove(u4); System.out.println("cache = " + cache); assert cache.size() == 1; } } libjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/blocks/LazyRemovalSetTest.java0000644000175000017500000001251011647260573033372 0ustar moellermoellerpackage org.jgroups.blocks; import org.jgroups.Address; import org.jgroups.Global; import org.jgroups.util.UUID; import org.jgroups.util.Util; import org.testng.annotations.Test; import java.util.Arrays; import java.util.List; /** * @author Bela Ban */ @Test(groups=Global.FUNCTIONAL,sequential=false) public class LazyRemovalSetTest { public static void testAdd() { LazyRemovalSet cache=new LazyRemovalSet(); UUID uuid=UUID.randomUUID(); cache.add(uuid); System.out.println("cache = " + cache); assert 1 == cache.size(); assert cache.contains(uuid); cache.remove(uuid); System.out.println("cache = " + cache); assert cache.contains(uuid); } public static void testRemoveAll() { LazyRemovalSet cache=new LazyRemovalSet(10, 0); cache.add("one", "two", "three", "four", "five", "two"); System.out.println("cache = " + cache); assert cache.size() == 5; List list=Arrays.asList("four", "two"); System.out.println("removing " + list); cache.removeAll(list); System.out.println("cache = " + cache); assert cache.size() == 5; cache.removeMarkedElements(); System.out.println("cache = " + cache); assert cache.size() == 3; assert cache.contains("one"); assert cache.contains("three"); assert cache.contains("five"); } public static void testRetainAll() { LazyRemovalSet cache=new LazyRemovalSet(10, 0); cache.add("one", "two", "three"); System.out.println("cache = " + cache); assert cache.size() == 3; List retain=Arrays.asList("two", "three"); System.out.println("retaining " + retain); cache.retainAll(retain); System.out.println("cache = " + cache); assert cache.size() == 3; assert cache.contains("two"); assert cache.contains("three"); cache.removeMarkedElements(); System.out.println("cache = " + cache); assert cache.size() == 2; } public static void testRemovalOnExceedingMaxSize() { LazyRemovalSet cache=new LazyRemovalSet(2, 0); cache.add("u1", "u2", "u3", "u4"); assert cache.size() == 4; cache.remove("u3"); System.out.println("cache = " + cache); assert cache.size() == 3; cache.remove("u1"); System.out.println("cache = " + cache); assert cache.size() == 2; cache.remove("u4"); System.out.println("cache = " + cache); assert cache.size() == 2; cache.removeMarkedElements(); System.out.println("cache = " + cache); assert cache.size() == 1; } public static void testRemovalOnExceedingMaxSizeAndMaxTime() { LazyRemovalSet cache=new LazyRemovalSet(2, 1000); cache.add("u1", "u2", "u3", "u4"); System.out.println("cache = " + cache); assert cache.size() == 4; cache.remove("u3"); System.out.println("cache = " + cache); assert cache.size() == 4; cache.remove("u1"); System.out.println("cache = " + cache); assert cache.size() == 4; cache.remove("u4"); System.out.println("cache = " + cache); assert cache.size() == 4; cache.removeMarkedElements(); System.out.println("cache = " + cache); assert cache.size() == 4; System.out.println("sleeping for 1 sec"); Util.sleep(1100); cache.remove("u4"); System.out.println("cache = " + cache); assert cache.size() == 1; } public static void testCapacityExceeded() { LazyRemovalSet cache=new LazyRemovalSet(5, 0); for(int i=1; i <=10; i++) cache.add(i); System.out.println("cache = " + cache); assert cache.size() == 10; cache.retainAll(Arrays.asList(1,4,6,8)); cache.add(11); System.out.println("cache = " + cache); assert cache.size() == 5; } public static void testContains() { LazyRemovalSet
                cache=new LazyRemovalSet
                (5, 0); Address a=Util.createRandomAddress("A"), b=Util.createRandomAddress("B"), c=Util.createRandomAddress("C"), d=Util.createRandomAddress("D"); cache.add(a,b,c,d); System.out.println("cache = " + cache); assert cache.size() == 4; assert cache.contains(a); assert cache.contains(b); assert cache.contains(d); assert cache.contains(d); cache.retainAll(Arrays.asList(a,c)); System.out.println("cache = " + cache); assert cache.size() == 4; cache.removeMarkedElements(); System.out.println("cache = " + cache); assert cache.size() == 2; } public static void testReAddition() { LazyRemovalSet cache=new LazyRemovalSet(1, 1000); cache.add("one", "two", "three"); Util.sleep(1500); System.out.println("cache = " + cache); cache.add("two"); System.out.println("cache = " + cache); cache.clear(false); cache.removeMarkedElements(); System.out.println("cache = " + cache); assert cache.size() == 1; assert cache.contains("two"); } } libjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/protocols/0000755000175000017500000000000011647260573027516 5ustar moellermoellerlibjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/protocols/AUTHTest.java0000644000175000017500000001067211647260573031770 0ustar moellermoellerpackage org.jgroups.protocols; import org.jgroups.Global; import org.jgroups.auth.MD5Token; import org.jgroups.auth.SimpleToken; import org.testng.annotations.Test; /** * A set of JUnit tests for the AUTH protocol * @author Chris Mills */ @Test(groups=Global.FUNCTIONAL,sequential=false) public class AUTHTest { /** * Creates two SimpleToken objects with identical auth_values and authenticates one against the other * Test fails if an exception is thrown or authentication fails */ public static void testSimpleToken() { SimpleToken token1=new SimpleToken(); token1.setAuthValue("chris"); SimpleToken token2=new SimpleToken(); token2.setAuthValue("chris"); assert token1.authenticate(token2, null); } /** * Creates two SimpleToken objects with different auth_values and authenticates one against the other *

                * Test fails if an exception is thrown or authentication passes */ public static void testSimpleTokenMismatch() { SimpleToken token1=new SimpleToken(); token1.setAuthValue("chris"); SimpleToken token2=new SimpleToken(); token2.setAuthValue("chrismills"); assert !token1.authenticate(token2, null); } /** * Creates two MD5Token objects with identical auth_values and authenticates one against the other *

                * Uses an MD5 hash type *

                * Test fails if an exception is thrown or authentication fails */ public static void testMD5Token() { MD5Token token1=new MD5Token(); token1.setAuthValue("chris"); token1.setHashType("MD5"); MD5Token token2=new MD5Token(); token2.setAuthValue("chris"); token2.setHashType("MD5"); assert token1.authenticate(token2, null); } /** * Creates two MD5Token objects with different auth_values and authenticates one against the other *

                * Uses an MD5 hash type *

                * Test fails if an exception is thrown or authentication passes */ public static void testMD5TokenMismatch() { MD5Token token1=new MD5Token(); token1.setAuthValue("chris"); token1.setHashType("MD5"); MD5Token token2=new MD5Token(); token2.setAuthValue("chrismills"); token2.setHashType("MD5"); assert !token1.authenticate(token2, null); } /** * Creates two MD5Token objects with identical auth_values and authenticates one against the other *

                * Uses an SHA hash type *

                * Test fails if an exception is thrown or authentication fails */ public static void testSHAToken() { MD5Token token1=new MD5Token(); token1.setAuthValue("chris"); token1.setHashType("SHA"); MD5Token token2=new MD5Token(); token2.setAuthValue("chris"); token2.setHashType("SHA"); assert token1.authenticate(token2, null); } /** * Creates two MD5Token objects with different auth_values and authenticates one against the other *

                * Uses an SHA hash type *

                * Test fails if an exception is thrown or authentication passes */ public static void testSHATokenMismatch() { MD5Token token1=new MD5Token(); token1.setAuthValue("chris"); token1.setHashType("SHA"); MD5Token token2=new MD5Token(); token2.setAuthValue("chrismills"); token2.setHashType("SHA"); assert !token1.authenticate(token2, null); } /** * Test to create an AuthHeader object and set and get the Token object *

                * Fails if an exception is thrown or the set and get don't equal the same object */ public static void testAuthHeader() { SimpleToken token1=new SimpleToken(); token1.setAuthValue("chris"); AuthHeader header=new AuthHeader(); header.setToken(token1); assert token1 == header.getToken(); } /** * Test to create an AuthHeader object and set and get the Token object *

                * Fails if an exception is thrown or the set and get equal the same object */ public static void testAuthHeaderDifferent() { SimpleToken token1=new SimpleToken(); token1.setAuthValue("chris"); SimpleToken token2=new SimpleToken(); token2.setAuthValue("chris"); AuthHeader header=new AuthHeader(); header.setToken(token1); assert !(token2 == header.getToken()); } } ././@LongLink0000000000000000000000000000015200000000000011563 Lustar rootrootlibjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/protocols/ENCRYPT14KeystoreTest.javalibjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/protocols/ENCRYPT14KeystoreTest0000644000175000017500000002125511647260573033325 0ustar moellermoeller/* * Created on 04-Jul-2004 */ package org.jgroups.protocols; import org.jgroups.Address; import org.jgroups.Event; import org.jgroups.Global; import org.jgroups.Message; import org.jgroups.conf.ClassConfigurator; import org.jgroups.stack.Protocol; import org.testng.annotations.Test; import javax.crypto.Cipher; import java.io.*; import java.security.MessageDigest; import java.util.HashMap; import java.util.Map; /** * @author xenephon */ @Test(groups=Global.FUNCTIONAL, sequential=false) public class ENCRYPT14KeystoreTest { static final short ENCRYPT_ID=ClassConfigurator.getProtocolId(ENCRYPT.class); public static void testInitWrongKeystoreProperties() { ENCRYPT encrypt=new ENCRYPT(); encrypt.keyStoreName = "unkownKeystore.keystore"; try { encrypt.init(); } catch(Exception e) { System.out.println("didn't find incorrect keystore (as expected): " + e.getMessage()); assert e.getMessage().equals("Unable to load keystore " + "unkownKeystore.keystore" + " ensure file is on classpath"); } } public static void testInitKeystoreProperties() throws Exception { ENCRYPT encrypt=new ENCRYPT(); encrypt.keyStoreName = "defaultStore.keystore"; encrypt.init(); assert encrypt.getSymDecodingCipher() != null; assert encrypt.getSymEncodingCipher() != null; } public static void testMessageDownEncode() throws Exception { ENCRYPT encrypt=new ENCRYPT(); encrypt.keyStoreName = "defaultStore.keystore"; encrypt.init(); ENCRYPT encrypt2=new ENCRYPT(); encrypt2.keyStoreName = "defaultStore.keystore"; encrypt2.init(); MockObserver observer=new MockObserver(); encrypt.setObserver(observer); encrypt.keyServer=true; String messageText="hello this is a test message"; Message msg=new Message(null, null, messageText.getBytes()); Event event=new Event(Event.MSG, msg); encrypt.down(event); Message sentMsg=(Message)((Event)observer.getDownMessages().get("message0")).getArg(); String encText=new String(sentMsg.getBuffer()); assert !encText.equals(messageText); Cipher cipher=encrypt2.getSymDecodingCipher(); byte[] decodedBytes=cipher.doFinal(sentMsg.getBuffer()); String temp=new String(decodedBytes); System.out.println("decoded text:" + temp); assert temp.equals(messageText); } public static void testMessageUpDecode() throws Exception { ENCRYPT encrypt=new ENCRYPT(); encrypt.keyStoreName = "defaultStore.keystore"; encrypt.init(); ENCRYPT encrypt2=new ENCRYPT(); encrypt2.keyStoreName = "defaultStore.keystore"; encrypt2.init(); MockObserver observer=new MockObserver(); encrypt.setObserver(observer); encrypt.keyServer=true; String messageText="hello this is a test message"; Cipher cipher=encrypt2.getSymEncodingCipher(); byte[] encodedBytes=cipher.doFinal(messageText.getBytes()); assert !new String(encodedBytes).equals(messageText); MessageDigest digest=MessageDigest.getInstance("MD5"); digest.reset(); digest.update(encrypt.getDesKey().getEncoded()); String symVersion=new String(digest.digest(), "UTF-8"); Message msg=new Message(null, null, encodedBytes); msg.putHeader(ENCRYPT_ID, new ENCRYPT.EncryptHeader(ENCRYPT.EncryptHeader.ENCRYPT, symVersion)); Event event=new Event(Event.MSG, msg); encrypt.up(event); Message rcvdMsg=(Message)((Event)observer.getUpMessages().get("message0")).getArg(); String decText=new String(rcvdMsg.getBuffer()); assert decText.equals(messageText); } public static void testMessageUpWrongKey() throws Exception { ENCRYPT encrypt=new ENCRYPT(); encrypt.keyStoreName = "defaultStore.keystore"; encrypt.init(); // use a second instance so we know we are not accidentally using internal key ENCRYPT encrypt2=new ENCRYPT(); encrypt.keyStoreName = "defaultStore2.keystore"; encrypt2.init(); MockObserver observer=new MockObserver(); encrypt.setObserver(observer); encrypt.keyServer=true; String messageText="hello this is a test message"; Cipher cipher=encrypt2.getSymEncodingCipher(); byte[] encodedBytes=cipher.doFinal(messageText.getBytes()); assert !new String(encodedBytes).equals(messageText); MessageDigest digest=MessageDigest.getInstance("MD5"); digest.reset(); digest.update(encrypt2.getDesKey().getEncoded()); String symVersion=new String(digest.digest()); Message msg=new Message(null, null, encodedBytes); msg.putHeader(ENCRYPT_ID, new ENCRYPT.EncryptHeader(ENCRYPT.EncryptHeader.ENCRYPT, symVersion)); Event event=new Event(Event.MSG, msg); encrypt.up(event); assert observer.getUpMessages().isEmpty(); } public static void testMessageUpNoEncryptHeader() throws Exception { ENCRYPT encrypt=new ENCRYPT(); encrypt.keyStoreName = "defaultStore.keystore"; encrypt.init(); ENCRYPT encrypt2=new ENCRYPT(); encrypt.keyStoreName = "defaultStore.keystore"; encrypt2.init(); MockObserver observer=new MockObserver(); encrypt.setObserver(observer); encrypt.keyServer=true; String messageText="hello this is a test message"; Cipher cipher=encrypt2.getSymEncodingCipher(); byte[] encodedBytes=cipher.doFinal(messageText.getBytes()); assert !new String(encodedBytes).equals(messageText); Message msg=new Message(null, null, encodedBytes); Event event=new Event(Event.MSG, msg); encrypt.up(event); assert observer.getUpMessages().isEmpty(); } public static void testEventUpNoMessage() throws Exception { ENCRYPT encrypt=new ENCRYPT(); encrypt.keyStoreName = "defaultStore.keystore"; encrypt.init(); MockObserver observer=new MockObserver(); encrypt.setObserver(observer); encrypt.keyServer=true; Event event=new Event(Event.MSG, null); encrypt.up(event); assert observer.getUpMessages().size() == 1; } public static void testMessageUpNoBuffer() throws Exception { ENCRYPT encrypt=new ENCRYPT(); encrypt.keyStoreName = "defaultStore.keystore"; encrypt.init(); MockObserver observer=new MockObserver(); encrypt.setObserver(observer); encrypt.keyServer=true; Message msg=new Message(null, null, null); Event event=new Event(Event.MSG, msg); encrypt.up(event); assert observer.getUpMessages().size() == 1; } static class MockObserver implements ENCRYPT.Observer { private Map upMessages=new HashMap(); private Map downMessages=new HashMap(); private int counter=0; private void storeUp(Event evt) { upMessages.put("message" + counter++, evt); } private void storeDown(Event evt) { downMessages.put("message" + counter++, evt); } public void up(Event evt) { storeUp(evt); } public void setProtocol(Protocol prot) { } public void passUp(Event evt) { storeUp(evt); } public void down(Event evt) { } public void passDown(Event evt) { storeDown(evt); } protected Map getUpMessages() { return upMessages; } protected void setUpMessages(Map upMessages) { this.upMessages=upMessages; } protected Map getDownMessages() { return downMessages; } protected void setDownMessages(Map downMessages) { this.downMessages=downMessages; } } static class MockAddress implements Address { private static final long serialVersionUID=-2044466632514705356L; public boolean isMulticastAddress() { return false; } public int size() { return 0; } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { } public void writeExternal(ObjectOutput out) throws IOException { } public void writeTo(DataOutputStream out) throws IOException { ; } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { ; } public int compareTo(Address o) { return -1; } } } ././@LongLink0000000000000000000000000000015200000000000011563 Lustar rootrootlibjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/protocols/ENCRYPTAsymmetricTest.javalibjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/protocols/ENCRYPTAsymmetricTest0000644000175000017500000004207411647260573033472 0ustar moellermoeller/* * Created on 04-Jul-2004 * * To change the template for this generated file go to * Window - Preferences - Java - Code Generation - Code and Comments */ package org.jgroups.protocols; import org.jgroups.*; import org.jgroups.conf.ClassConfigurator; import org.jgroups.protocols.ENCRYPT.EncryptHeader; import org.jgroups.stack.Protocol; import org.jgroups.util.Util; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; import javax.crypto.Cipher; import java.io.*; import java.security.MessageDigest; import java.security.Security; import java.util.HashMap; import java.util.Map; import java.util.Vector; /** * @author xenephon */ @Test(groups=Global.FUNCTIONAL, sequential=false) public class ENCRYPTAsymmetricTest { static final short ENCRYPT_ID=ClassConfigurator.getProtocolId(ENCRYPT.class); @BeforeClass public static void initProvider() { Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); } public static void testInitNoProperties() throws Exception { ENCRYPT encrypt=new ENCRYPT(); encrypt.init(); // test the default asymetric key assert "RSA".equals(encrypt.getAsymAlgorithm()); assert encrypt.getAsymInit() == 512; assert "RSA".equals(encrypt.getKpair().getPublic().getAlgorithm()); assert "X.509".equals(encrypt.getKpair().getPublic().getFormat()); assert encrypt.getKpair().getPublic().getEncoded() != null; // test the default symetric key assert "AES".equals(encrypt.getSymAlgorithm()); assert encrypt.getSymInit() == 128; assert "AES".equals(encrypt.getDesKey().getAlgorithm()); assert "RAW".equals(encrypt.getDesKey().getFormat()); assert encrypt.getDesKey().getEncoded() != null; //test the resulting ciphers System.out.println("Provider:" + encrypt.getAsymCipher().getProvider()); assert encrypt.getAsymCipher() != null; assert encrypt.getSymDecodingCipher() != null; assert encrypt.getSymEncodingCipher() != null; } public static void testInitBCAsymProperties() throws Exception { ENCRYPT encrypt=new ENCRYPT(); encrypt.asymAlgorithm = "RSA"; // encrypt.asymProvider = "BC"; encrypt.init(); // test the default asymetric key assert "RSA".equals(encrypt.getAsymAlgorithm()); assert encrypt.getAsymInit() == 512; assert "RSA".equals(encrypt.getKpair().getPublic().getAlgorithm()); //Strangely this returns differently from the default provider for RSA which is also BC! assert "X.509".equals(encrypt.getKpair().getPublic().getFormat()); assert encrypt.getKpair().getPublic().getEncoded() != null; //test the resulting ciphers assert encrypt.getAsymCipher() != null; } @Test(expectedExceptions=Exception.class) public static void testInitIDEAProperties() throws Exception { ENCRYPT encrypt=new ENCRYPT(); encrypt.symAlgorithm = "IDEA"; encrypt.symInit = 128; encrypt.init(); } public static void testInitAESProperties() throws Exception { ENCRYPT encrypt=new ENCRYPT(); encrypt.symAlgorithm = "AES"; encrypt.symInit = 128; encrypt.init(); // test the default symetric key assert "AES".equals(encrypt.getSymAlgorithm()) : "expected AES but was " + encrypt.getSymAlgorithm(); Util.assertEquals(128, encrypt.getSymInit()); Util.assertEquals("AES", encrypt.getDesKey().getAlgorithm()); Util.assertEquals("RAW", encrypt.getDesKey().getFormat()); Util.assertNotNull(encrypt.getDesKey().getEncoded()); //test the resulting ciphers Util.assertNotNull(encrypt.getSymDecodingCipher()); Util.assertNotNull(encrypt.getSymEncodingCipher()); } public static void testViewChangeBecomeKeyserver() throws Exception { // set up the peer ENCRYPT encrypt=new ENCRYPT(); encrypt.init(); // set in the observer MockAddress tempAddress=new MockAddress("encrypt"); encrypt.setLocal_addr(tempAddress); MockObserver observer=new MockObserver(); encrypt.setObserver(observer); // produce encrypted message Cipher cipher=encrypt.getSymEncodingCipher(); MessageDigest digest=MessageDigest.getInstance("MD5"); digest.reset(); digest.update(encrypt.getDesKey().getEncoded()); String symVersion=new String(digest.digest(), "UTF-8"); encrypt.keyServer=false; Message msg=new Message(); msg.setBuffer(cipher.doFinal("hello".getBytes())); msg.putHeader(ENCRYPT_ID, new EncryptHeader(EncryptHeader.ENCRYPT, symVersion)); Event evt=new Event(Event.MSG, msg); //pass in event to encrypt layer encrypt.up(evt); // assert that message is queued as we have no key Util.assertTrue(observer.getUpMessages().isEmpty()); // send a view change to trigger the become key server // we use the fact that our address is now the controller one Vector tempVector=new Vector(); tempVector.add(tempAddress); View tempView=new View(new ViewId(tempAddress, 1), tempVector); Event event=new Event(Event.VIEW_CHANGE, tempView); // this should have changed us to the key server encrypt.up(event); // send another encrypted message Message msg2=new Message(); msg2.setBuffer(cipher.doFinal("hello2".getBytes())); msg2.putHeader(ENCRYPT_ID, new EncryptHeader(EncryptHeader.ENCRYPT, symVersion)); // we should have three messages now in our observer // that are decrypted Event evt2=new Event(Event.MSG, msg2); encrypt.up(evt2); Util.assertEquals(3, observer.getUpMessages().size()); Event sent=(Event)observer.getUpMessages().get("message1"); Util.assertEquals("hello", new String(((Message)sent.getArg()).getBuffer())); sent=(Event)observer.getUpMessages().get("message2"); Util.assertEquals("hello2", new String(((Message)sent.getArg()).getBuffer())); } public static void testViewChangeNewKeyServer() throws Exception { // create peer and server ENCRYPT peer=new ENCRYPT(); peer.init(); ENCRYPT server=new ENCRYPT(); server.init(); // set up server server.keyServer=true; MockObserver serverObserver=new MockObserver(); server.setObserver(serverObserver); Address serverAddress=new MockAddress("server"); server.setLocal_addr(serverAddress); //set the server up as keyserver Vector serverVector=new Vector(); serverVector.add(serverAddress); View tempView=new View(new ViewId(serverAddress, 1), serverVector); Event serverEvent=new Event(Event.VIEW_CHANGE, tempView); server.up(serverEvent); // set up peer Address peerAddress=new MockAddress("peer"); peer.setLocal_addr(peerAddress); MockObserver peerObserver=new MockObserver(); peer.setObserver(peerObserver); peer.keyServer=false; MessageDigest digest=MessageDigest.getInstance("MD5"); digest.reset(); digest.update(server.getDesKey().getEncoded()); String symVersion=new String(digest.digest(), "UTF-8"); // encrypt and send an initial message to peer Cipher cipher=server.getSymEncodingCipher(); Message msg=new Message(); msg.setBuffer(cipher.doFinal("hello".getBytes())); msg.putHeader(ENCRYPT_ID, new EncryptHeader(EncryptHeader.ENCRYPT, symVersion)); Event evt=new Event(Event.MSG, msg); peer.up(evt); //assert that message is queued as we have no key from server Util.assertTrue(peerObserver.getUpMessages().isEmpty()); // send a view change where we are not the controller // send to peer - which should have peer2 as its key server peer.up(serverEvent); // assert that peer\ keyserver address is now set Util.assertEquals(serverAddress, peer.getKeyServerAddr()); // get the resulting message from the peer - should be a key request Event sent=(Event)peerObserver.getDownMessages().get("message0"); Util.assertEquals(((EncryptHeader)((Message)sent.getArg()).getHeader(ENCRYPT_ID)).getType(), EncryptHeader.KEY_REQUEST); Util.assertEquals(new String(((Message)sent.getArg()).getBuffer()), new String(peer.getKpair().getPublic().getEncoded())); // send this event to server server.up(sent); Event reply=(Event)serverObserver.getDownMessages().get("message1"); //assert that reply is the session key encrypted with peer's public key Util.assertEquals(((EncryptHeader)((Message)reply.getArg()).getHeader(ENCRYPT_ID)).getType(), EncryptHeader.SECRETKEY); assert !peer.getDesKey().equals(server.getDesKey()); // now send back to peer peer.up(reply); // assert that both now have same key Util.assertEquals(peer.getDesKey(), server.getDesKey()); // send another encrypted message to peer to test queue Message msg2=new Message(); msg2.setBuffer(cipher.doFinal("hello2".getBytes())); msg2.putHeader(ENCRYPT_ID, new EncryptHeader(EncryptHeader.ENCRYPT, symVersion)); Event evt2=new Event(Event.MSG, msg2); peer.up(evt2); // make sure we have the events now in the up layers Util.assertEquals(3, peerObserver.getUpMessages().size()); Event tempEvt=(Event)peerObserver.getUpMessages().get("message2"); Util.assertEquals("hello", new String(((Message)tempEvt.getArg()).getBuffer())); tempEvt=(Event)peerObserver.getUpMessages().get("message3"); Util.assertEquals("hello2", new String(((Message)tempEvt.getArg()).getBuffer())); } public static void testViewChangeNewKeyServerNewKey() throws Exception { // create peer and server ENCRYPT peer=new ENCRYPT(); peer.init(); ENCRYPT server=new ENCRYPT(); server.init(); ENCRYPT peer2=new ENCRYPT(); peer2.init(); // set up server server.keyServer=true; MockObserver serverObserver=new MockObserver(); server.setObserver(serverObserver); //set the local address and view change to simulate a started instance Address serverAddress=new MockAddress("server"); server.setLocal_addr(serverAddress); // set the server up as keyserver Vector serverVector=new Vector(); serverVector.add(serverAddress); View tempView=new View(new ViewId(serverAddress, 1), serverVector); Event serverEvent=new Event(Event.VIEW_CHANGE, tempView); server.up(serverEvent); // set up peer as if it has started but not recieved view change Address peerAddress=new MockAddress("peer"); peer.setLocal_addr(peerAddress); MockObserver peerObserver=new MockObserver(); peer.setObserver(peerObserver); peer.keyServer=false; // set up peer2 with server as key server Address peer2Address=new MockAddress("peer2"); peer2.setLocal_addr(peer2Address); MockObserver peer2Observer=new MockObserver(); peer2.setObserver(peer2Observer); peer2.keyServer=false; peer2.setKeyServerAddr(serverAddress); // send an encrypted message from the server Message msg=new Message(); msg.setBuffer("hello".getBytes()); Event evt=new Event(Event.MSG, msg); server.down(evt); // message0 is in response to view change Event encEvt=(Event)serverObserver.getDownMessages().get("message1"); // sent to peer encrypted - should be queued in encyption layer as we do not have a keyserver set peer.up(encEvt); //assert that message is queued as we have no key from server Util.assertTrue(peerObserver.getUpMessages().isEmpty()); // send a view change to peer where peer2 is controller Vector peerVector=new Vector(); peerVector.add(peer2Address); View tempPeerView=new View(new ViewId(peer2Address, 1), peerVector); Event event=new Event(Event.VIEW_CHANGE, tempPeerView); // send to peer - should set peer2 as keyserver peer.up(event); // assert that peer\ keyserver address is now set Util.assertEquals(peer2Address, peer.getKeyServerAddr()); // get the resulting message from the peer - should be a key request to peer2 Event sent=(Event)peerObserver.getDownMessages().get("message0"); // ensure type and that request contains peers pub key Util.assertEquals(((EncryptHeader)((Message)sent.getArg()).getHeader(ENCRYPT_ID)).getType(), EncryptHeader.KEY_REQUEST); Util.assertEquals(new String(((Message)sent.getArg()).getBuffer()), new String(peer.getKpair().getPublic().getEncoded())); //assume that server is no longer available and peer2 is new server // but did not get the key from server before assuming role // send this event to peer2 // send a view change to trigger the become key server // we use the fact that our address is now the controller one // send a view change where we are not the controller Vector peer2Vector=new Vector(); peer2Vector.add(peer2Address); View tempPeer2View=new View(new ViewId(peer2Address, 1), peer2Vector); Event event2=new Event(Event.VIEW_CHANGE, tempPeer2View); // this should have changed us to the key server peer2.up(event2); peer2.up(sent); Event reply=(Event)peer2Observer.getDownMessages().get("message1"); //assert that reply is the session key encrypted with peer's public key Util.assertEquals(((EncryptHeader)((Message)reply.getArg()).getHeader(ENCRYPT_ID)).getType(), EncryptHeader.SECRETKEY); assert !peer.getDesKey().equals(peer2.getDesKey()); assert !server.getDesKey().equals(peer2.getDesKey()); // now send back to peer peer.up(reply); // assert that both now have same key Util.assertEquals(peer.getDesKey(), peer2.getDesKey()); assert !server.getDesKey().equals(peer.getDesKey()); // send another encrypted message to peer to test queue Message msg2=new Message(); msg2.setBuffer("hello2".getBytes()); Event evt2=new Event(Event.MSG, msg2); peer2.down(evt2); Event Evt2=(Event)peer2Observer.getDownMessages().get("message2"); peer.up(Evt2); // make sure we have the events now in the up layers Util.assertEquals(2, peerObserver.getUpMessages().size()); Event tempEvt=(Event)peerObserver.getUpMessages().get("message2"); Util.assertEquals("hello2", new String(((Message)tempEvt.getArg()).getBuffer())); } static class MockObserver implements ENCRYPT.Observer { private Map upMessages=new HashMap(); private Map downMessages=new HashMap(); private int counter=0; /* (non-Javadoc) * @see org.jgroups.UpHandler#up(org.jgroups.Event) */ private void storeUp(Event evt) { upMessages.put("message" + counter++, evt); } private void storeDown(Event evt) { downMessages.put("message" + counter++, evt); } public void up(Event evt) { storeUp(evt); } public void setProtocol(Protocol prot) { } public void passUp(Event evt) { storeUp(evt); } public void down(Event evt) { } public void passDown(Event evt) { storeDown(evt); } protected Map getUpMessages() { return upMessages; } protected void setUpMessages(Map upMessages) { this.upMessages=upMessages; } protected Map getDownMessages() { return downMessages; } protected void setDownMessages(Map downMessages) { this.downMessages=downMessages; } } static class MockAddress implements Address { private static final long serialVersionUID=-479331506050129599L; String name; public MockAddress(String name) { this.name=name; } public MockAddress() { } public boolean isMulticastAddress() { return false; } public int size() { return 0; } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { } public void writeExternal(ObjectOutput out) throws IOException { } public int compareTo(Address o) { return -1; } public boolean equals(Object obj) { MockAddress address=(MockAddress)obj; return address.name.equals(this.name); } public void writeTo(DataOutputStream out) throws IOException { } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { } } } ././@LongLink0000000000000000000000000000015100000000000011562 Lustar rootrootlibjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/protocols/NAKACK_Delivery_Test.javalibjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/protocols/NAKACK_Delivery_Test.0000644000175000017500000001651611647260573033322 0ustar moellermoellerpackage org.jgroups.protocols; import org.jgroups.*; import org.jgroups.conf.ClassConfigurator; import org.jgroups.stack.Protocol; import org.jgroups.stack.NakReceiverWindow; import org.jgroups.protocols.pbcast.NAKACK; import org.jgroups.protocols.pbcast.NakAckHeader; import org.jgroups.util.*; import org.jgroups.util.UUID; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.*; import java.util.concurrent.*; /** * Tests whether a mix of OOB and regular messages (with duplicates), sent my multiple threads, are delivery * correctly. Correct delivery means: *

                  *
                • All messages are received exactly once (no duplicates and no missing messages) *
                • For regular messages only: all messages are received in the order in which they were sent (order of seqnos) *
                * @author Bela Ban */ @Test(groups=Global.FUNCTIONAL) public class NAKACK_Delivery_Test { private NAKACK nak; private Address c1, c2; static final short NAKACK_ID=ClassConfigurator.getProtocolId(NAKACK.class); MyReceiver receiver=new MyReceiver(); Executor pool; final static int NUM_MSGS=50; @BeforeMethod protected void setUp() throws Exception { c1=Util.createRandomAddress("C1"); c2=Util.createRandomAddress("C2"); nak=new NAKACK(); TP transport=new TP() { public boolean supportsMulticasting() {return false;} public void sendMulticast(byte[] data, int offset, int length) throws Exception {} public void sendUnicast(PhysicalAddress dest, byte[] data, int offset, int length) throws Exception {} public String getInfo() {return null;} public Object down(Event evt) {return null;} protected PhysicalAddress getPhysicalAddress() {return null;} public TimeScheduler getTimer() {return new DefaultTimeScheduler(1);} }; transport.setId((short)100); nak.setDownProtocol(transport); receiver.init(c1, c2); nak.setUpProtocol(receiver); nak.start(); Vector
                members=new Vector
                (2); members.add(c1); members.add(c2); View view=new View(c1, 1, members); // set the local address nak.down(new Event(Event.SET_LOCAL_ADDRESS, c1)); // set a dummy digest MutableDigest digest=new MutableDigest(2); digest.add(c1, 0, 0, 0); digest.add(c2, 0, 0, 0); nak.down(new Event(Event.SET_DIGEST, digest)); // set dummy view nak.down(new Event(Event.VIEW_CHANGE, view)); pool=new ThreadPoolExecutor(1, 100, 1000, TimeUnit.MILLISECONDS, new SynchronousQueue()); // pool=new DirectExecutor(); // if(pool instanceof ThreadPoolExecutor) ((ThreadPoolExecutor)pool).setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); } @AfterMethod protected void tearDown() { nak.stop(); if(pool instanceof ThreadPoolExecutor) ((ThreadPoolExecutor)pool).shutdownNow(); } /** * Sends NUM_MSGS (regular or OOB) multicasts on c1 and c2, checks that both c1 and c2 received NUM_MSGS messages. * This test doesn't use a transport, but injects messages directly into NAKACK. */ public void testSendingOfRandomMessages() { List seqnos=generateRandomNumbers(1, NUM_MSGS); seqnos.addAll(generateRandomNumbers(1, NUM_MSGS)); seqnos.addAll(generateRandomNumbers(Math.min(15, NUM_MSGS / 2), NUM_MSGS / 2)); seqnos.addAll(generateRandomNumbers(2, NUM_MSGS)); seqnos.addAll(generateRandomNumbers(5, Math.max(5, NUM_MSGS - 10))); Set no_duplicates=new HashSet(seqnos); System.out.println("sending " + seqnos.size() + " msgs (including duplicates); size excluding duplicates=" + no_duplicates.size()); // we need to add our own messages (nak is for C1), or else they will get discarded by NAKACK.handleMessage() NakReceiverWindow win=nak.getWindow(c1); for(int i=1; i <= NUM_MSGS; i++) win.add(i, msg(c1, i, i, true)); for(int i: seqnos) { boolean oob=Util.tossWeightedCoin(0.5); pool.execute(new Sender(c2, i, i, oob)); pool.execute(new Sender(c1, i, i, oob)); } ConcurrentMap> msgs=receiver.getMsgs(); Collection c1_list=msgs.get(c1); Collection c2_list=msgs.get(c2); long end_time=System.currentTimeMillis() + 10000; while(System.currentTimeMillis() < end_time) { int size_c1=c1_list.size(); int size_c2=c2_list.size(); System.out.println("size C1 = " + size_c1 + ", size C2=" + size_c2); if(size_c1 == NUM_MSGS && size_c2 == NUM_MSGS) break; Util.sleep(1000); } assert c1_list.size() == NUM_MSGS : "[C1] expected " + NUM_MSGS + " messages, but got " + c1_list.size(); assert c2_list.size() == NUM_MSGS : "[C2] expected " + NUM_MSGS + " messages, but got " + c2_list.size(); } private static List generateRandomNumbers(int from, int to) { List retval=new ArrayList(20); for(int i=from; i <= to; i++) retval.add(i); Collections.shuffle(retval); return retval; } private void send(Address sender, long seqno, int number, boolean oob) { assert sender != null; nak.up(new Event(Event.MSG, msg(sender, seqno, number, oob))); } private static Message msg(Address sender, long seqno, int number, boolean oob) { Message msg=new Message(null, sender, number); if(oob) msg.setFlag(Message.OOB); if(seqno != -1) msg.putHeader(NAKACK_ID, NakAckHeader.createMessageHeader(seqno)); return msg; } static class MyReceiver extends Protocol { final ConcurrentMap> msgs=new ConcurrentHashMap>(); public ConcurrentMap> getMsgs() { return msgs; } public void init(Address ... mbrs) { for(Address mbr: mbrs) { msgs.putIfAbsent(mbr, new ConcurrentLinkedQueue()); } } public Object up(Event evt) { if(evt.getType() == Event.MSG) { Message msg=(Message)evt.getArg(); Address sender=msg.getSrc(); Collection list=msgs.get(sender); if(list == null) { list=new ConcurrentLinkedQueue(); Collection tmp=msgs.putIfAbsent(sender, list); if(tmp != null) list=tmp; } list.add(msg); } return null; } } class Sender implements Runnable { final Address sender; final long seqno; final int number; final boolean oob; public Sender(Address sender, long seqno, int number, boolean oob) { this.sender=sender; this.seqno=seqno; this.number=number; this.oob=oob; } public void run() { send(sender, seqno, number, oob); } } }././@LongLink0000000000000000000000000000015300000000000011564 Lustar rootrootlibjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/protocols/NAKACK_SET_DIGEST_Test.javalibjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/protocols/NAKACK_SET_DIGEST_Tes0000644000175000017500000000541011647260573032756 0ustar moellermoellerpackage org.jgroups.protocols; import org.jgroups.*; import org.jgroups.protocols.pbcast.NAKACK; import org.jgroups.util.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; /** * Tests setting of digest NAKACK.down(SET_DIGEST), JIRA issue is https://jira.jboss.org/jira/browse/JGRP-1060 * @author Bela Ban */ @Test(groups=Global.FUNCTIONAL) public class NAKACK_SET_DIGEST_Test { private NAKACK nak; private MutableDigest d1, d2; private Address a, b, c; private static final short TP_ID=101; @BeforeMethod protected void setUp() throws Exception { a=Util.createRandomAddress("A"); b=Util.createRandomAddress("B"); c=Util.createRandomAddress("C"); nak=new NAKACK(); d1=new MutableDigest(2); d1.add(a, 0, 11, 11); d1.add(b, 0, 30, 35); d2=new MutableDigest(3); d2.add(a, 0, 10, 10); d2.add(b, 0, 30, 30); d2.add(c, 10, 50, 50); TP transport=new TP() { public boolean supportsMulticasting() {return false;} public void sendMulticast(byte[] data, int offset, int length) throws Exception {} public void sendUnicast(PhysicalAddress dest, byte[] data, int offset, int length) throws Exception {} public String getInfo() {return null;} public Object down(Event evt) {return null;} protected PhysicalAddress getPhysicalAddress() {return null;} public TimeScheduler getTimer() {return new DefaultTimeScheduler(1);} }; transport.setId(TP_ID); nak.setDownProtocol(transport); nak.start(); // View view=new View(a, 1, new Vector(Arrays.asList(new Address[]{a, b, c}))); // nak.down(new Event(Event.VIEW_CHANGE, view)); } @AfterMethod protected void tearDown() { nak.stop(); } 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); assert digest.size() == 3; assert digest.contains(a); assert digest.contains(b); assert 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); assert digest.size() == 3; // https://jira.jboss.org/jira/browse/JGRP-1060 assert digest.contains(a); assert digest.contains(b); assert digest.contains(c); } } ././@LongLink0000000000000000000000000000014600000000000011566 Lustar rootrootlibjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/protocols/NAKACK_StressTest.javalibjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/protocols/NAKACK_StressTest.jav0000644000175000017500000001727211647260573033364 0ustar moellermoellerpackage org.jgroups.protocols; import org.jgroups.*; import org.jgroups.conf.ClassConfigurator; import org.jgroups.protocols.pbcast.NAKACK; import org.jgroups.protocols.pbcast.NakAckHeader; import org.jgroups.stack.Protocol; import org.jgroups.util.*; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * Tests time for N threads to deliver M messages to NAKACK * @author Bela Ban */ @Test(groups=Global.FUNCTIONAL, sequential=true) public class NAKACK_StressTest { static final int NUM_MSGS=1000000; static final int NUM_THREADS=50; static final short NAKACK_ID=ClassConfigurator.getProtocolId(NAKACK.class); @DataProvider(name="createTimer") Object[][] createTimer() { return Util.createTimer(); } @Test(dataProvider="createTimer") public static void stressTest(TimeScheduler timer) { start(NUM_THREADS, NUM_MSGS, false, timer); } @Test(dataProvider="createTimer") public static void stressTestOOB(TimeScheduler timer) { start(NUM_THREADS, NUM_MSGS, true, timer); } private static void start(final int num_threads, final int num_msgs, boolean oob, TimeScheduler timer) { final NAKACK nak=new NAKACK(); final AtomicInteger counter=new AtomicInteger(num_msgs); final AtomicLong seqno=new AtomicLong(1); final AtomicInteger delivered_msgs=new AtomicInteger(0); final Lock lock=new ReentrantLock(); final Condition all_msgs_delivered=lock.newCondition(); final ConcurrentLinkedQueue delivered_msg_list=new ConcurrentLinkedQueue(); final Address local_addr=Util.createRandomAddress("A"); final Address sender=Util.createRandomAddress("B"); if(timer == null) timer=new TimeScheduler2(); nak.setTimer(timer); System.out.println("timer is a " + timer.getClass()); nak.setDownProtocol(new Protocol() {public Object down(Event evt) {return null;}}); nak.setUpProtocol(new Protocol() { public Object up(Event evt) { if(evt.getType() == Event.MSG) { delivered_msgs.incrementAndGet(); NakAckHeader hdr=(NakAckHeader)((Message)evt.getArg()).getHeader(NAKACK_ID); if(hdr != null) delivered_msg_list.add(hdr.getSeqno()); if(delivered_msgs.get() >= num_msgs) { lock.lock(); try { all_msgs_delivered.signalAll(); } finally { lock.unlock(); } } } return null; } }); nak.setDiscardDeliveredMsgs(true); nak.down(new Event(Event.SET_LOCAL_ADDRESS, local_addr)); nak.down(new Event(Event.BECOME_SERVER)); View view=new View(local_addr, 1, Arrays.asList(local_addr, sender)); nak.down(new Event(Event.VIEW_CHANGE, view)); MutableDigest digest=new MutableDigest(); digest.add(local_addr, 0, 0, 0); digest.add(sender, 0, 0, 0); nak.down(new Event(Event.SET_DIGEST, digest)); final CountDownLatch latch=new CountDownLatch(1); Sender[] adders=new Sender[num_threads]; for(int i=0; i < adders.length; i++) { adders[i]=new Sender(nak, latch, counter, seqno, oob, sender); adders[i].start(); } long start=System.currentTimeMillis(); latch.countDown(); // starts all adders lock.lock(); try { while(delivered_msgs.get() < num_msgs) { try { all_msgs_delivered.await(1000, TimeUnit.MILLISECONDS); System.out.println("received " + delivered_msgs.get() + " msgs"); } catch(InterruptedException e) { e.printStackTrace(); } } } finally { lock.unlock(); } long time=System.currentTimeMillis() - start; double requests_sec=num_msgs / (time / 1000.0); System.out.println("\nTime: " + time + " ms, " + Util.format(requests_sec) + " requests / sec\n"); System.out.println("Delivered messages: " + delivered_msg_list.size()); if(delivered_msg_list.size() < 100) System.out.println("Elements: " + delivered_msg_list); nak.stop(); timer.stop(); List results=new ArrayList(delivered_msg_list); if(oob) Collections.sort(results); assert results.size() == num_msgs : "expected " + num_msgs + ", but got " + results.size(); System.out.println("Checking results consistency"); int i=1; for(Long num: results) { if(num.longValue() != i) { assert i == num : "expected " + i + " but got " + num; return; } i++; } System.out.println("OK"); } private static Message createMessage(Address dest, Address src, long seqno, boolean oob) { Message msg=new Message(dest, src, "hello world"); NakAckHeader hdr=NakAckHeader.createMessageHeader(seqno) ; msg.putHeader(NAKACK_ID, hdr); if(oob) msg.setFlag(Message.OOB); return msg; } static class Sender extends Thread { final NAKACK nak; final CountDownLatch latch; final AtomicInteger num_msgs; final AtomicLong current_seqno; final boolean oob; final Address sender; public Sender(NAKACK nak, CountDownLatch latch, AtomicInteger num_msgs, AtomicLong current_seqno, boolean oob, final Address sender) { this.nak=nak; this.latch=latch; this.num_msgs=num_msgs; this.current_seqno=current_seqno; this.oob=oob; this.sender=sender; setName("Adder"); } public void run() { try { latch.await(); } catch(InterruptedException e) { e.printStackTrace(); return; } while(num_msgs.getAndDecrement() > 0) { long seqno=current_seqno.getAndIncrement(); Message msg=createMessage(null, sender, seqno, oob); nak.up(new Event(Event.MSG, msg)); } } } @Test(enabled=false) public static void main(String[] args) { int num_threads=10; int num_msgs=1000000; boolean oob=false; for(int i=0; i < args.length; i++) { if(args[i].equals("-num_threads")) { num_threads=Integer.parseInt(args[++i]); continue; } if(args[i].equals("-num_msgs")) { num_msgs=Integer.parseInt(args[++i]); continue; } if(args[i].equals("-oob")) { oob=Boolean.parseBoolean(args[++i]); continue; } System.out.println("NAKACK_StressTest [-num_msgs msgs] [-num_threads threads] [-oob ]"); return; } start(num_threads, num_msgs, oob, null); } }././@LongLink0000000000000000000000000000015000000000000011561 Lustar rootrootlibjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/protocols/UNICAST2_StressTest.javalibjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/protocols/UNICAST2_StressTest.j0000644000175000017500000002033111647260573033263 0ustar moellermoellerpackage org.jgroups.protocols; import org.jgroups.Address; import org.jgroups.Event; import org.jgroups.Global; import org.jgroups.Message; import org.jgroups.conf.ClassConfigurator; import org.jgroups.stack.Protocol; import org.jgroups.util.TimeScheduler; import org.jgroups.util.TimeScheduler2; import org.jgroups.util.Util; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * Tests time for N threads to deliver M messages to UNICAST2 * @author Bela Ban */ @Test(groups=Global.FUNCTIONAL, sequential=true) public class UNICAST2_StressTest { static final int NUM_MSGS=1000000; static final int NUM_THREADS=50; static final int MAX_MSG_BATCH_SIZE=50000; static final short UNICAST_ID=ClassConfigurator.getProtocolId(UNICAST2.class); @DataProvider(name="createTimer") Object[][] createTimer() { return Util.createTimer(); } @Test(dataProvider="createTimer") public static void stressTest(TimeScheduler timer) { start(NUM_THREADS, NUM_MSGS, false, MAX_MSG_BATCH_SIZE, timer); } @Test(dataProvider="createTimer") public static void stressTestOOB(TimeScheduler timer) { start(NUM_THREADS, NUM_MSGS, true, MAX_MSG_BATCH_SIZE, timer); } private static void start(final int num_threads, final int num_msgs, boolean oob, int max_msg_batch_size, TimeScheduler timer) { final UNICAST2 unicast=new UNICAST2(); final AtomicInteger counter=new AtomicInteger(num_msgs); final AtomicLong seqno=new AtomicLong(1); final AtomicInteger delivered_msgs=new AtomicInteger(0); final Lock lock=new ReentrantLock(); final Condition all_msgs_delivered=lock.newCondition(); final ConcurrentLinkedQueue delivered_msg_list=new ConcurrentLinkedQueue(); final Address local_addr=Util.createRandomAddress(); final Address sender=Util.createRandomAddress(); if(timer == null) timer=new TimeScheduler2(); unicast.setTimer(timer); System.out.println("timer is a " + timer.getClass()); unicast.setDownProtocol(new Protocol() { public Object down(Event evt) {return null;} }); unicast.setUpProtocol(new Protocol() { public Object up(Event evt) { if(evt.getType() == Event.MSG) { delivered_msgs.incrementAndGet(); UNICAST2.Unicast2Header hdr=(UNICAST2.Unicast2Header)((Message)evt.getArg()).getHeader(UNICAST_ID); if(hdr != null) delivered_msg_list.add(hdr.getSeqno()); if(delivered_msgs.get() >= num_msgs) { lock.lock(); try { all_msgs_delivered.signalAll(); } finally { lock.unlock(); } } } return null; } }); unicast.down(new Event(Event.SET_LOCAL_ADDRESS, local_addr)); unicast.setMaxMessageBatchSize(max_msg_batch_size); unicast.setValue("max_bytes", 20000); // send the first message manually, to initialize the AckReceiverWindow tables Message msg=createMessage(local_addr, sender, 1L, oob, true); unicast.up(new Event(Event.MSG, msg)); Util.sleep(500); final CountDownLatch latch=new CountDownLatch(1); Sender[] adders=new Sender[num_threads]; for(int i=0; i < adders.length; i++) { adders[i]=new Sender(unicast, latch, counter, seqno, oob, local_addr, sender); adders[i].start(); } long start=System.currentTimeMillis(); latch.countDown(); // starts all adders lock.lock(); try { while(delivered_msgs.get() < num_msgs) { try { all_msgs_delivered.await(1000, TimeUnit.MILLISECONDS); System.out.println("received " + delivered_msgs.get() + " msgs"); } catch(InterruptedException e) { e.printStackTrace(); } } } finally { lock.unlock(); } long time=System.currentTimeMillis() - start; double requests_sec=num_msgs / (time / 1000.0); System.out.println("\nTime: " + time + " ms, " + Util.format(requests_sec) + " requests / sec\n"); System.out.println("Delivered messages: " + delivered_msg_list.size()); if(delivered_msg_list.size() < 100) System.out.println("Elements: " + delivered_msg_list); unicast.stop(); timer.stop(); List results=new ArrayList(delivered_msg_list); if(oob) Collections.sort(results); assert results.size() == num_msgs : "expected " + num_msgs + ", but got " + results.size(); System.out.println("Checking results consistency"); int i=1; for(Long num: results) { if(num.longValue() != i) { assert i == num : "expected " + i + " but got " + num; return; } i++; } System.out.println("OK"); } private static Message createMessage(Address dest, Address src, long seqno, boolean oob, boolean first) { Message msg=new Message(dest, src, "hello world"); UNICAST2.Unicast2Header hdr=UNICAST2.Unicast2Header.createDataHeader(seqno, (short)1, first); msg.putHeader(UNICAST_ID, hdr); if(oob) msg.setFlag(Message.OOB); return msg; } static class Sender extends Thread { final UNICAST2 unicast; final CountDownLatch latch; final AtomicInteger num_msgs; final AtomicLong current_seqno; final boolean oob; final Address dest; final Address sender; public Sender(UNICAST2 unicast, CountDownLatch latch, AtomicInteger num_msgs, AtomicLong current_seqno, boolean oob, final Address dest, final Address sender) { this.unicast=unicast; this.latch=latch; this.num_msgs=num_msgs; this.current_seqno=current_seqno; this.oob=oob; this.dest=dest; this.sender=sender; setName("Adder"); } public void run() { try { latch.await(); } catch(InterruptedException e) { e.printStackTrace(); return; } while(num_msgs.getAndDecrement() > 0) { long seqno=current_seqno.getAndIncrement(); Message msg=createMessage(dest, sender, seqno, oob, false); unicast.up(new Event(Event.MSG, msg)); } } } @Test(enabled=false) public static void main(String[] args) { int num_threads=10; int num_msgs=1000000; int max=20000; boolean oob=false; for(int i=0; i < args.length; i++) { if(args[i].equals("-num_threads")) { num_threads=Integer.parseInt(args[++i]); continue; } if(args[i].equals("-num_msgs")) { num_msgs=Integer.parseInt(args[++i]); continue; } if(args[i].equals("-oob")) { oob=Boolean.parseBoolean(args[++i]); continue; } if(args[i].equals("-max")) { max=Integer.parseInt(args[++i]); continue; } System.out.println("UNICAST2_StressTest [-num_msgs msgs] [-num_threads threads] " + "[-oob ] [-max ]"); return; } start(num_threads, num_msgs, oob, max, null); } }././@LongLink0000000000000000000000000000014700000000000011567 Lustar rootrootlibjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/protocols/UNICAST_StressTest.javalibjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/protocols/UNICAST_StressTest.ja0000644000175000017500000001754111647260573033353 0ustar moellermoellerpackage org.jgroups.protocols; import org.jgroups.Address; import org.jgroups.Event; import org.jgroups.Global; import org.jgroups.Message; import org.jgroups.conf.ClassConfigurator; import org.jgroups.stack.Protocol; import org.jgroups.util.Util; import org.testng.annotations.Test; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * Tests time for N threads to deliver M messages to UNICAST * @author Bela Ban */ @Test(groups=Global.FUNCTIONAL, sequential=true) public class UNICAST_StressTest { static final int NUM_MSGS=1000000; static final int NUM_THREADS=50; static final int MAX_MSG_BATCH_SIZE=50000; static final short UNICAST_ID=ClassConfigurator.getProtocolId(UNICAST.class); @Test public static void stressTest() { start(NUM_THREADS, NUM_MSGS, false, MAX_MSG_BATCH_SIZE); } @Test public static void stressTestOOB() { start(NUM_THREADS, NUM_MSGS, true, MAX_MSG_BATCH_SIZE); } private static void start(final int num_threads, final int num_msgs, boolean oob, int max_msg_batch_size) { final UNICAST unicast=new UNICAST(); final AtomicInteger counter=new AtomicInteger(num_msgs); final AtomicLong seqno=new AtomicLong(1); final AtomicInteger delivered_msgs=new AtomicInteger(0); final Lock lock=new ReentrantLock(); final Condition all_msgs_delivered=lock.newCondition(); final ConcurrentLinkedQueue delivered_msg_list=new ConcurrentLinkedQueue(); final Address local_addr=Util.createRandomAddress("A"); final Address sender=Util.createRandomAddress("B"); unicast.setDownProtocol(new Protocol() { public Object down(Event evt) { return null; } }); unicast.setUpProtocol(new Protocol() { public Object up(Event evt) { if(evt.getType() == Event.MSG) { delivered_msgs.incrementAndGet(); UNICAST.UnicastHeader hdr=(UNICAST.UnicastHeader)((Message)evt.getArg()).getHeader(UNICAST_ID); if(hdr != null) delivered_msg_list.add(hdr.getSeqno()); if(delivered_msgs.get() >= num_msgs) { lock.lock(); try { all_msgs_delivered.signalAll(); } finally { lock.unlock(); } } } return null; } }); unicast.down(new Event(Event.SET_LOCAL_ADDRESS, local_addr)); unicast.setMaxMessageBatchSize(max_msg_batch_size); // send the first message manually, to initialize the AckReceiverWindow tables Message msg=createMessage(local_addr, sender, 1L, oob, true); unicast.up(new Event(Event.MSG, msg)); Util.sleep(500); final CountDownLatch latch=new CountDownLatch(1); Adder[] adders=new Adder[num_threads]; for(int i=0; i < adders.length; i++) { adders[i]=new Adder(unicast, latch, counter, seqno, oob, local_addr, sender); adders[i].start(); } long start=System.currentTimeMillis(); latch.countDown(); // starts all adders lock.lock(); try { while(delivered_msgs.get() < num_msgs) { try { all_msgs_delivered.await(1000, TimeUnit.MILLISECONDS); System.out.println("received " + delivered_msgs.get() + " msgs"); // send a spurious message to trigger removal of pending messages in AckReceiverWindow msg=createMessage(local_addr, sender, 1L, oob, false); unicast.up(new Event(Event.MSG, msg)); } catch(InterruptedException e) { e.printStackTrace(); } } } finally { lock.unlock(); } long time=System.currentTimeMillis() - start; double requests_sec=num_msgs / (time / 1000.0); System.out.println("\nTime: " + time + " ms, " + Util.format(requests_sec) + " requests / sec\n"); System.out.println("Delivered messages: " + delivered_msg_list.size()); if(delivered_msg_list.size() < 100) System.out.println("Elements: " + delivered_msg_list); List results=new ArrayList(delivered_msg_list); if(oob) Collections.sort(results); assert results.size() == num_msgs : "expected " + num_msgs + ", but got " + results.size(); System.out.println("Checking results consistency"); int i=1; for(Long num: results) { if(num.longValue() != i) { assert i == num : "expected " + i + " but got " + num; return; } i++; } System.out.println("OK"); } private static Message createMessage(Address dest, Address src, long seqno, boolean oob, boolean first) { Message msg=new Message(dest, src, "hello world"); UNICAST.UnicastHeader hdr=UNICAST.UnicastHeader.createDataHeader(seqno, (short)1, first); msg.putHeader(UNICAST_ID, hdr); if(oob) msg.setFlag(Message.OOB); return msg; } static class Adder extends Thread { final UNICAST unicast; final CountDownLatch latch; final AtomicInteger num_msgs; final AtomicLong current_seqno; final boolean oob; final Address dest; final Address sender; public Adder(UNICAST unicast, CountDownLatch latch, AtomicInteger num_msgs, AtomicLong current_seqno, boolean oob, final Address dest, final Address sender) { this.unicast=unicast; this.latch=latch; this.num_msgs=num_msgs; this.current_seqno=current_seqno; this.oob=oob; this.dest=dest; this.sender=sender; } public void run() { try { latch.await(); } catch(InterruptedException e) { e.printStackTrace(); return; } while(num_msgs.getAndDecrement() > 0) { long seqno=current_seqno.getAndIncrement(); Message msg=createMessage(dest, sender, seqno, oob, false); unicast.up(new Event(Event.MSG, msg)); } } } @Test(enabled=false) public static void main(String[] args) { int num_threads=10; int num_msgs=1000000; int max=20000; boolean oob=false; for(int i=0; i < args.length; i++) { if(args[i].equals("-num_threads")) { num_threads=Integer.parseInt(args[++i]); continue; } if(args[i].equals("-num_msgs")) { num_msgs=Integer.parseInt(args[++i]); continue; } if(args[i].equals("-oob")) { oob=Boolean.parseBoolean(args[++i]); continue; } if(args[i].equals("-max")) { max=Integer.parseInt(args[++i]); continue; } System.out.println("UNICAST_StressTest [-num_msgs msgs] [-num_threads threads] " + "[-oob ] [-max ]"); return; } start(num_threads, num_msgs, oob, max); } }libjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/tests/0000755000175000017500000000000011647260573026634 5ustar moellermoellerlibjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/tests/AckCollectorTest.java0000644000175000017500000001344711647260573032715 0ustar moellermoeller package org.jgroups.tests; import org.jgroups.Address; import org.jgroups.Global; import org.jgroups.TimeoutException; import org.jgroups.util.AckCollector; import org.jgroups.util.Util; import org.testng.Assert; import org.testng.annotations.Test; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @Test(groups=Global.FUNCTIONAL) public class AckCollectorTest { final List list=Arrays.asList("one", "two", "three", "four", "five"); public void testConstructor() { AckCollector ac=new AckCollector(null, list); System.out.println("AckCollector is " + ac); Assert.assertEquals(5, ac.size()); } public void testWaitForAllAcksNoTimeout() { final AckCollector ac=new AckCollector(null, list); 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(); Assert.assertEquals(0, ac.size()); } @Test(expectedExceptions=TimeoutException.class) public void testWaitForAllAcksWithTimeoutException() throws TimeoutException { AckCollector ac=new AckCollector(null, list); ac.waitForAllAcks(200); } public void testWaitForAllAcksWithTimeout() { final AckCollector ac=new AckCollector(null, list); 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(5000); assert true : "we should not get a timeout exception here"; } catch(TimeoutException e) { assert false : "we should not get a timeout exception here"; } Assert.assertEquals(0, ac.size()); } @Test(expectedExceptions=TimeoutException.class) public void testWaitForAllAcksWithTimeoutException2() throws TimeoutException { final AckCollector ac=new AckCollector(null, list); new Thread() { public void run() { ac.ack("one"); System.out.println("AckCollector: " + ac); Util.sleep(200); ac.ack("two"); System.out.println("AckCollector: " + ac); Util.sleep(200); ac.ack("three"); System.out.println("AckCollector: " + ac); Util.sleep(200); ac.ack("four"); System.out.println("AckCollector: " + ac); Util.sleep(200); ac.ack("five"); System.out.println("AckCollector: " + ac); } }.start(); ac.waitForAllAcks(300); } @Test(expectedExceptions=TimeoutException.class) public void testReset() throws TimeoutException { final AckCollector ac=new AckCollector(null, list); final List new_list=Arrays.asList("six", "seven", "eight"); 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); ac.waitForAllAcks(1000); System.out.println("new AckCollector: " + ac); } public void testReset2() throws TimeoutException { final AckCollector ac=new AckCollector(null, list); final List new_list=Arrays.asList("six", "seven", "eight"); 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(5000); System.out.println("new AckCollector: " + ac); } public static void testNullList() throws TimeoutException { AckCollector coll=new AckCollector(); coll.waitForAllAcks(1000); } public static void testOneList() throws TimeoutException, UnknownHostException { List tmp=new ArrayList(); Address addr=Util.createRandomAddress(); tmp.add(addr); AckCollector coll=new AckCollector(null, tmp); coll.ack(addr); coll.waitForAllAcks(1000); } } ././@LongLink0000000000000000000000000000015100000000000011562 Lustar rootrootlibjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/tests/AckMcastSenderWindowTest.javalibjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/tests/AckMcastSenderWindowTest.0000644000175000017500000001636311647260573033525 0ustar moellermoellerpackage org.jgroups.tests; import org.jgroups.Address; import org.jgroups.Message; import org.jgroups.Global; import org.jgroups.stack.AckMcastSenderWindow; import org.jgroups.stack.IpAddress; import org.jgroups.stack.StaticInterval; import org.testng.Assert; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; 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. */ @Test(groups=Global.FUNCTIONAL,sequential=true) public class AckMcastSenderWindowTest { /** * 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(); @BeforeMethod void setUp() throws Exception { _cmd=new Cmd(); _win=new AckMcastSenderWindow(_cmd); } @AfterMethod void tearDown() throws Exception { _win.stop(); } /** * 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(); } public static 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)); Assert.assertEquals(3, mywin.getNumberOfResponsesExpected(seqno)); Assert.assertEquals(2, mywin.getNumberOfResponsesReceived(seqno)); mywin.waitUntilAllAcksReceived(4000); mywin.suspect(sender3); Assert.assertEquals(0, mywin.size()); } 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(); } } /** * 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)) assert false : "Acknowledging a non-existent msg, great!"; else System.out.println("retransmitting " + seqno + ", msg=" + msg); } static class MyCommand implements AckMcastSenderWindow.RetransmitCommand { public void retransmit(long seqno, Message msg, Address dest) { System.out.println("-- retransmitting " + seqno); } } } ././@LongLink0000000000000000000000000000015400000000000011565 Lustar rootrootlibjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/tests/AckReceiverWindowStressTest.javalibjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/tests/AckReceiverWindowStressTe0000644000175000017500000001240611647260573033632 0ustar moellermoellerpackage org.jgroups.tests; import org.jgroups.stack.AckReceiverWindow; import org.jgroups.Message; import org.jgroups.Global; import org.jgroups.util.Util; import org.jgroups.util.Tuple; import org.testng.annotations.Test; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicBoolean; import java.util.List; /** * Tests time for N threads to insert and remove M messages into an AckReceiverWindow * @author Bela Ban */ @Test(groups=Global.FUNCTIONAL, sequential=true) public class AckReceiverWindowStressTest { static final int NUM_MSGS=1000000; static final int NUM_THREADS=50; static final int SEGMENT_SIZE=50000; @Test public static void stressTest() { start(NUM_THREADS, NUM_MSGS, SEGMENT_SIZE); } private static void start(int num_threads, int num_msgs, int segment_size) { final AckReceiverWindow win=new AckReceiverWindow(1, segment_size); final AtomicInteger counter=new AtomicInteger(num_msgs); final AtomicLong seqno=new AtomicLong(1); final AtomicInteger removed_msgs=new AtomicInteger(0); final CountDownLatch latch=new CountDownLatch(1); Adder[] adders=new Adder[num_threads]; for(int i=0; i < adders.length; i++) { adders[i]=new Adder(win, latch, counter, seqno, removed_msgs); adders[i].start(); } long start=System.currentTimeMillis(); latch.countDown(); // starts all adders for(Adder adder: adders) { try { adder.join(); } catch(InterruptedException e) { e.printStackTrace(); } } for(int i=0; i < 50; i++) { if(removed_msgs.get() >= num_msgs) break; else { System.out.println("removed: " + removed_msgs.get()); Util.sleep(100); List msgs=win.removeManyAsList(segment_size); if(msgs != null && !msgs.isEmpty()) removed_msgs.addAndGet(msgs.size()); } } long time=System.currentTimeMillis() - start; double requests_sec=num_msgs / (time / 1000.0); System.out.println("\nTime: " + time + " ms, " + Util.format(requests_sec) + " requests / sec\n"); System.out.println("Total removed messages: " + removed_msgs); assert removed_msgs.get() == num_msgs : "removed messages (" + removed_msgs.get() + ") != num_msgs (" + num_msgs + ")"; } static class Adder extends Thread { final AckReceiverWindow win; final CountDownLatch latch; final AtomicInteger num_msgs; final AtomicLong current_seqno; final AtomicInteger removed_msgs; final static AtomicBoolean processing=new AtomicBoolean(false); public Adder(AckReceiverWindow win, CountDownLatch latch, AtomicInteger num_msgs, AtomicLong current_seqno, AtomicInteger removed_msgs) { this.win=win; this.latch=latch; this.num_msgs=num_msgs; this.current_seqno=current_seqno; this.removed_msgs=removed_msgs; } public void run() { try { latch.await(); } catch(InterruptedException e) { e.printStackTrace(); return; } Message msg=new Message(false); while(num_msgs.getAndDecrement() > 0) { long seqno=current_seqno.getAndIncrement(); int result=win.add2(seqno, msg); if(result != 1) System.err.println("seqno " + seqno + " not added correctly"); // simulates UNICAST: all threads call add2() concurrently, but only 1 thread removes messages if(processing.compareAndSet(false, true)) { try { while(true) { List msgs=win.removeManyAsList(20000); if(msgs == null || msgs.isEmpty()) break; removed_msgs.addAndGet(msgs.size()); } } finally { processing.set(false); } } } } } @Test(enabled=false) public static void main(String[] args) { int num_threads=50; int num_msgs=1000000; int segment_size=20000; for(int i=0; i < args.length; i++) { if(args[i].equals("-num_threads")) { num_threads=Integer.parseInt(args[++i]); continue; } if(args[i].equals("-num_msgs")) { num_msgs=Integer.parseInt(args[++i]); continue; } if(args[i].equals("-segment_size")) { segment_size=Integer.parseInt(args[++i]); continue; } System.out.println("AckReceiverWindowStressTest [-num_msgs msgs] [-num_threads threads] [-segment_size ]"); return; } start(num_threads, num_msgs, segment_size); } } ././@LongLink0000000000000000000000000000014600000000000011566 Lustar rootrootlibjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/tests/AckReceiverWindowTest.javalibjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/tests/AckReceiverWindowTest.jav0000644000175000017500000002332111647260573033552 0ustar moellermoellerpackage org.jgroups.tests; import org.jgroups.Global; import org.jgroups.Message; import org.jgroups.util.Util; import org.jgroups.util.Tuple; import org.jgroups.stack.AckReceiverWindow; import org.testng.Assert; import org.testng.annotations.Test; import org.testng.annotations.BeforeMethod; import org.testng.annotations.AfterMethod; import java.util.concurrent.CountDownLatch; import java.util.List; import java.util.LinkedList; import java.util.concurrent.atomic.AtomicInteger; /** * @author Bela Ban */ @Test(groups=Global.FUNCTIONAL,sequential=true) public class AckReceiverWindowTest { AckReceiverWindow win; @BeforeMethod public void setUp() throws Exception { win=new AckReceiverWindow(1); } @AfterMethod public void tearDown() throws Exception { win.reset(); } public void testAdd() { Message m; win.reset(); // use a different window for this test method win=new AckReceiverWindow(10); Assert.assertEquals(0, win.size()); win.add(9, msg()); Assert.assertEquals(0, win.size()); win.add(10, msg()); Assert.assertEquals(1, win.size()); win.add(13, msg()); Assert.assertEquals(2, win.size()); m=win.remove(); assert m != null; Assert.assertEquals(1, win.size()); m=win.remove(); assert m == null; Assert.assertEquals(1, win.size()); win.add(11, msg()); win.add(12, msg()); Assert.assertEquals(3, win.size()); m=win.remove(); assert m != null; m=win.remove(); assert m != null; m=win.remove(); assert m != null; Assert.assertEquals(0, win.size()); m=win.remove(); assert m == null; } public void testAddExisting() { win.add(1, msg()); assert win.size() == 1; win.add(1, msg()); assert win.size() == 1; win.add(2, msg()); assert win.size() == 2; } public void testAddLowerThanNextToRemove() { win.add(1, msg()); win.add(2, msg()); win.remove(); // next_to_remove is 2 win.add(1, msg()); assert win.size() == 1; } public void testRemove() { win.add(2, msg()); Message msg=win.remove(); assert msg == null; assert win.size() == 1; win.add(1, msg()); assert win.size() == 2; assert win.remove() != null; assert win.size() == 1; assert win.remove() != null; assert win.size() == 0; assert win.remove() == null; } public void testDuplicates() { Assert.assertEquals(0, win.size()); assert win.add(9, msg()); assert win.add(1, msg()); assert win.add(2, msg()); assert !win.add(2, msg()); assert !win.add(0, msg()); assert win.add(3, msg()); assert win.add(4, msg()); assert !win.add(4, msg()); } public static void testRemoveRegularMessages() { AckReceiverWindow win=new AckReceiverWindow(1); win.add(1, msg()); System.out.println("win = " + win); win.remove(); System.out.println("win = " + win); assert win.size() == 0; win.add(3, msg()); win.remove(); System.out.println("win = " + win); assert win.size() == 1; win.add(2, msg(true)); win.remove(); System.out.println("win = " + win); assert win.size() == 1; } public static void testRemoveMany() { AckReceiverWindow win=new AckReceiverWindow(1); Tuple, Long> tuple=win.removeMany(100); assert tuple == null; win.add(2, msg()); win.add(4, msg()); tuple=win.removeMany(100); assert tuple == null; win.add(3, msg()); win.add(1, msg()); tuple=win.removeMany(10); List list=tuple.getVal1(); assert list.size() == 4; assert tuple.getVal2() == 4; } public static void testRemoveMany2() { AckReceiverWindow win=new AckReceiverWindow(1); for(int i=1; i <=50; i++) win.add(i, msg()); System.out.println("win = " + win); List list=win.removeManyAsList(25); System.out.println("win = " + win); assert list != null && list.size() == 25; assert win.size() == 25; list=win.removeManyAsList(30); System.out.println("win = " + win); assert list != null && list.size() == 25; assert win.size() == 0; } public static void testSmallerThanNextToRemove() { AckReceiverWindow win=new AckReceiverWindow(1); Message msg; for(long i=1; i <= 5; i++) win.add(i, msg()); System.out.println("win = " + win); boolean added=win.add(3, msg()); assert !added; for(;;) { msg=win.remove(); if(msg == null) break; } added=win.add(3, msg()); assert !added; } public static void testConcurrentAdds() throws InterruptedException { AckReceiverWindow win=new AckReceiverWindow(1); final int NUM=100; final int NUM_THREADS=10; final CountDownLatch latch=new CountDownLatch(1); final Adder[] adders=new Adder[NUM_THREADS]; for(int i=0; i < adders.length; i++) { adders[i]=new Adder(1, NUM, 10, win, latch); } for(Adder adder: adders) adder.start(); latch.countDown(); for(Adder adder: adders) adder.join(); System.out.println("win = " + win); assert win.size() == NUM; } @Test(invocationCount=10) public static void testConcurrentAddsAndRemoves() throws InterruptedException { AckReceiverWindow win=new AckReceiverWindow(1); final int NUM=100; final int NUM_THREADS=10; final CountDownLatch latch=new CountDownLatch(1); final Adder[] adders=new Adder[NUM_THREADS]; for(int i=0; i < adders.length; i++) { adders[i]=new Adder(1, NUM, 10, win, latch); adders[i].start(); } final AtomicInteger count=new AtomicInteger(NUM); final Remover[] removers=new Remover[NUM_THREADS]; for(int i=0; i < removers.length; i++) { removers[i]=new Remover(win, latch, count); removers[i].start(); } latch.countDown(); for(Adder adder: adders) adder.join(); int total=0; int index=1; for(Remover remover: removers) { remover.join(); List list=remover.getList(); System.out.println("remover #" + index++ + ": " + list.size() + " msgs"); total+=list.size(); } System.out.println("total = " + total + "\n"); if(total != NUM) { for(Remover remover: removers) { System.out.println(remover + ": " + print(remover.getList())); } } assert total == NUM; } private static String print(List list) { StringBuilder sb=new StringBuilder(); for(Message msg: list) { if(msg == AckReceiverWindow.TOMBSTONE) sb.append("T "); else sb.append(msg.getObject() + " "); } return sb.toString(); } private static Message msg() { return msg(false); } private static Message msg(long seqno) { return msg(false, seqno); } private static Message msg(boolean oob, long seqno) { Message retval=new Message(null, null, seqno); if(oob) retval.setFlag(Message.OOB); return retval; } private static Message msg(boolean oob) { Message retval=new Message(); if(oob) retval.setFlag(Message.OOB); return retval; } private static class Adder extends Thread { private final long from, to, duplicates; private final AckReceiverWindow win; private final CountDownLatch latch; public Adder(long from, long to, long duplicates, AckReceiverWindow win, CountDownLatch latch) { this.from=from; this.to=to; this.duplicates=duplicates; this.win=win; this.latch=latch; setName("Adder"); } public void run() { try { latch.await(); } catch(InterruptedException e) { e.printStackTrace(); } for(long i=from; i <= to; i++) { for(int j=0; j < duplicates; j++) { win.add(i, msg(true, i)); } } } } private static class Remover extends Thread { private final AckReceiverWindow win; private final CountDownLatch latch; private final List list=new LinkedList(); private final AtomicInteger count; public Remover(AckReceiverWindow win, CountDownLatch latch, final AtomicInteger count) { this.win=win; this.latch=latch; this.count=count; setName("Remover"); } public List getList() { return list; } public void run() { try { latch.await(); } catch(InterruptedException e) { e.printStackTrace(); return; } while(count.get() > 0) { Message msg=win.remove(); if(msg != null) { count.decrementAndGet(); list.add(msg); } else { Util.sleep(100); } } } } } libjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/tests/AckSenderWindowTest.java0000644000175000017500000002273311647260573033375 0ustar moellermoellerpackage org.jgroups.tests; import org.jgroups.Global; import org.jgroups.Message; import org.jgroups.stack.AckSenderWindow; import org.jgroups.stack.StaticInterval; import org.jgroups.util.DefaultTimeScheduler; import org.jgroups.util.TimeScheduler; import org.jgroups.util.Util; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** * Test cases for AckSenderWindow * @author Bela Ban */ @Test(groups=Global.FUNCTIONAL,sequential=true) public class AckSenderWindowTest { static final int NUM_MSGS=100; final static long[] xmit_timeouts={1000, 2000, 4000, 8000}; static final double PERCENTAGE_OFF=1.3; // how much can expected xmit_timeout and real timeout differ to still be okay ? final Map msgs=new ConcurrentHashMap(); // keys=seqnos (Long), values=Entries @DataProvider(name="provider") Object[][] provider() { final TimeScheduler timer=new DefaultTimeScheduler(10); return new Object[][] { {timer, new AckSenderWindow(new MyRetransmitCommand(), new StaticInterval(xmit_timeouts), timer)} }; } @Test(dataProvider="provider") public void testSimpleAdd(TimeScheduler timer, AckSenderWindow win) throws InterruptedException { try { for(int i=1; i <=5; i++) win.add(i, new Message()); System.out.println("win = " + win); assert win.size() == 5; win.ack(1); System.out.println("win = " + win); assert win.size() == 4; win.ack(4); System.out.println("win = " + win); assert win.size() == 1; win.ack(44); assert win.size() == 0; } finally { timer.stop(); win.reset(); } } /** Tests whether retransmits are called at correct times for 1000 messages */ @Test(dataProvider="provider") public void testRetransmits(TimeScheduler timer, AckSenderWindow win) throws InterruptedException { int num_non_correct_entries=0; try { // 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 all retransmits"); long end_time=System.currentTimeMillis() + 20000L, start=System.currentTimeMillis(); Util.sleep(1000); while(System.currentTimeMillis() < end_time) { // 3. Check whether all Entries have correct retransmission times num_non_correct_entries=checkEntries(false); if(num_non_correct_entries == 0) break; Util.sleep(2000L); } System.out.println("-- waited for " + (System.currentTimeMillis() - start) + " ms"); num_non_correct_entries=checkEntries(true); if(num_non_correct_entries > 0) System.err.println("Number of incorrect retransmission timeouts: " + num_non_correct_entries); assert num_non_correct_entries == 0; } finally { timer.stop(); win.reset(); } } @Test(dataProvider="provider") public void testLowest(TimeScheduler timer, AckSenderWindow win) { try { for(long i=1; i < 5; i++) win.add(i, new Message()); System.out.println("win = " + win + ", lowest=" + win.getLowest()); assert win.getLowest() == Global.DEFAULT_FIRST_UNICAST_SEQNO; win.ack(3); System.out.println("win = " + win + ", lowest=" + win.getLowest()); assert win.getLowest() == 4; win.ack(4); System.out.println("win = " + win + ", lowest=" + win.getLowest()); assert win.getLowest() == 5; win.ack(2); assert win.getLowest() == 5; } finally { timer.stop(); win.reset(); } } @Test(dataProvider="provider") public void testGetLowestMessage(TimeScheduler timer, AckSenderWindow win) { long[] seqnos={1,2,3,4,5}; final Message[] messages=new Message[]{new Message(),new Message(),new Message(),new Message(),new Message()}; try { for(int i=0; i < seqnos.length; i++) { win.add(seqnos[i], messages[i]); } System.out.println("win = " + win); Message msg=win.getLowestMessage(); assert messages[0] == msg; win.ack(2); msg=win.getLowestMessage(); assert messages[2] == msg; win.ack(7); msg=win.getLowestMessage(); assert msg == null; } finally { timer.stop(); win.reset(); } } @Test(dataProvider="provider") public void testAdd(TimeScheduler timer, AckSenderWindow win) { try { for(int i=1; i <= 10; i++) win.add(i, new Message()); System.out.println("win = " + win); assert win.size() == 10; win.ack(7); assert win.size() == 3; } finally { timer.stop(); win.reset(); } } @Test(dataProvider="provider") public void testAck(TimeScheduler timer, AckSenderWindow win) { try { for(int i=1; i <= 3; i++) win.add(i, new Message()); assert win.size() == 3; win.ack(1); assert win.size() == 2; win.ack(2); assert win.size() == 1; win.ack(3); assert win.size() == 0; } finally { timer.stop(); win.reset(); } } private int checkEntries(boolean print) { int retval=0; Entry entry; for(long i=0; i < NUM_MSGS; i++) { entry=msgs.get(new Long(i)); if(!entry.isCorrect(i, print)) { retval++; } } return retval; } static 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, boolean print) { long t; long expected; long diff, delta; t=first_xmit - start_time; expected=xmit_timeouts[0]; diff=Math.abs(expected - t); delta=(long)(expected * PERCENTAGE_OFF); if(diff <= delta) return true; t=second_xmit - first_xmit; expected=xmit_timeouts[1]; diff=Math.abs(expected - t); delta=(long)(expected * PERCENTAGE_OFF); if(diff <= delta) return true; t=third_xmit - second_xmit; expected=xmit_timeouts[2]; diff=Math.abs(expected - t); delta=(long)(expected * PERCENTAGE_OFF); if(diff <= delta) return true; t=fourth_xmit - third_xmit; expected=xmit_timeouts[3]; diff=Math.abs(expected - t); delta=(long)(expected * PERCENTAGE_OFF); if(diff <= delta) return true; if(print) { System.err.println("#" + seqno + ": " + this + ": (" + "entry is more than " + PERCENTAGE_OFF + " percentage off "); return false; } return true; } boolean isCorrect(long seqno) { return isCorrect(seqno, 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=msgs.get(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(); } } } } } libjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/tests/AgeOutCacheTest.java0000644000175000017500000000732411647260573032455 0ustar moellermoellerpackage org.jgroups.tests; import org.jgroups.Global; import org.jgroups.util.AgeOutCache; import org.jgroups.util.TimeScheduler; import org.jgroups.util.Util; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; /** * Test cases for AgeOutCache * * @author Bela Ban */ @Test(groups = Global.FUNCTIONAL,sequential=true) public class AgeOutCacheTest { @DataProvider(name="timerCreator") protected Object[][] timerCreator() { return Util.createTimer(); } @Test(dataProvider="timerCreator") public void testExpiration(TimeScheduler timer) { AgeOutCache cache=new AgeOutCache(timer, 500L, new AgeOutCache.Handler() { public void expired(Integer key) { System.out.println(key + " expired"); } }); for(int i = 1; i <= 5; i++) cache.add(i); System.out.println("cache:\n" + cache); int size=cache.size(); assert size == 5 : "size is " + size; for(int i=0; i < 30; i++) { if(cache.size() == 0) break; Util.sleep(1000); } System.out.println("cache:\n" + cache); size=cache.size(); assert size == 0 : "size is " + size; timer.stop(); } @Test(dataProvider="timerCreator") public void testRemoveAndExpiration(TimeScheduler timer) { AgeOutCache cache = new AgeOutCache(timer, 500L); for (int i = 1; i <= 5; i++) cache.add(i); System.out.println("cache:\n" + cache); cache.remove(3); cache.remove(5); cache.remove(6); // not existent System.out.println("cache:\n" + cache); assert cache.size() == 3 : "cache size should be 3 but is " + cache; for(int i=0; i < 10; i++) { if(cache.size() == 0) break; Util.sleep(500); } assert cache.size() == 0; timer.stop(); } @Test(dataProvider="timerCreator") public void testContains(TimeScheduler timer) { AgeOutCache cache = new AgeOutCache(timer, 5000L); for (int i = 1; i <= 5; i++) cache.add(i); System.out.println("cache:\n" + cache); assert cache.contains(3); cache.remove(3); System.out.println("cache:\n" + cache); assert !cache.contains(3); cache.clear(); assert cache.size() == 0; assert !cache.contains(4); timer.stop(); } @Test(dataProvider="timerCreator") public void testGradualExpiration(TimeScheduler timer) { AgeOutCache cache = new AgeOutCache(timer, 5000L, new AgeOutCache.Handler() { public void expired(Integer key) { System.out.println(key + " expired"); } }); for (int i = 1; i <= 10; i++) { cache.add(i); System.out.print("."); Util.sleep(1000); } System.out.println("\ncache:\n" + cache); int size = cache.size(); assert size < 10 && size > 0 : " size was " + size + ", but should have been < 10 or > 0"; timer.stop(); } }libjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/tests/BARRIERTest.java0000644000175000017500000001021611647260573031425 0ustar moellermoellerpackage org.jgroups.tests; import org.jgroups.*; 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.Protocol; import org.jgroups.util.UUID; import org.jgroups.util.Util; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.Vector; import java.util.concurrent.atomic.AtomicInteger; /** * Tests the BARRIER protocol * @author Bela Ban */ @Test(groups=Global.FUNCTIONAL, sequential=true) public class BARRIERTest { Address a1; Vector

                members; View v; Simulator s; BARRIER barrier_prot=new BARRIER(); PING bottom_prot; @BeforeMethod public void setUp() throws Exception { a1=UUID.randomUUID(); 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(); VIEW_SYNC view_sync=new VIEW_SYNC(); Protocol[] stack=new Protocol[]{view_sync, barrier_prot, bottom_prot}; s.setProtocolStack(stack); s.start(); } @AfterMethod public void tearDown() throws Exception { s.stop(); } public void testBlocking() { assert !barrier_prot.isClosed(); s.send(new Event(Event.CLOSE_BARRIER)); assert barrier_prot.isClosed(); s.send(new Event(Event.OPEN_BARRIER)); assert !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(2000); int num_in_flight_threads=barrier_prot.getNumberOfInFlightThreads(); assert num_in_flight_threads == 0; s.send(new Event(Event.OPEN_BARRIER)); Util.sleep(2000); num_in_flight_threads=barrier_prot.getNumberOfInFlightThreads(); assert num_in_flight_threads == 0; int received_msgs=receiver.getNumberOfReceivedMessages(); assert received_msgs == 5 : "expected " + 5 + " messages but got " + received_msgs; } 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 { AtomicInteger num_mgs_received=new AtomicInteger(0); public void receive(Event evt) { if(evt.getType() == Event.MSG) { if(num_mgs_received.incrementAndGet() % 1000 == 0) System.out.println("<== " + num_mgs_received.get()); } } public int getNumberOfReceivedMessages() { return num_mgs_received.get(); } } 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"); } } } } libjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/tests/BoundedListTest.java0000644000175000017500000000370611647260573032561 0ustar moellermoeller package org.jgroups.tests; import org.jgroups.util.BoundedList; import org.jgroups.Global; import org.testng.Assert; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; @Test(groups=Global.FUNCTIONAL) public class BoundedListTest { public static void testAdd() throws Exception { BoundedList list=new BoundedList(3); Assert.assertEquals(0, list.size()); list.add(new Integer(1)); System.out.println(list); Assert.assertEquals(1, list.size()); list.add(new Integer(2)); System.out.println(list); list.add(new Integer(3)); System.out.println(list); Assert.assertEquals(3, list.size()); list.add(new Integer(4)); System.out.println(list); Assert.assertEquals(3, list.size()); int tmp; tmp=list.removeFromHead().intValue(); Assert.assertEquals(2, tmp); tmp=list.removeFromHead().intValue(); Assert.assertEquals(3, tmp); tmp=list.removeFromHead().intValue(); Assert.assertEquals(4, tmp); } public static void testContains() throws Exception { BoundedList strlist=new BoundedList(3); 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); assert !(strlist.contains("Bela")); assert strlist.contains("Nicole"); assert strlist.contains("Michelle"); } public static void testWithManyElements() { BoundedList list=new BoundedList(3); for(int i=0; i < 100000; i++) { list.add(i); } System.out.println("list: " + list); Assert.assertEquals(3, list.size()); } } libjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/tests/ConfiguratorTest.java0000644000175000017500000001705211647260573033006 0ustar moellermoellerpackage org.jgroups.tests; import org.jgroups.Global; import org.jgroups.JChannel; import org.jgroups.conf.ProtocolConfiguration; import org.jgroups.stack.Configurator; import org.jgroups.stack.Protocol; import org.jgroups.stack.ProtocolStack; import org.testng.Assert; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.List; /** * Tests ProtocolStack.insertProtocol() and removeProtocol() * @author Bela Ban */ @Test(groups=Global.FUNCTIONAL,sequential=true) public class ConfiguratorTest { ProtocolStack stack; static final String props="UDP:PING:FD:pbcast.NAKACK(retransmit_timeouts=300,600):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"}; @BeforeMethod void setUp() throws Exception { JChannel mock_channel=new JChannel() {}; stack=new ProtocolStack(mock_channel); } public void testRemovalOfTop() throws Exception { stack.setup(Configurator.parseConfigurations(props)); Protocol prot=stack.removeProtocol("FC"); assert prot != null; List protocols=stack.getProtocols(); Assert.assertEquals(5, protocols.size()); assert protocols.get(0).getName().endsWith("UNICAST"); assert stack.getTopProtocol().getUpProtocol() != null; assert stack.getTopProtocol().getDownProtocol() != null; assert stack.getTopProtocol().getDownProtocol().getUpProtocol() != null; assert stack.getTopProtocol().getDownProtocol().getDownProtocol() != null; } public void testRemovalOfBottom() throws Exception { stack.setup(Configurator.parseConfigurations(props)); Protocol prot=stack.removeProtocol("UDP"); assert prot != null; List protocols=stack.getProtocols(); Assert.assertEquals(5, protocols.size()); assert protocols.get(protocols.size() -1).getName().endsWith("PING"); } public void testAddingAboveTop() throws Exception{ stack.setup(Configurator.parseConfigurations(props)); Protocol new_prot=(Protocol)Class.forName("org.jgroups.protocols.TRACE").newInstance(); stack.insertProtocol(new_prot, ProtocolStack.ABOVE, "FC"); List protocols=stack.getProtocols(); Assert.assertEquals(7, protocols.size()); assert protocols.get(0).getName().endsWith("TRACE"); assert stack.getTopProtocol().getUpProtocol() != null; assert stack.getTopProtocol().getDownProtocol() != null; assert stack.getTopProtocol().getDownProtocol().getUpProtocol() != null; assert stack.getTopProtocol().getDownProtocol().getDownProtocol() != null; } @Test(expectedExceptions={IllegalArgumentException.class}) public void testAddingBelowBottom() throws Exception{ stack.setup(Configurator.parseConfigurations(props)); Protocol new_prot=(Protocol)Class.forName("org.jgroups.protocols.TRACE").newInstance(); stack.insertProtocol(new_prot, ProtocolStack.BELOW, "UDP"); } public void testInsertion() throws Exception { stack.setup(Configurator.parseConfigurations(props)); List protocols=stack.getProtocols(); assert protocols != null; Assert.assertEquals(6, protocols.size()); for(int i=0; i < names.length; i++) { String name=names[i]; Protocol p=protocols.get(i); Assert.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(); Assert.assertEquals(7, protocols.size()); for(int i=0; i < below.length; i++) { String name=below[i]; Protocol p=protocols.get(i); Assert.assertEquals(name, p.getName()); } // remove Protocol prot=stack.removeProtocol("TRACE"); assert prot != null; protocols=stack.getProtocols(); Assert.assertEquals(6, protocols.size()); for(int i=0; i < names.length; i++) { String name=names[i]; Protocol p=protocols.get(i); Assert.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(); Assert.assertEquals(7, protocols.size()); for(int i=0; i < above.length; i++) { String name=above[i]; Protocol p=protocols.get(i); Assert.assertEquals(name, p.getName()); } } public static 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;" + "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;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):" + "pbcast.GMS(print_local_addr=true;view_bundling=true;join_timeout=3000):" + "FC(max_block_time=10000;max_credits=5000000;min_threshold=0.25):" + "FRAG2(frag_size=60000):" + "pbcast.STREAMING_STATE_TRANSFER(use_reading_thread=true)"; List ret=Configurator.parseConfigurations(config); System.out.println("config:\n" + ret); Assert.assertEquals(14, 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); Assert.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); Assert.assertEquals(4, ret.size()); } } ././@LongLink0000000000000000000000000000014600000000000011566 Lustar rootrootlibjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/tests/ConnectionMapUnitTest.javalibjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/tests/ConnectionMapUnitTest.jav0000644000175000017500000001477311647260573033607 0ustar moellermoeller package org.jgroups.tests; import org.jgroups.Address; import org.jgroups.Global; import org.jgroups.blocks.TCPConnectionMap; import org.jgroups.util.DefaultThreadFactory; import org.jgroups.util.Util; import org.testng.Assert; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; /** * @author Bela Ban */ @Test(groups=Global.FUNCTIONAL,sequential=true) public class ConnectionMapUnitTest { TCPConnectionMap ct1, ct2; static final int port1=15555, port2=16666; @BeforeMethod protected void setUp() throws Exception { ct1=new TCPConnectionMap("TCPConnectionMap1", new DefaultThreadFactory(Util.getGlobalThreadGroup(), "test", true), null, null, null, null, port1, port1); ct1.setUseSendQueues(false); ct1.start(); ct2=new TCPConnectionMap("TCPConnectionMap2", new DefaultThreadFactory(Util.getGlobalThreadGroup(), "test", true), null, null, null, null, port2, port2); ct2.setUseSendQueues(false); ct2.start(); } @AfterMethod void tearDown() throws Exception { if(ct1 != null) { ct1.stop(); ct1=null; } if(ct2 != null) { ct2.stop(); ct2=null; } } public void testSetup() { Assert.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.setReceiver(new TCPConnectionMap.Receiver() { public void receive(Address sender, byte[] data, int offset, int length) {} }); 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)"); Assert.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)"); Assert.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)"); Assert.assertEquals(r1.getNumExpected(), r1.getNumReceived()); } static void log(String msg) { System.out.println("-- [" + Thread.currentThread() + "]: " + msg); } static class MyReceiver implements TCPConnectionMap.Receiver { long num_expected=0, num_received=0, start_time=0, stop_time=0; boolean done=false, send_response=false; long modulo; TCPConnectionMap ct; MyReceiver(TCPConnectionMap 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) { } } } } } } libjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/tests/CreditMapTest.java0000644000175000017500000002073711647260573032220 0ustar moellermoellerpackage org.jgroups.tests; import org.jgroups.Address; import org.jgroups.Global; import org.jgroups.util.CreditMap; import org.jgroups.util.Util; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.List; import java.util.concurrent.CyclicBarrier; /** * Tests CreditMap * @author Bela Ban */ @Test(groups=Global.FUNCTIONAL,sequential=true) public class CreditMapTest { static Address a=Util.createRandomAddress("A"); static Address b=Util.createRandomAddress("B"); static Address c=Util.createRandomAddress("C"); static Address d=Util.createRandomAddress("D"); static long MAX_CREDITS=1000; protected CreditMap map; @BeforeMethod void create() { map=new CreditMap(MAX_CREDITS); } @AfterMethod void destroy() { map.clear(); } private void addAll() { map.putIfAbsent(a); map.putIfAbsent(b); map.putIfAbsent(c); map.putIfAbsent(d); } private void replenishAll(long credits) { map.replenish(a, credits); map.replenish(b, credits); map.replenish(c, credits); map.replenish(d, credits); } public void testSimpleDecrement() { addAll(); System.out.println("map:\n" + map); boolean rc=map.decrement(200, 4000); System.out.println("rc=" + rc + ", map:\n" + map); assert rc; assert map.getMinCredits() == MAX_CREDITS - 200; assert map.getAccumulatedCredits() == 200; rc=map.decrement(150, 100); System.out.println("\nrc=" + rc + ", map:\n" + map); assert rc; assert map.getMinCredits() == MAX_CREDITS - 200 - 150; assert map.getAccumulatedCredits() == 200 + 150; rc=map.decrement(300, 100); System.out.println("\nrc=" + rc + ", map:\n" + map); assert rc; assert map.getMinCredits() == MAX_CREDITS - 200 - 150 - 300; assert map.getAccumulatedCredits() == 200 + 150 + 300; rc=map.decrement(500, 100); System.out.println("\nrc=" + rc + ", map:\n" + map); assert !rc; assert map.getMinCredits() == MAX_CREDITS - 200 - 150 - 300; assert map.getAccumulatedCredits() == 200 + 150 + 300; } public void testDecrementAndReplenish() { testSimpleDecrement(); map.replenish(a, MAX_CREDITS); System.out.println("\nmap:\n" + map); assert map.getMinCredits() == MAX_CREDITS - 200 - 150 - 300; assert map.getAccumulatedCredits() == 0; map.replenish(b, MAX_CREDITS); map.replenish(c, MAX_CREDITS); System.out.println("\nmap:\n" + map); assert map.getMinCredits() == MAX_CREDITS - 200 - 150 - 300; assert map.getAccumulatedCredits() == 0; map.replenish(d, MAX_CREDITS); System.out.println("\nmap:\n" + map); assert map.getMinCredits() == MAX_CREDITS; assert map.getAccumulatedCredits() == 0; } public void testDecrementAndReplenish2() { map.putIfAbsent(a); map.decrement(200, 100); map.putIfAbsent(b); map.decrement(200, 100); map.putIfAbsent(c); map.decrement(200, 100); map.putIfAbsent(d); map.decrement(200, 100); // A: 200, B: 400, C: 600, D: 800 System.out.println("map = " + map); assert map.getAccumulatedCredits() == 200; assert map.getMinCredits() == 200; map.replenish(d, 100); map.replenish(c, 100); map.replenish(a, 100); map.replenish(b, 100); assert map.getMinCredits() == 300; } public void testBlockingDecrementAndReplenishment() throws Exception { final CyclicBarrier barrier=new CyclicBarrier(2); Thread thread=new Thread() { public void run() { try { barrier.await(); Util.sleep(1000); replenishAll(100); } catch(Exception e) { e.printStackTrace(); } } }; thread.start(); addAll(); boolean rc=map.decrement(800, 100); assert rc; System.out.println("map:\n" + map); barrier.await(); rc=map.decrement(250, 5000); assert rc; System.out.println("map:\n" + map); assert map.getMinCredits() == 50; assert map.getAccumulatedCredits() == 250; } public void testBlockingDecrementAndReplenishment2() { long[] credit_sizes={500, 100, 100, 500, 300}; Decrementer[] decrementers=new Decrementer[credit_sizes.length]; addAll(); boolean rc=map.decrement(800, 100); assert rc; for(int i=0; i < credit_sizes.length; i++) decrementers[i]=new Decrementer(map, credit_sizes[i], 20000, true); for(Decrementer decr: decrementers) decr.start(); for(int i=0; i < 10; i++) { if(countAliveThreads(decrementers) == 3) break; Util.sleep(500); } int alive=countAliveThreads(decrementers); assert alive == 3; replenishAll(400); // the 300 credit decr will succeed now for(int i=0; i < 10; i++) { if(countAliveThreads(decrementers) == 2) break; Util.sleep(500); } assert countAliveThreads(decrementers) == 2; replenishAll(700); // one of the two 500 creds will succeed for(int i=0; i < 10; i++) { if(countAliveThreads(decrementers) == 1) break; Util.sleep(500); } assert countAliveThreads(decrementers) == 1; replenishAll(300); // the other one of the 500 creds will succeed for(int i=0; i < 10; i++) { if(countAliveThreads(decrementers) == 0) break; Util.sleep(500); } assert countAliveThreads(decrementers) == 0; } public void testClear() { addAll(); boolean rc=map.decrement(800, 100); assert rc; Decrementer decr1=new Decrementer(map, 300, 20000, false), decr2=new Decrementer(map, 500, 20000, false); decr1.start(); decr2.start(); Util.sleep(500); map.clear(); for(int i=0; i < 20; i++) { if(!decr1.isAlive() && !decr2.isAlive()) break; else Util.sleep(1000); } assert !decr1.isAlive(); assert !decr2.isAlive(); } public void testGetMembersWithInsufficientCredits() { addAll(); boolean rc=map.decrement(800, 50); assert rc; List
                list=map.getMembersWithInsufficientCredits(100); assert list.isEmpty(); list=map.getMembersWithInsufficientCredits(200); assert list.isEmpty(); list=map.getMembersWithInsufficientCredits(250); assert list.size() == 4; assert list.contains(a) && list.contains(b) && list.contains(c) && list.contains(d); map.remove(b); map.remove(c); list=map.getMembersWithInsufficientCredits(250); assert list.size() == 2; assert list.contains(a) && list.contains(d); map.decrement(100, 50); map.putIfAbsent(b); map.putIfAbsent(c); // Now A and D have 100 credits, B and C 1000 list=map.getMembersWithInsufficientCredits(800); assert list.size() == 2; assert list.contains(a) && list.contains(d); list=map.getMembersWithInsufficientCredits(100); assert list.isEmpty(); } protected int countAliveThreads(Thread[] threads) { int alive=0; for(Thread thread: threads) if(thread.isAlive()) alive++; return alive; } protected static class Decrementer extends Thread { private final CreditMap map; private final long amount; private final long timeout; protected final boolean loop; public Decrementer(CreditMap map, long amount, long timeout, boolean loop) { this.map=map; this.amount=amount; this.timeout=timeout; this.loop=loop; } public void run() { while(true) { boolean rc=map.decrement(amount, timeout); if(rc) { System.out.println("[" + getId() + "] decremented " + amount + " credits"); break; } if(!loop) break; } } } } libjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/tests/CustomProtocolTest.java0000644000175000017500000000224211647260573033333 0ustar moellermoellerpackage org.jgroups.tests; import org.jgroups.JChannel; import org.jgroups.Global; import org.jgroups.stack.Protocol; import org.testng.annotations.Test; /** * Tests custom protocol. * Author: Lenny Phan */ public class CustomProtocolTest { static final String PROTOCOL_STACK = "UDP(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;" + "print_local_addr=true)"; @Test(groups=Global.FUNCTIONAL) public static void testMyProtocol() throws Exception { System.out.println("PROTOCOL_STACK: " + PROTOCOL_STACK); JChannel channel = new JChannel(PROTOCOL_STACK); System.out.println("channel = " + channel); assert true; } public static class MyProtocol extends Protocol { } } libjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/tests/DigestTest.java0000644000175000017500000003670111647260573031565 0ustar moellermoeller package org.jgroups.tests; import org.jgroups.Address; import org.jgroups.Global; import org.jgroups.util.Digest; import org.jgroups.util.MutableDigest; import org.jgroups.util.Util; import org.testng.Assert; import org.testng.annotations.BeforeMethod; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; import java.io.*; import java.util.HashMap; import java.util.Map; /** * @author Bela Ban */ @Test(groups=Global.FUNCTIONAL, sequential=true) public class DigestTest { Digest d, d2; MutableDigest md; Address a1, a2, a3; @BeforeClass void beforeClass() throws Exception { a1=Util.createRandomAddress(); a2=Util.createRandomAddress(); a3=Util.createRandomAddress(); } @BeforeMethod void beforeMethod() { 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)); d=new Digest(map); md=new MutableDigest(map); } public void testSize() { d2=new Digest(3); Assert.assertEquals(0, d2.size()); } public void testEquals() { d2=d.copy(); System.out.println("d: " + d + "\nd2= " + d2); Assert.assertEquals(d, d); Assert.assertEquals(d, d2); } public void testDifference(){ 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 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); Assert.assertNotSame(digest, digest2); Digest diff = digest2.difference(digest); System.out.println(diff); assert diff.contains(a3); Assert.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(Util.createRandomAddress(), new Digest.Entry(1, 2, 3)); Digest digest3 =new Digest(map3); diff = digest3.difference(digest); System.out.println(diff); Assert.assertEquals(2, diff.size()); diff = digest3.difference(digest2); System.out.println(diff); Assert.assertEquals(1, diff.size()); Digest diff2 = digest2.difference(digest3); System.out.println(diff2); Assert.assertEquals(1, diff2.size()); Assert.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); assert 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); assert 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); assert !(my.isGreaterThanOrEqual(d)); } public void testEquals2() { md=new MutableDigest(d); System.out.println("d: " + d + "\nmd= " + md); Assert.assertEquals(d, d); Assert.assertEquals(d, md); md.incrementHighestDeliveredSeqno(a1); System.out.println("d: " + d + "\nmd= " + md); assert !(d.equals(md)); } public void testMutability() { Digest md2=md; Assert.assertEquals(md, md2); md.incrementHighestDeliveredSeqno(a2); Assert.assertEquals(md, md2); } public void testImmutability() { MutableDigest tmp=new MutableDigest(d); Assert.assertEquals(d, tmp); tmp.incrementHighestDeliveredSeqno(a2); assert !(d.equals(tmp)); } public void testImmutability2() { Digest tmp=d.copy(); Assert.assertEquals(d, tmp); } public void testImmutability3() { Digest tmp=new Digest(d); Assert.assertEquals(tmp, d); } public void testImmutability4() { Digest copy=md.copy(); Assert.assertEquals(copy, md); md.incrementHighestDeliveredSeqno(a1); assert !(copy.equals(md)); } public void testSeal() { MutableDigest tmp=new MutableDigest(3); tmp.add(a2, 1,2,3); Assert.assertEquals(1, tmp.size()); tmp.seal(); try { tmp.add(a2, 4,5,6); assert false : "should run into an exception"; } catch(IllegalAccessError e) { System.out.println("received exception \"" + e.toString() + "\" - as expected"); } Assert.assertEquals(1, tmp.size()); } public void testSeal2() { md.incrementHighestDeliveredSeqno(a1); md.seal(); try { md.incrementHighestDeliveredSeqno(a3); assert false : "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() { Assert.assertEquals(3, md.size()); md.add(a1, 100, 200, 201); Assert.assertEquals(3, md.size()); md.add(Util.createRandomAddress(), 1,2,3); Assert.assertEquals(4, md.size()); } public void testAddDigest() { Digest tmp=md.copy(); md.add(tmp); Assert.assertEquals(3, md.size()); } public void testAddDigest2() { MutableDigest tmp=new MutableDigest(4); tmp.add(Util.createRandomAddress(), 1,2,3); tmp.add(Util.createRandomAddress(), 1,2,3); tmp.add(a2, 1,2,3); tmp.add(a3, 1,2,3); md.add(tmp); Assert.assertEquals(5, md.size()); } public void testGet() { Digest.Entry entry; entry=d.get(a1); Assert.assertEquals(entry, new Digest.Entry(4, 500, 501)); entry=d.get(a2); Assert.assertEquals(entry, new Digest.Entry(25, 26, 26)); entry=d.get(a3); Assert.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); Assert.assertEquals(md.highestDeliveredSeqnoAt(a1), tmp + 1); tmp=md.highestDeliveredSeqnoAt(a2); md.incrementHighestDeliveredSeqno(a2); Assert.assertEquals(md.highestDeliveredSeqnoAt(a2), tmp + 1); tmp=md.highestDeliveredSeqnoAt(a3); md.incrementHighestDeliveredSeqno(a3); Assert.assertEquals(md.highestDeliveredSeqnoAt(a3), tmp + 1); } public void testConstructor() { Assert.assertEquals(3, md.size()); md.clear(); Assert.assertEquals(0, md.size()); md.clear(); Assert.assertEquals(0, md.size()); } public static void testConstructor2() { Digest dd=new Digest(3); Assert.assertEquals(0, dd.size()); } public static void testConstructor3() { Digest dd=new MutableDigest(3); Assert.assertEquals(0, dd.size()); } public void testContains() { assert d.contains(a1); assert d.contains(a2); assert d.contains(a3); } public void testResetAt() { md.resetAt(a1); Assert.assertEquals(0, md.lowSeqnoAt(a1)); Assert.assertEquals(0, md.highestDeliveredSeqnoAt(a1)); Assert.assertEquals(0, md.highestReceivedSeqnoAt(a1)); } public void testLowSeqnoAt() { Assert.assertEquals(4, d.lowSeqnoAt(a1)); Assert.assertEquals(25, d.lowSeqnoAt(a2)); Assert.assertEquals(20, d.lowSeqnoAt(a3)); } public void testHighSeqnoAt() { Assert.assertEquals(500, d.highestDeliveredSeqnoAt(a1)); Assert.assertEquals(26, d.highestDeliveredSeqnoAt(a2)); Assert.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() { Assert.assertEquals(501, d.highestReceivedSeqnoAt(a1)); Assert.assertEquals(26, d.highestReceivedSeqnoAt(a2)); Assert.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() { Assert.assertEquals(4, d.lowSeqnoAt(a1)); Assert.assertEquals(500, d.highestDeliveredSeqnoAt(a1)); Assert.assertEquals(501, md.highestReceivedSeqnoAt(a1)); md.setHighestDeliveredAndSeenSeqnos(a1, 2, 10, 20); Assert.assertEquals(2, md.lowSeqnoAt(a1)); Assert.assertEquals(10, md.highestDeliveredSeqnoAt(a1)); Assert.assertEquals(20, md.highestReceivedSeqnoAt(a1)); } public void testCopy() { d=d.copy(); testLowSeqnoAt(); testHighSeqnoAt(); testHighSeqnoSeenAt(); testContains(); testResetAt(); } public void testCopy2() { Digest tmp=d.copy(); Assert.assertEquals(tmp, d); } public void testMutableCopy() { Digest copy=md.copy(); System.out.println("md=" + md + "\ncopy=" + copy); Assert.assertEquals(md, copy); md.add(a1, 4, 500, 1000); System.out.println("md=" + md + "\ncopy=" + copy); assert !(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); Assert.assertEquals(3, d.size()); Assert.assertEquals(3, digest.size()); Assert.assertEquals(3, digest.lowSeqnoAt(a1)); Assert.assertEquals(500, digest.highestDeliveredSeqnoAt(a1)); Assert.assertEquals(502, digest.highestReceivedSeqnoAt(a1)); Assert.assertEquals(20, digest.lowSeqnoAt(a2)); Assert.assertEquals(26, digest.highestDeliveredSeqnoAt(a2)); Assert.assertEquals(27, digest.highestReceivedSeqnoAt(a2)); Assert.assertEquals(20, digest.lowSeqnoAt(a3)); Assert.assertEquals(26, digest.highestDeliveredSeqnoAt(a3)); Assert.assertEquals(35, digest.highestReceivedSeqnoAt(a3)); } public void testNonConflictingMerge() { MutableDigest cons_d=new MutableDigest(5); Address ip1=Util.createRandomAddress(), ip2=Util.createRandomAddress(); 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); Assert.assertEquals(5, cons_d.size()); //System.out.println("\ncons_d after: " + cons_d); Assert.assertEquals(1, cons_d.lowSeqnoAt(ip1)); Assert.assertEquals(2, cons_d.lowSeqnoAt(ip2)); Assert.assertEquals(4, cons_d.lowSeqnoAt(a1)); Assert.assertEquals(25, cons_d.lowSeqnoAt(a2)); Assert.assertEquals(20, cons_d.lowSeqnoAt(a3)); Assert.assertEquals(10, cons_d.highestDeliveredSeqnoAt(ip1)); Assert.assertEquals(20, cons_d.highestDeliveredSeqnoAt(ip2)); Assert.assertEquals(500, cons_d.highestDeliveredSeqnoAt(a1)); Assert.assertEquals(26, cons_d.highestDeliveredSeqnoAt(a2)); Assert.assertEquals(25, cons_d.highestDeliveredSeqnoAt(a3)); Assert.assertEquals(10, cons_d.highestReceivedSeqnoAt(ip1)); Assert.assertEquals(20, cons_d.highestReceivedSeqnoAt(ip2)); Assert.assertEquals(501, cons_d.highestReceivedSeqnoAt(a1)); Assert.assertEquals(26, cons_d.highestReceivedSeqnoAt(a2)); Assert.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); Assert.assertEquals(3, md.size()); //System.out.println("d after: " + d); Assert.assertEquals(4, md.lowSeqnoAt(a1)); Assert.assertEquals(500, md.highestDeliveredSeqnoAt(a1)); Assert.assertEquals(501, md.highestReceivedSeqnoAt(a1)); Assert.assertEquals(25, md.lowSeqnoAt(a2)); Assert.assertEquals(26, md.highestDeliveredSeqnoAt(a2)); Assert.assertEquals(26, md.highestReceivedSeqnoAt(a2)); Assert.assertEquals(18, md.lowSeqnoAt(a3)); Assert.assertEquals(28, md.highestDeliveredSeqnoAt(a3)); Assert.assertEquals(35, md.highestReceivedSeqnoAt(a3)); } public void testSameSendersOtherIsNull() { assert !(d.sameSenders(null)); } public void testSameSenders1MNullDifferentLenth() { d2=new Digest(1); assert !(d2.sameSenders(d)); } public void testSameSenders1MNullSameLength() { d2=new Digest(3); assert !(d2.sameSenders(d)); } public void testSameSendersIdentical() { d2=d.copy(); assert 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); assert md.sameSenders(tmp); assert d.sameSenders(tmp); } public void testSameSendersNotSameLength() { md=new MutableDigest(3); md.add(a1, 4, 500, 501); md.add(a2, 25, 26, 26); assert !(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); Assert.assertEquals(d, tmp); } public void testSerializedSize() throws Exception { long len=d.serializedSize(); byte[] buf=Util.streamableToByteBuffer(d); Assert.assertEquals(len, buf.length); } } ././@LongLink0000000000000000000000000000015000000000000011561 Lustar rootrootlibjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/tests/ExponentialIntervalTest.javalibjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/tests/ExponentialIntervalTest.j0000644000175000017500000000350611647260573033646 0ustar moellermoellerpackage org.jgroups.tests; import org.jgroups.stack.ExponentialInterval; import org.jgroups.Global; import org.testng.Assert; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; /** * @author Bela Ban */ public class ExponentialIntervalTest { @Test(groups=Global.FUNCTIONAL) public static void testInitialization() { ExponentialInterval interval=new ExponentialInterval(10); System.out.println("interval=" + interval); long value=interval.next(); System.out.println("interval=" + interval); Assert.assertEquals(10, value); value=interval.next(); System.out.println("interval=" + interval); Assert.assertEquals(20, value); } @Test(groups=Global.FUNCTIONAL) public static void testNoargConstructor() { ExponentialInterval interval=new ExponentialInterval(); Assert.assertEquals(30, interval.next()); Assert.assertEquals(60, interval.next()); } @Test(groups=Global.FUNCTIONAL) public static void testMax() { ExponentialInterval interval=new ExponentialInterval(1000); System.out.println("interval=" + interval); Assert.assertEquals(1000, interval.next()); System.out.println("interval=" + interval); Assert.assertEquals(2000, interval.next()); System.out.println("interval=" + interval); Assert.assertEquals(4000, interval.next()); System.out.println("interval=" + interval); Assert.assertEquals(8000, interval.next()); System.out.println("interval=" + interval); Assert.assertEquals(15000, interval.next()); System.out.println("interval=" + interval); Assert.assertEquals(15000, interval.next()); System.out.println("interval=" + interval); } }libjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/tests/FCTest.java0000644000175000017500000000551211647260573030632 0ustar moellermoeller package org.jgroups.tests; import org.jgroups.*; import org.jgroups.debug.Simulator; import org.jgroups.protocols.FC; import org.jgroups.protocols.FRAG2; import org.jgroups.stack.IpAddress; import org.jgroups.stack.Protocol; import org.jgroups.util.Util; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.Vector; /** * Tests the flow control (FC) protocol * @author Bela Ban */ @Test(groups=Global.FUNCTIONAL,sequential=true) public class FCTest { Simulator s=null; static final int SIZE=1000; // bytes static final int NUM_MSGS=100000; static final int PRINT=NUM_MSGS / 10; @BeforeMethod void setUp() throws Exception { IpAddress a1=new IpAddress(1111); Vector
                members=new Vector
                (); members.add(a1); View v=new View(a1, 1, members); s=new Simulator(); s.setLocalAddress(a1); s.setView(v); s.addMember(a1); FC fc=new FC(); fc.setMinCredits(1000); fc.setMaxCredits(10000); fc.setMaxBlockTime(1000); FRAG2 frag=new FRAG2(); frag.setFragSize(8000); Protocol[] stack=new Protocol[]{frag, fc}; s.setProtocolStack(stack); s.start(); } @AfterMethod void tearDown() throws Exception { s.stop(); } @Test(groups=Global.FUNCTIONAL) 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 % PRINT == 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--; } assert 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 % PRINT == 0) System.out.println("<== " + num_mgs_received); } } public int getNumberOfReceivedMessages() { return num_mgs_received; } } } libjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/tests/FixedSizeBitSetTest.java0000644000175000017500000000636311647260573033354 0ustar moellermoellerpackage org.jgroups.tests; import org.jgroups.Global; import org.jgroups.util.FixedSizeBitSet; import org.jgroups.util.Util; import org.testng.annotations.Test; import java.util.Set; import java.util.TreeSet; /** * @author Bela Ban */ @Test(groups=Global.FUNCTIONAL,sequential=false) public class FixedSizeBitSetTest { public static void testConstructor() { FixedSizeBitSet set=new FixedSizeBitSet(10); assert set.cardinality() == 0; assert set.size() == 10; } @Test(expectedExceptions=IndexOutOfBoundsException.class) public static void testSetWithIndexOutOfBounds() { FixedSizeBitSet set=new FixedSizeBitSet(10); set.set(0); set.set(10); } @Test(expectedExceptions=IndexOutOfBoundsException.class) public static void testClearWithIndexOutOfBounds() { FixedSizeBitSet set=new FixedSizeBitSet(10); set.clear(10); } @Test(expectedExceptions=IndexOutOfBoundsException.class) public static void testGetWithIndexOutOfBounds() { FixedSizeBitSet set=new FixedSizeBitSet(10); set.get(10); } public static void testToString() { FixedSizeBitSet set=new FixedSizeBitSet(10); System.out.println("set = " + set); set.set(0); set.set(9); System.out.println("set = " + set); } public static void testNextSetBit() { FixedSizeBitSet set=new FixedSizeBitSet(64); int index=set.nextSetBit(10); assert index == -1 : "expected -1 but got " + index; index=set.nextSetBit(63); assert index == -1 : "expected -1 but got " + index; set.set(62); index=set.nextSetBit(62); assert index == 62 : "expected 62 but got " + index; index=set.nextSetBit(63); assert index == -1 : "expected -1 but got " + index; set.set(63); index=set.nextSetBit(63); assert index == 63 : "expected 63 but got " + index; } public static void testNextSetBit2() { FixedSizeBitSet set=new FixedSizeBitSet(64); set.set(0); int index=set.nextSetBit(0); assert index == 0 : "expected 0 but got " + index; } public static void testNextClearBit() { FixedSizeBitSet set=new FixedSizeBitSet(64); int index=set.nextClearBit(0); assert index == 0 : "expected 0 but got " + index; set.set(62); set.set(63); index=set.nextClearBit(62); assert index == -1; } public static void testNextSetAndClearBitOutOfRangeIndex() { FixedSizeBitSet set=new FixedSizeBitSet(64); for(int num: new int[]{64, 120}) { int index=set.nextSetBit(num); assert index == -1; index=set.nextClearBit(num); assert index == -1; } } public static void testLargeSet() { FixedSizeBitSet set=new FixedSizeBitSet(1500); Set sorted_set=new TreeSet(); for(int i=0; i < 500; i++) { int num=(int)Util.random(1499); sorted_set.add(num); } for(int num: sorted_set) set.set(num); int num_set=sorted_set.size(); System.out.println("set " + num_set + " bits"); assert set.cardinality() == num_set; } } libjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/tests/FragTest.java0000644000175000017500000000645111647260573031224 0ustar moellermoeller package org.jgroups.tests; import org.testng.annotations.*; import org.jgroups.Address; import org.jgroups.Event; import org.jgroups.Message; import org.jgroups.Global; 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 */ @Test(groups=Global.FUNCTIONAL) public class FragTest { public static final long NUM_MSGS=10; public static final int MSG_SIZE=100000; public static final int FRAG_SIZE=24000; private static 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 testRegularMessages() throws Exception { FragReceiver frag_receiver=new FragReceiver(this); ProtocolTester t=new ProtocolTester("FRAG2(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'); 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 void testMessagesWithOffsets() throws Exception { FragReceiver frag_receiver=new FragReceiver(this); ProtocolTester t=new ProtocolTester("FRAG2(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'); byte[] big_buffer=new byte[(int)(MSG_SIZE * NUM_MSGS)]; int offset=0; for(int i=0; i < NUM_MSGS; i++) { big_msg=new Message(null, null, big_buffer, offset, 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); offset+=MSG_SIZE; } t.stop(); } private static class FragReceiver extends Protocol { long num_msgs=0; FragTest t=null; FragReceiver(FragTest t) { this.t=t; } 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; } } } libjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/tests/HeadersTest.java0000644000175000017500000001222611647260573031715 0ustar moellermoellerpackage org.jgroups.tests; import org.jgroups.Global; import org.jgroups.Header; import org.jgroups.util.Headers; import org.testng.annotations.Test; import java.io.*; import java.util.Map; /** * Tests the functionality of the Headers class * @author Bela Ban */ @Test(groups=Global.FUNCTIONAL,sequential=false) public class HeadersTest { private static final short UDP_ID=1, FRAG_ID=2, NAKACK_ID=3; 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(); short[] ids=hdrs.getRawIDs(); assert ids.length == hdrs.capacity(); Header[] headers=hdrs.getRawHeaders(); assert headers.length == hdrs.capacity(); 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(); short[] ids=hdrs.getRawIDs(); Header[] headers=hdrs.getRawHeaders(); assert ids.length == hdrs.capacity(); assert headers.length == hdrs.capacity(); assert hdrs.size() == 3; // make sure 'hdrs' is not changed when 'old' is modified, as 'hdrs' is a copy old.putHeader((short)300, new MyHeader()); assert hdrs.capacity() == 3 : "capacity must be 3 but was " + hdrs.capacity(); assert ids.length == hdrs.capacity(); assert headers.length == hdrs.capacity(); assert hdrs.size() == 3; } public static void testGetRawData() { Headers hdrs=createHeaders(3); short[] ids=hdrs.getRawIDs(); Header[] headers=hdrs.getRawHeaders(); assert ids.length == 3; assert headers.length == 3; assert ids[0] == NAKACK_ID; assert headers[0] == h1; assert ids[1] == FRAG_ID; assert headers[1] == h2; assert ids[2] == UDP_ID; assert headers[2] == h3; assert ids.length == hdrs.capacity(); assert headers.length == hdrs.capacity(); 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_ID) == h1; assert map.get(FRAG_ID) == h2; assert map.get(UDP_ID) == 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_ID, h1); hdrs.putHeader(FRAG_ID, h2); hdrs.putHeader(UDP_ID, h3); return hdrs; } public static void testPutHeader() { Headers hdrs=createHeaders(3); assert hdrs.getHeader(NAKACK_ID) == h1; hdrs.putHeader(NAKACK_ID, new MyHeader()); assert hdrs.size() == 3; assert hdrs.getHeader(NAKACK_ID) != h1; assert hdrs.capacity() == 3; hdrs.putHeader((short)400, new MyHeader()); assert hdrs.size() == 4; assert hdrs.capacity() > 3; } public static void testPutHeaderIfAbsent() { Headers hdrs=createHeaders(3); Header hdr=hdrs.putHeaderIfAbsent(FRAG_ID, new MyHeader()); assert hdr == h2; assert hdr == hdrs.getHeader(FRAG_ID); assert hdrs.size() == 3; assert hdrs.capacity() == 3; hdr=hdrs.putHeaderIfAbsent((short)400, 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((short)400); assert hdrs.getHeader(UDP_ID) == h3; } public static void testResize() { Headers hdrs=createHeaders(3); int capacity=hdrs.capacity(); System.out.println("hdrs = " + hdrs + ", capacity=" + capacity); hdrs.putHeader((short)400, new MyHeader()); System.out.println("hdrs = " + hdrs + ", capacity=" + hdrs.capacity()); assert hdrs.capacity() > capacity; capacity=hdrs.capacity(); for(int i=10; i <= 13; i++) hdrs.putHeader((short)i, new MyHeader()); System.out.println("hdrs = " + hdrs + ", capacity=" + hdrs.capacity()); assert hdrs.capacity() > capacity; } public static class MyHeader extends Header { public MyHeader() { } public String toString() { return "MyHeader"; } public void writeTo(DataOutputStream out) throws IOException { } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { } public int size() { return 0; } } } ././@LongLink0000000000000000000000000000014600000000000011566 Lustar rootrootlibjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/tests/InetAddressChecksTest.javalibjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/tests/InetAddressChecksTest.jav0000644000175000017500000001157511647260573033535 0ustar moellermoellerpackage org.jgroups.tests; import org.jgroups.Event; import org.jgroups.Global ; import org.jgroups.conf.ProtocolConfiguration; import org.jgroups.stack.Protocol; import org.jgroups.stack.ProtocolStack; import org.jgroups.stack.Configurator ; import org.jgroups.stack.Configurator.InetAddressInfo; import org.jgroups.annotations.Property; import org.jgroups.util.Util ; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.*; import java.net.InetAddress ; /** * Tests checks made on InetAddress and related addresses in Configurator. * @author Richard Achmatowicz */ @Test(groups=Global.FUNCTIONAL,sequential=true) public class InetAddressChecksTest { ProtocolStack stack = null; Protocol protocol = null ; static final String ipCheckNoConsistentProps="org.jgroups.tests.InetAddressChecksTest$IPCHECK(" + "inetAddress1=127.0.0.1;inetAddress2=::1;inetAddress3=192.168.0.100;i=3)" ; static final String ipCheckConsistentProps="org.jgroups.tests.InetAddressChecksTest$IPCHECK(" + "inetAddress1=127.0.0.1;inetAddress2=127.0.0.1;inetAddress3=192.168.0.100;i=3)" ; List order = new LinkedList() ; @BeforeMethod void setUp() { stack=new ProtocolStack(); } /* * Checks IP version mechanism for inconsistent version processing */ @Test(expectedExceptions=RuntimeException.class) public void testIPVersionCheckingNoConsistentVersion() throws Exception { Vector protocol_configs = new Vector() ; Vector protocols = new Vector() ; // create the layer described by IPCHECK protocol = Configurator.createProtocol(ipCheckNoConsistentProps, stack) ; // process the defaults protocol_configs.add(new ProtocolConfiguration(ipCheckNoConsistentProps)) ; protocols.add(protocol) ; Map> inetAddressMap = null ; try { inetAddressMap = Configurator.createInetAddressMap(protocol_configs, protocols) ; Collection addrs=Configurator.getAddresses(inetAddressMap); Configurator.determineIpVersionFromAddresses(addrs) ; } catch(RuntimeException e) { System.out.println("Expected exception received: " + e.getMessage()) ; throw e ; } // get the value which should have been assigned a default InetAddress a = ((IPCHECK)protocol).getInetAddress1() ; System.out.println("value of inetAddress1 = " + a) ; InetAddress b = ((IPCHECK)protocol).getInetAddress2() ; System.out.println("value of inetAddress2 = " + b) ; InetAddress c = ((IPCHECK)protocol).getInetAddress3() ; System.out.println("value of inetAddress3 = " + c) ; } /* * Checks IP version mechanism for consistent version processing */ public void testIPVersionCheckingConsistentVersion() throws Exception { Vector protocol_configs = new Vector() ; Vector protocols = new Vector() ; // create the layer described by IPCHECK protocol = Configurator.createProtocol(ipCheckConsistentProps, stack) ; // process the defaults protocol_configs.add(new ProtocolConfiguration(ipCheckConsistentProps)) ; protocols.add(protocol) ; Map> inetAddressMap = null ; inetAddressMap = Configurator.createInetAddressMap(protocol_configs, protocols) ; Collection addrs=Configurator.getAddresses(inetAddressMap); Configurator.determineIpVersionFromAddresses(addrs) ; // get the value which should have been assigned a default InetAddress a = ((IPCHECK)protocol).getInetAddress1() ; System.out.println("value of inetAddress1 = " + a) ; InetAddress b = ((IPCHECK)protocol).getInetAddress2() ; System.out.println("value of inetAddress2 = " + b) ; InetAddress c = ((IPCHECK)protocol).getInetAddress3() ; System.out.println("value of inetAddress3 = " + c) ; } /* * Checks which IP stacks are available on the platform */ public static void testWhichIPStacksAvailable() throws Exception { boolean isIPv4 = Util.isStackAvailable(true); boolean isIPv6 = Util.isStackAvailable(false); System.out.println("isIPv4 = " + isIPv4); System.out.println("isIPv6 = " + isIPv6); } public static class IPCHECK extends Protocol { @Property(name="inetAddress1") InetAddress inetAddress1 ; public InetAddress getInetAddress1() { return inetAddress1 ; } @Property(name="inetAddress2") InetAddress inetAddress2 ; public InetAddress getInetAddress2() { return inetAddress2 ; } @Property(name="inetAddress3") InetAddress inetAddress3 ; public InetAddress getInetAddress3() { return inetAddress3 ; } @Property(description="wilma") int i = 0 ; // do nothing public Object down(Event evt) { return down_prot.down(evt); } // do nothing public Object up(Event evt) { return up_prot.up(evt); } } } libjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/tests/InterruptTest.java0000644000175000017500000001263411647260573032341 0ustar moellermoeller package org.jgroups.tests; import org.testng.annotations.*; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import org.jgroups.Global; /** * Tests Thread.interrupt() against InputStream.read(), Object.wait() and Thread.sleep() * @author Bela Ban Oct 5 2001 */ @Test(groups=Global.FUNCTIONAL) public class InterruptTest { 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; static 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 static void testSleepInterrupt() { SleeperThread thread=new SleeperThread(SLEEP); runTest(thread); } public static 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); }*/ static 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()); assert !thread.isAlive(); } static void sleep(long msecs) { try { Thread.sleep(msecs); } catch(Exception ex) { System.err.println("InterruptTest.sleep(): " + ex); } } static 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; } } static void runSleep() { try { Thread.sleep(TIMEOUT); } catch(InterruptedException ex) { System.err.println("InterruptTest.SleeperThread.runSleep(): " + ex); } } static void runWait() { Object mutex=new Object(); synchronized(mutex) { try { mutex.wait(); } catch(InterruptedException ex) { System.err.println("InterruptTest.SleeperThread.runWait(): " + ex); } } } static 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); } } } } libjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/tests/IpAddressTest.java0000644000175000017500000003204311647260573032217 0ustar moellermoeller package org.jgroups.tests; import org.jgroups.Address; import org.jgroups.Global; import org.jgroups.stack.IpAddress; import org.jgroups.util.StackType; import org.jgroups.util.Util; import org.testng.Assert; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; import java.io.*; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @Test(groups=Global.FUNCTIONAL,sequential=true) public class IpAddressTest { IpAddress a, b, c, d, e, f, g, h, i, j, k; @BeforeClass public void setUp() throws Exception { StackType type=Util.getIpStackType(); if(type == StackType.IPv6) { a=new IpAddress("::1", 5555); b=new IpAddress("::1", 5555); d=new IpAddress("::1", 5556); e=new IpAddress("::1", 5555); f=new IpAddress("2001:0db8:0000:0000:0000:002e:0370:2334", 80); g=new IpAddress("2001:0db8:0000:0000:0000:002e:0370:2334", 8080); h=new IpAddress("ff0e::3:4:5", 5555); } else { a=new IpAddress("localhost", 5555); b=new IpAddress("localhost", 5555); 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); } c=b; } public static void testUnknownAddress() { try { IpAddress tmp=new IpAddress("idontknow.com", 55); assert false : "should throw an UnknownHostException for " + tmp; } catch(UnknownHostException e1) { } } public void testEquality() throws Exception { Assert.assertEquals(a, b); Assert.assertEquals(c, b); Assert.assertEquals(a, e); Assert.assertEquals(c, e); } public static void testEqualityWithDnsRoundRobin() throws UnknownHostException { IpAddress x1, x2, x3; StackType type=Util.getIpStackType(); String tmp=type == StackType.IPv6? "::1" : "127.0.0.1"; InetAddress addr=InetAddress.getByName(tmp); byte[] rawAddr=addr.getAddress(); InetAddress inet1=InetAddress.getByAddress("MyHost1", rawAddr); InetAddress inet2=InetAddress.getByAddress("MyHost2", rawAddr); InetAddress inet3=InetAddress.getByAddress("MyHost3", rawAddr); Assert.assertEquals(inet1, inet2); x1=new IpAddress(inet1, 5555); x2=new IpAddress(inet2, 5555); x3=new IpAddress(inet3, 5555); Assert.assertEquals(x1, x2); Assert.assertEquals(x3, x1); HashSet
                s=new HashSet
                (); Collections.addAll(s, x1, x2, x3); System.out.println("s=" + s); Assert.assertEquals(1, s.size()); HashMap m=new HashMap(); m.put(x1, "Bela"); m.put(x2, "Michelle"); m.put(x3, "Nicole"); Assert.assertEquals(1, m.size()); Assert.assertEquals("Nicole", m.get(x1)); } public void testInequality() throws Exception { IpAddress tmp=null; assert !a.equals(d); assert !c.equals(d); assert !a.equals(f); assert !e.equals(f); assert !f.equals(g); assert !(a.equals(tmp)); } public void testSameHost() throws Exception { assert Util.sameHost(a, b); assert Util.sameHost(a, c); assert Util.sameHost(a, d); assert Util.sameHost(a, e); assert Util.sameHost(f, g); } public void testNotSameHost() throws Exception { assert !Util.sameHost(a, f); assert !Util.sameHost(e, f); assert !Util.sameHost(e, null); assert !Util.sameHost(null, null); } public void testMcast() { assert h.isMulticastAddress(); assert !a.isMulticastAddress(); assert !e.isMulticastAddress(); assert !g.isMulticastAddress(); } public void testCompareTo() { Assert.assertEquals(0, a.compareTo(b)); assert a.compareTo(d) < 0; assert 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(); Assert.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 static void testIPv6WithExternalization() throws IOException, ClassNotFoundException { InetAddress tmp=Util.getNonLoopbackAddress(); 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(); Assert.assertEquals(ip, ip2); } public static void testIPv6WithStreamable() throws IOException, ClassNotFoundException { InetAddress tmp=Util.getNonLoopbackAddress(); 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); Assert.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(); Assert.assertEquals(a, a2); Assert.assertEquals(b, b2); assert a2.getAdditionalData() == null; Assert.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(); Assert.assertEquals(b2, c2); Assert.assertEquals(a, a2); Assert.assertEquals(b, b2); Assert.assertEquals(c, c2); Assert.assertEquals(d, d2); Assert.assertEquals(e, e2); Assert.assertEquals(f, f2); Assert.assertEquals(g, g2); Assert.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=createStackConformantAddress(5555); x.setAdditionalData(new byte[]{'b','e','l','a'}); y=createStackConformantAddress(1111); 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); Assert.assertEquals(a, a2); Assert.assertEquals(b, b2); assert a2.getAdditionalData() == null; Assert.assertEquals("Bela Ban", new String(b2.getAdditionalData())); assert x2.getAdditionalData() != null; Assert.assertEquals(4, x2.getAdditionalData().length); assert y2.getIpAddress() != null; Assert.assertEquals(1111, y2.getPort()); assert y2.getAdditionalData() != null; Assert.assertEquals(4, y2.getAdditionalData().length); } public static 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=createStackConformantAddress(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); assert x2.getPort() > 0; Assert.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); Assert.assertEquals(b2, c2); Assert.assertEquals(a, a2); Assert.assertEquals(b, b2); Assert.assertEquals(c, c2); Assert.assertEquals(d, d2); Assert.assertEquals(e, e2); Assert.assertEquals(f, f2); Assert.assertEquals(g, g2); Assert.assertEquals(h, h2); } private static IpAddress createStackConformantAddress(int port) throws UnknownHostException { StackType type=Util.getIpStackType(); if(type == StackType.IPv6) return new IpAddress("::1", port); else return new IpAddress("127.0.0.1", port); } } libjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/tests/MembershipTest.java0000644000175000017500000001110011647260573032423 0ustar moellermoeller package org.jgroups.tests; import org.jgroups.Address; import org.jgroups.Global; import org.jgroups.Membership; import org.jgroups.util.UUID; import org.jgroups.util.Util; import org.testng.Assert; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.Arrays; import java.util.List; import java.util.Vector; /** * Author: Bela Ban */ @Test(groups=Global.FUNCTIONAL,sequential=true) public class MembershipTest { Membership m1, m2; List
                v1, v2; Address a1, a2, a3, a4, a5; @BeforeMethod public void setUp() { a1=Util.createRandomAddress(); a2=Util.createRandomAddress(); a3=a2; a4=Util.createRandomAddress(); a5=Util.createRandomAddress(); m1=new Membership(); } public void testConstructor() { v1=Arrays.asList(a1, a2, a3); m2=new Membership(v1); assert m2.size() == 2; assert m2.contains(a1); assert m2.contains(a2); assert m2.contains(a3); } public void testClone() { v1=Arrays.asList(a1, a2, a3); m2=new Membership(v1); m1=(Membership)m2.clone(); assert m1.size() == m2.size(); assert m1.contains(a1); assert m1.contains(a2); assert m2.contains(a1); assert m2.contains(a2); } public void testCopy() { v1=Arrays.asList(a1, a2, a3); m2=new Membership(v1); m1=m2.copy(); assert m1.size() == m2.size(); assert m1.contains(a1); assert m1.contains(a2); assert m2.contains(a1); assert m2.contains(a2); } public void testAdd() { m1.add(a1, a2, a3); assert m1.size() == 2; assert m1.contains(a1); assert m1.contains(a2); assert m1.contains(a3); } public void testAddVector() { v1=Arrays.asList(a1, a2, a3); m1.add(v1); assert m1.size() == 2; assert m1.contains(a1); assert m1.contains(a2); } public void testAddVectorDupl() { v1=Arrays.asList(a1, a2, a3, a4, a5); m1.add(a5, a1); m1.add(v1); assert m1.size() == 4; assert m1.contains(a1); assert m1.contains(a2); assert m1.contains(a4); assert m1.contains(a5); } public void testRemove() { m1.add(a1, a2, a3, a4, a5); m1.remove(a2); assert m1.size() == 3; } public void testGetMembers() { testAdd(); Vector v=m1.getMembers(); assert v.size() == 2; } public void testSet() { v1=Arrays.asList(a1, a2); m1.add(a1, a2, a4, a5); m1.set(v1); assert m1.size() == 2; assert m1.contains(a1); assert m1.contains(a2); } public void testSet2() { m1=new Membership(); m2=new Membership(); m1.add(a1, a2, a4); m2.add(a5); m2.set(m1); assert m2.size() == 3; assert m2.contains(a1); assert m2.contains(a2); assert m2.contains(a4); assert !m2.contains(a5); } public void testMerge() { v1=Arrays.asList(a5); v2=Arrays.asList(a2, a3); m1.add(a1, a2, a3, a4); m1.merge(v1, v2); assert m1.size() == 3; assert m1.contains(a1); assert m1.contains(a4); assert m1.contains(a5); } public void testSort() { m1.add(a3, a4, a2, a1, a5, a2); System.out.println("membership:\n" + printUUIDs(m1)); Assert.assertEquals(4, m1.size()); Assert.assertEquals(a3, m1.elementAt(0)); Assert.assertEquals(a4, m1.elementAt(1)); Assert.assertEquals(a1, m1.elementAt(2)); Assert.assertEquals(a5, m1.elementAt(3)); m1.sort(); System.out.println("sorted: " + m1); Assert.assertEquals(4, m1.size()); Address addr0=m1.elementAt(0); Address addr1=m1.elementAt(1); Address addr2=m1.elementAt(2); Address addr3=m1.elementAt(3); System.out.println("sorted membership:\n" + printUUIDs(m1)); assert addr0.compareTo(addr1) < 0; assert addr1.compareTo(addr2) < 0; assert addr2.compareTo(addr3) < 0; } private static String printUUIDs(Membership mbrs) { StringBuilder sb=new StringBuilder(); boolean first=true; for(int i=0; i < mbrs.size(); i++) { UUID mbr=(UUID)mbrs.elementAt(i); if(first) first=false; else sb.append(", "); sb.append(mbr.toStringLong()); } return sb.toString(); } } libjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/tests/MergerTest.java0000644000175000017500000000736211647260573031570 0ustar moellermoellerpackage org.jgroups.tests; import org.jgroups.Address; import org.jgroups.Global; import org.jgroups.View; import org.jgroups.protocols.pbcast.Merger; import org.jgroups.util.Util; import org.testng.annotations.Test; import java.util.*; /** * @author Bela Ban */ @Test(groups=Global.FUNCTIONAL,sequential=false) public class MergerTest { private final static Address a=Util.createRandomAddress("A"), b=Util.createRandomAddress("B"), c=Util.createRandomAddress("C"), d=Util.createRandomAddress("D"), e=Util.createRandomAddress("E"), f=Util.createRandomAddress("F"); /** * A: AB * B: AB * C: CD * D: CD */ public void testSimpleMerge() { Map map=new HashMap(); map.put(a, makeView(a, a,b)); map.put(b, makeView(a, a,b)); map.put(c, makeView(c, c,d)); map.put(d, makeView(c, c,d)); System.out.println("map:\n" + print(map)); assert map.size() == 4; Merger.sanitizeViews(map); System.out.println("map after sanitization:\n" + print(map)); assert map.size() == 4; assert map.get(a).size() == 2; assert map.get(b).size() == 2; assert map.get(c).size() == 2; assert map.get(d).size() == 2; } /** * A: ABC * B: BC * C: BC */ public void testOverlappingMerge() { Map map=new HashMap(); map.put(a, makeView(a, a,b,c)); map.put(b, makeView(b, b,c)); map.put(c, makeView(b, b,c)); System.out.println("map:\n" + print(map)); assert map.size() == 3; Merger.sanitizeViews(map); System.out.println("map after sanitization:\n" + print(map)); assert map.size() == 3; assert map.get(a).size() == 1; assert map.get(b).size() == 2; assert map.get(c).size() == 2; } /** * A: AB * B: B */ public void testOverlappingMerge2() { Map map=new HashMap(); map.put(a, makeView(a, a,b)); map.put(b, makeView(b, b)); System.out.println("map:\n" + print(map)); assert map.size() == 2; Merger.sanitizeViews(map); System.out.println("map after sanitization:\n" + print(map)); assert map.size() == 2; assert map.get(a).size() == 1; assert map.get(b).size() == 1; } /** * A: AB * B: BC * C: CD * D: DE */ public void testOverlappingMerge3() { Map map=new HashMap(); map.put(a, makeView(a, a,b)); map.put(b, makeView(b, b,c)); map.put(c, makeView(c, c,d)); map.put(d, makeView(d, d,e)); System.out.println("map:\n" + print(map)); assert map.size() == 4; Merger.sanitizeViews(map); System.out.println("map after sanitization:\n" + print(map)); assert map.size() == 4; assert map.get(a).size() == 1; assert map.get(b).size() == 1; assert map.get(c).size() == 1; assert map.get(d).size() == 2; } private static Collection makeList(T ... elements) { return new ArrayList(Arrays.asList(elements)); } private static View makeView(Address coord, Address ... members) { return new View(coord, 1, new Vector
                (Arrays.asList(members))); } static String print(Map map) { StringBuilder sb=new StringBuilder(); for(Map.Entry entry: map.entrySet()) { sb.append(entry.getKey()).append(": ").append(entry.getValue().getMembers()).append("\n"); } return sb.toString(); } } libjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/tests/MessageSizeTest.java0000644000175000017500000000770611647260573032570 0ustar moellermoeller package org.jgroups.tests; import org.jgroups.Global; import org.jgroups.Message; import org.jgroups.Address; import org.jgroups.Version; import org.jgroups.conf.ClassConfigurator; import org.jgroups.protocols.PingHeader; import org.jgroups.protocols.TpHeader; import org.jgroups.protocols.UNICAST; import org.jgroups.protocols.UDP; import org.jgroups.protocols.pbcast.NakAckHeader; import org.jgroups.protocols.pbcast.NAKACK; import org.jgroups.util.*; import org.testng.Assert; import org.testng.annotations.Test; import java.io.DataOutputStream; /** * Tests the size of marshalled messages (multicast, unicast) * @author Bela Ban */ @Test(groups=Global.FUNCTIONAL) public class MessageSizeTest { private static final byte MULTICAST=2; private static final short UDP_ID=100; private static final short UNICAST_ID=101; private static final short NAKACK_ID=102; private static final int MCAST_MAX_SIZE=84; private static final int UNICAST_MAX_SIZE=102; /** * Tests size of a multicast message. * Current record: 84 bytes (March 2010) * Prev: 166, 109, 103, 84 * @throws Exception */ public static void testMulticast() throws Exception { Address src=Util.createRandomAddress(); Message msg=createMessage(null, src); Buffer buf=marshal(msg); System.out.println("buf = " + buf); int len=buf.getLength(); System.out.println("len = " + len); assert len <= MCAST_MAX_SIZE; if(len < MCAST_MAX_SIZE) { double percentage=compute(len, MCAST_MAX_SIZE); System.out.println("multicast message (" + len + " bytes) is " + Util.format(percentage) + "% smaller than previous max size (" + MCAST_MAX_SIZE + " bytes)"); } } /** * Tests size of a unicast message. * Current record: 102 (March 2010) * Prev: 161, 127, 121, 102 * @throws Exception */ public static void testUnicast() throws Exception { Address dest=Util.createRandomAddress(); Address src=Util.createRandomAddress(); Message msg=createMessage(dest, src); Buffer buf=marshal(msg); System.out.println("buf = " + buf); int len=buf.getLength(); System.out.println("len = " + len); assert len <= UNICAST_MAX_SIZE; if(len < UNICAST_MAX_SIZE) { double percentage=compute(len, UNICAST_MAX_SIZE); System.out.println("multicast message (" + len + " bytes) is " + Util.format(percentage) + "% smaller than previous max size (" + UNICAST_MAX_SIZE + " bytes)"); } } private static double compute(int new_length, int old_length) { if(new_length >= old_length) return 0.0; return 100.0* (1.0 - (new_length / (double)old_length)); } private static Buffer marshal(Message msg) throws Exception { ExposedByteArrayOutputStream out_stream=new ExposedByteArrayOutputStream((int)(msg.size() + 50)); ExposedDataOutputStream dos=new ExposedDataOutputStream(out_stream); Address dest=msg.getDest(); boolean multicast=dest == null || dest.isMulticastAddress(); writeMessage(msg, dos, multicast); return new Buffer(out_stream.getRawBuffer(), 0, out_stream.size()); } protected 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; dos.writeByte(flags); msg.writeTo(dos); } static Message createMessage(Address dest, Address src) { Message msg=new Message(dest, src, "hello world"); msg.putHeader(NAKACK_ID, NakAckHeader.createMessageHeader(322649)); msg.putHeader(UNICAST_ID, UNICAST.UnicastHeader.createDataHeader(465784, (short)23323, true)); msg.putHeader(UDP_ID, new TpHeader("DrawDemo")); return msg; } }libjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/tests/MessageTest.java0000644000175000017500000003314011647260573031724 0ustar moellermoeller package org.jgroups.tests; import org.jgroups.Global; import org.jgroups.Header; import org.jgroups.Message; import org.jgroups.conf.ClassConfigurator; import org.jgroups.protocols.PingHeader; import org.jgroups.protocols.TpHeader; import org.jgroups.protocols.UDP; import org.jgroups.protocols.PING; import org.jgroups.protocols.pbcast.NakAckHeader; import org.jgroups.protocols.pbcast.NAKACK; import org.jgroups.util.Range; import org.jgroups.util.UUID; import org.jgroups.util.Util; import org.testng.Assert; import org.testng.annotations.Test; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.util.Map; /** * @author Bela Ban */ @Test(groups=Global.FUNCTIONAL) public class MessageTest { static final short UDP_ID=101; static final short PING_ID=102; static final short NAKACK_ID=103; public static void testFlags() { Message m1=new Message(); assert !(m1.isFlagSet(Message.OOB)); try { m1.setFlag((byte)1002); assert false : "1002 is not a byte value"; } catch(IllegalArgumentException ex) { } assert m1.getFlags() == 0; } public static void testFlags2() { Message m1=new Message(); m1.setFlag(Message.OOB); assert m1.isFlagSet(Message.OOB); assert Message.OOB == (m1.getFlags() & Message.OOB); assert !(m1.isFlagSet(Message.DONT_BUNDLE)); Assert.assertNotSame((m1.getFlags() & Message.DONT_BUNDLE), Message.DONT_BUNDLE); } public static void testFlags3() { Message msg=new Message(); assert msg.isFlagSet(Message.OOB) == false; msg.setFlag(Message.OOB); assert msg.isFlagSet(Message.OOB); msg.setFlag(Message.OOB); assert msg.isFlagSet(Message.OOB); } public static void testClearFlags() { Message msg=new Message(); msg.setFlag(Message.OOB); assert msg.isFlagSet(Message.OOB); msg.clearFlag(Message.OOB); assert msg.isFlagSet(Message.OOB) == false; msg.clearFlag(Message.OOB); assert msg.isFlagSet(Message.OOB) == false; msg.setFlag(Message.OOB); assert msg.isFlagSet(Message.OOB); } public static void testClearFlags2() { Message msg=new Message(); msg.setFlag(Message.OOB); msg.setFlag(Message.NO_FC); assert msg.isFlagSet(Message.DONT_BUNDLE) == false; assert msg.isFlagSet(Message.OOB); assert msg.isFlagSet(Message.NO_FC); msg.clearFlag(Message.OOB); assert msg.isFlagSet(Message.OOB) == false; msg.setFlag(Message.DONT_BUNDLE); assert msg.isFlagSet(Message.DONT_BUNDLE); assert msg.isFlagSet(Message.NO_FC); msg.clearFlag(Message.NO_FC); assert msg.isFlagSet(Message.NO_FC) == false; msg.clearFlag(Message.NO_FC); assert msg.isFlagSet(Message.NO_FC) == false; msg.clearFlag(Message.DONT_BUNDLE); msg.clearFlag(Message.OOB); assert msg.getFlags() == 0; assert msg.isFlagSet(Message.OOB) == false; assert msg.isFlagSet(Message.DONT_BUNDLE) == false; assert msg.isFlagSet(Message.NO_FC) == false; msg.setFlag(Message.DONT_BUNDLE); assert msg.isFlagSet(Message.DONT_BUNDLE); msg.setFlag(Message.DONT_BUNDLE); assert msg.isFlagSet(Message.DONT_BUNDLE); } public static void testBufferSize() throws Exception { Message m1=new Message(null, null, "bela"); assert m1.getRawBuffer() != null; assert m1.getBuffer() != null; Assert.assertEquals(m1.getBuffer().length, m1.getLength()); byte[] new_buf={'m', 'i', 'c', 'h', 'e', 'l', 'l', 'e'}; m1.setBuffer(new_buf); assert m1.getRawBuffer() != null; assert m1.getBuffer() != null; Assert.assertEquals(new_buf.length, m1.getLength()); Assert.assertEquals(m1.getBuffer().length, m1.getLength()); } public static void testBufferOffset() throws Exception { byte[] buf={'b', 'e', 'l', 'a', 'b', 'a', 'n'}; Message m1=new Message(null, null, buf, 0, 4); Message 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()); Assert.assertEquals(4, b1.length); Assert.assertEquals(3, b2.length); } public static void testSetBufferWithNullBuffer() { byte[] buf={'b', 'e', 'l', 'a'}; Message m1=new Message(); m1.setBuffer(buf, 1, 2); // dummy data with non 0 oiffset and length Assert.assertEquals(1, m1.getOffset()); Assert.assertEquals(2, m1.getLength()); m1.setBuffer(null, 1, 2); // dummy offset and length, is ignored Assert.assertEquals(0, m1.getOffset()); Assert.assertEquals(0, m1.getLength()); } @Test(groups=Global.FUNCTIONAL, expectedExceptions=ArrayIndexOutOfBoundsException.class) public static void testInvalidOffset() { byte[] buf={'b', 'e', 'l', 'a', 'b', 'a', 'n'}; Message m1=new Message(null, null, buf, -1, 4); System.out.println("message is " + m1); } @Test(groups=Global.FUNCTIONAL, expectedExceptions=ArrayIndexOutOfBoundsException.class) public static void testInvalidLength() { byte[] buf={'b', 'e', 'l', 'a', 'b', 'a', 'n'}; Message m1=new Message(null, null, buf, 3, 6); System.out.println("we should not get here with " + m1); } public static void testGetRawBuffer() { byte[] buf={'b', 'e', 'l', 'a', 'b', 'a', 'n'}; Message m1=new Message(null, null, buf, 0, 4); Message m2=new Message(null, null, buf, 4, 3); Assert.assertEquals(buf.length, m1.getRawBuffer().length); Assert.assertEquals(4, m1.getBuffer().length); Assert.assertEquals(4, m1.getLength()); Assert.assertEquals(buf.length, m2.getRawBuffer().length); Assert.assertEquals(3, m2.getBuffer().length); Assert.assertEquals(3, m2.getLength()); } public static void testSetObject() { String s1="Bela Ban"; Message m1=new Message(null, null, s1); Assert.assertEquals(0, m1.getOffset()); Assert.assertEquals(m1.getBuffer().length, m1.getLength()); String s2=(String)m1.getObject(); Assert.assertEquals(s2, s1); } public static void testCopy() { Message m1=new Message(null, null, "Bela Ban"); Message m2=m1.copy(); Assert.assertEquals(m1.getOffset(), m2.getOffset()); Assert.assertEquals(m1.getLength(), m2.getLength()); } public static void testCopyWithOffset() { byte[] buf={'b', 'e', 'l', 'a', 'b', 'a', 'n'}; Message m1=new Message(null, null, buf, 0, 4); Message m2=new Message(null, null, buf, 4, 3); Message m3, m4; m3=m1.copy(); m4=m2.copy(); Assert.assertEquals(0, m3.getOffset()); Assert.assertEquals(4, m3.getLength()); Assert.assertEquals(4, m3.getBuffer().length); Assert.assertEquals(4, m4.getOffset()); Assert.assertEquals(3, m4.getLength()); Assert.assertEquals(3, m4.getBuffer().length); } public static void testCopyHeaders() { Message m1=new Message(null, null, "hello"); for(short id: new short[]{1, 2, 10, Global.BLOCKS_START_ID, Global.BLOCKS_START_ID +10}) { m1.putHeader(id, new DummyHeader(id)); } System.out.println("Headers for m1: " + m1.printHeaders()); Message m2=m1.copy(true, Global.BLOCKS_START_ID); System.out.println("Headers for m2: " + m2.printHeaders()); Map hdrs=m2.getHeaders(); assert hdrs.size() == 2; assert hdrs.containsKey(Global.BLOCKS_START_ID); short tmp=Global.BLOCKS_START_ID +10; assert hdrs.containsKey(tmp); } public static 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); Assert.assertEquals(3, retval.size()); r=(Range)retval.get(0); Assert.assertEquals(0, r.low); Assert.assertEquals(4, r.high); r=(Range)retval.get(1); Assert.assertEquals(4, r.low); Assert.assertEquals(4, r.high); r=(Range)retval.get(2); Assert.assertEquals(8, r.low); Assert.assertEquals(2, r.high); } public static 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); Assert.assertEquals(3, retval.size()); r=(Range)retval.get(0); Assert.assertEquals(3, r.low); Assert.assertEquals(4, r.high); r=(Range)retval.get(1); Assert.assertEquals(7, r.low); Assert.assertEquals(4, r.high); r=(Range)retval.get(2); Assert.assertEquals(11, r.low); Assert.assertEquals(2, r.high); } public static 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); Assert.assertEquals(1, retval.size()); r=(Range)retval.get(0); Assert.assertEquals(0, r.low); Assert.assertEquals(10, r.high); } public static 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); Assert.assertEquals(1, retval.size()); r=(Range)retval.get(0); Assert.assertEquals(0, r.low); Assert.assertEquals(10, r.high); } public static 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); Assert.assertEquals(2, retval.size()); r=(Range)retval.get(0); Assert.assertEquals(0, r.low); Assert.assertEquals(5, r.high); r=(Range)retval.get(1); Assert.assertEquals(5, r.low); Assert.assertEquals(5, r.high); } public static void testSizeNullMessage() throws Exception { Message msg=new Message(); _testSize(msg); } public static void testSizeMessageWithDest() throws Exception { Message msg=new Message(UUID.randomUUID(), null, null); _testSize(msg); } public static void testSizeMessageWithSrc() throws Exception { Message msg=new Message(null, UUID.randomUUID(), null); _testSize(msg); } public static void testSizeMessageWithDestAndSrc() throws Exception { Message msg=new Message(UUID.randomUUID(), UUID.randomUUID(), null); _testSize(msg); } public static void testSizeMessageWithDestAndSrcAndFlags() throws Exception { Message msg=new Message(UUID.randomUUID(), UUID.randomUUID(), null); msg.setFlag(Message.OOB); msg.setFlag(Message.DONT_BUNDLE); _testSize(msg); } public static void testSizeMessageWithBuffer() throws Exception { Message msg=new Message(null, null, "bela".getBytes()); _testSize(msg); } public static void testSizeMessageWithBuffer2() throws Exception { Message msg=new Message(null, null, new byte[]{'b', 'e', 'l', 'a'}); _testSize(msg); } public static void testSizeMessageWithBuffer3() throws Exception { Message msg=new Message(null, null, "bela"); _testSize(msg); } public static void testSizeMessageWithAdditionalData() throws Exception { UUID dest=UUID.randomUUID(); dest.setAdditionalData("bela".getBytes()); Message msg=new Message(dest, null, null); _testSize(msg); } public static void testSizeMessageWithDestAndSrcAndHeaders() throws Exception { Message msg=new Message(UUID.randomUUID(), UUID.randomUUID(), "bela".getBytes()); addHeaders(msg); _testSize(msg); } private static void addHeaders(Message msg) { TpHeader tp_hdr=new TpHeader("DemoChannel2"); msg.putHeader(UDP_ID, tp_hdr); PingHeader ping_hdr=new PingHeader(PingHeader.GET_MBRS_REQ, "demo-cluster"); msg.putHeader(PING_ID, ping_hdr); NakAckHeader nak_hdr=NakAckHeader.createXmitRequestHeader(100, 104, null); msg.putHeader(NAKACK_ID, nak_hdr); } private static 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); Assert.assertEquals(size, serialized_form.length); } protected static class DummyHeader extends Header { protected final short num; public DummyHeader(short num) { this.num=num; } public short getNum() { return num; } public int size() { return 0; } public void writeTo(DataOutputStream out) throws IOException { } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { } public String toString() { return "DummyHeader(" + num + ")"; } } } libjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/tests/MethodCallTest.java0000644000175000017500000003177711647260573032372 0ustar moellermoeller package org.jgroups.tests; import org.testng.annotations.*; import org.jgroups.blocks.MethodCall; import org.jgroups.util.Util; import org.jgroups.Global; import org.testng.Assert; 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 **/ @Test(groups=Global.FUNCTIONAL) public class MethodCallTest { public static class TargetClass { public static boolean foo(int a, String b) { System.out.println("test(" + a + ", " + b + ')'); return true; } public static 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 static void foobar() { System.out.println("foobar()"); } } final TargetClass target=new TargetClass(); public void testOld() { try { MethodCall mc=new MethodCall("foo", new Object[]{new Integer(22), "Bela"}, new Class[]{int.class,String.class}); Assert.assertEquals(mc.invoke(target), Boolean.TRUE); } catch(Throwable t) { assert false : t.toString(); } } public void testOld2() { try { MethodCall mc=new MethodCall("bar", new Object[]{new String[]{"one", "two", "three"}, "Bela"}, new Class[]{String[].class, String.class}); mc.invoke(target); } catch(Throwable t) { assert false : t.toString(); } } public void testWithNull() { try { MethodCall mc=new MethodCall("foobar", null, (Class[])null); System.out.println("mc: " + mc); mc.invoke(target); } catch(Throwable t) { assert false : t.toString(); } } public void testOldWithNull() { try { MethodCall mc=new MethodCall("bar", new Object[]{new String[]{"one", "two", "three"}, null}, new Class[]{String[].class, String.class}); mc.invoke(target); } catch(Throwable t) { assert false : t.toString(); } } public void testOldWithNull2() { try { MethodCall mc=new MethodCall("bar", new Object[]{null, "Bela"}, new Class[]{String[].class, String.class}); mc.invoke(target); } catch(Throwable t) { assert false : t.toString(); } } public void testOldWithNull3() { try { MethodCall mc=new MethodCall("foobar", null, (Class[])null); mc.invoke(target); } catch(Throwable t) { assert false : t.toString(); } } public void testOldWithNull4() { try { MethodCall mc=new MethodCall("foobar", new Object[0], (Class[])null); mc.invoke(target); } catch(Throwable t) { assert false : t.toString(); } } public void testMethod() { Method m; try { m=TargetClass.class.getMethod("foo", new Class[]{int.class, String.class}); MethodCall mc=new MethodCall(m, new Object[]{new Integer(22), "Bela"}); Assert.assertEquals(mc.invoke(target), Boolean.TRUE); } catch(Throwable t) { assert false : t.toString(); } } public void testTypes() { MethodCall mc; mc=new MethodCall("foo", new Object[]{new Integer(35), "Bela"}, new Class[]{int.class, String.class}); try { Assert.assertEquals(mc.invoke(target), Boolean.TRUE); } catch(Throwable t) { assert false : 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(target); } catch(Throwable t) { assert false : 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(target); } catch(Throwable t) { assert false : t.toString(); } } public void testTypesWithNullArgument2() throws Throwable { MethodCall mc; mc=new MethodCall("bar", new Object[]{new String[]{"one", "two", "three"}, new Object[]{}}, new Class[]{String[].class, String.class}); try { mc.invoke(target); assert false: "we should not get here as there should be an argument mismatch exception"; } catch(IllegalArgumentException ex) { System.out.println("caught IllegalArgumentException - as expected"); } } public void testTypesWithNullArgument3() { MethodCall mc; mc=new MethodCall("foobar", new Object[]{}, new Class[]{}); try { mc.invoke(target); } catch(IllegalArgumentException ex) { assert true : "this was expected"; } catch(Throwable t) { assert false : t.toString(); } } public void testTypesWithNullArgument4() { MethodCall mc; mc=new MethodCall("foobar", null, (Class[])null); try { mc.invoke(target); } catch(IllegalArgumentException ex) { assert true : "this was expected"; } catch(Throwable t) { assert false : t.toString(); } } public void testTypesWithNullArgument5() { MethodCall mc; mc=new MethodCall("foobar", new Object[0], new Class[0]); try { mc.invoke(target); } catch(IllegalArgumentException ex) { assert true : "this was expected"; } catch(Throwable t) { assert false : 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 { Assert.assertEquals(mc.invoke(target), Boolean.TRUE); } catch(Throwable t) { assert false : t.toString(); } } public static 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); } public static void testOLD() throws Throwable { MethodCall methodCall = new MethodCall("someMethod", new Object[] {"abc"}, new Class[]{String.class}); Target target = new Target(); Object result = methodCall.invoke(target); Assert.assertEquals("ABC", result); } public static void testInheritanceOLD() throws Throwable { MethodCall methodCall = new MethodCall("someMethod", new Object[] {"abc"}, new Class[]{String.class}); TargetSubclass target = new TargetSubclass(); Object result = methodCall.invoke(target); Assert.assertEquals("ABC", result); } public static 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); Assert.assertEquals("ABC", result); } public static 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); Assert.assertEquals("ABC", result); } public static 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); Assert.assertEquals("ABC", result); } public static 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); Assert.assertEquals("ABC", result); } /** * This tests whether overriden methods are correctly identified and invoked. */ public static 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); Assert.assertEquals("TargetSubclassABC", result); } public static void testNoArgumentMethodForTYPES() throws Throwable { MethodCall methodCall = new MethodCall("noArgumentMethod", new Object[0], new Class[0]); TargetSubclass target = new TargetSubclass(); Object result = methodCall.invoke(target); Assert.assertEquals("noArgumentMethodResult", result); } public static 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); Assert.assertEquals("ABC", result); } public static 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); Assert.assertEquals("ABC", result); } public static 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); Assert.assertEquals(m.get("name"), "Bela"); Assert.assertEquals(m.get("id"), new Integer(322649)); } private static MethodCall marshalAndUnmarshal(MethodCall m) throws Exception { byte[] buf=Util.objectToByteBuffer(m); return (MethodCall)Util.objectFromByteBuffer(buf); } public static class Target { public static String someMethod(String arg) { return arg.toUpperCase(); } public String overriddenMethod(String arg) { return "Target" + arg.toUpperCase(); } public static String noArgumentMethod() { return "noArgumentMethodResult"; } } public static class TargetSubclass extends Target { public String overriddenMethod(String arg) { return "TargetSubclass" + arg.toUpperCase(); } } } ././@LongLink0000000000000000000000000000014600000000000011566 Lustar rootrootlibjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/tests/NakReceiverWindowTest.javalibjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/tests/NakReceiverWindowTest.jav0000644000175000017500000005715011647260573033574 0ustar moellermoeller package org.jgroups.tests; import org.jgroups.Address; import org.jgroups.Global; import org.jgroups.Message; import org.jgroups.stack.NakReceiverWindow; import org.jgroups.stack.Retransmitter; import org.jgroups.util.TimeScheduler; import org.jgroups.util.Util; import org.testng.Assert; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; import java.util.List; @Test(groups=Global.FUNCTIONAL) public class NakReceiverWindowTest { private static final Address sender=Util.createRandomAddress(); private static final MyRetransmitCommand cmd=new MyRetransmitCommand(); @DataProvider(name="createTimer") Object[][] createTimer() { return Util.createTimer(); } @Test(dataProvider="createTimer") public void test1(TimeScheduler timer) throws Exception { try { NakReceiverWindow win=new NakReceiverWindow(sender, cmd, 1, timer); check(win, 0, 1, 1); assert win.get(23) == null; } finally { timer.stop(); } } @Test(dataProvider="createTimer") public void test2(TimeScheduler timer) throws Exception { try { NakReceiverWindow win=new NakReceiverWindow(sender, cmd, 100, timer); check(win, 0, 100, 100); } finally { timer.stop(); } } @Test(dataProvider="createTimer") public void test3(TimeScheduler timer) throws Exception { try { NakReceiverWindow win=new NakReceiverWindow(sender, cmd, 0, timer); win.add(1, new Message()); assert win.get(1) != null; check(win, 0, 1, 0); win.add(2, new Message()); check(win, 0, 2, 0); assert win.get(2) != null; win.remove(); check(win, 0, 2, 1); win.remove(); check(win, 0, 2, 2); } finally { timer.stop(); } } @Test(dataProvider="createTimer") public void test4(TimeScheduler timer) throws Exception { try { NakReceiverWindow win=new NakReceiverWindow(sender, cmd, 1, timer); win.add(2, new Message()); check(win, 0, 2, 1); } finally { timer.stop(); } } @Test(dataProvider="createTimer") public void test5(TimeScheduler timer) throws Exception { try { NakReceiverWindow win=new NakReceiverWindow(sender, cmd, 100, timer); win.add(101, new Message()); win.add(100, new Message()); check(win, 0, 101, 100); } finally { timer.stop(); } } @Test(dataProvider="createTimer") public void test6(TimeScheduler timer) throws Exception { try { 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); } finally { timer.stop(); } } @Test(dataProvider="createTimer") public void testLowerBounds(TimeScheduler timer) { try { 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); } finally { timer.stop(); } } @Test(dataProvider="createTimer") public void test7(TimeScheduler timer) throws Exception { try { 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); } finally { timer.stop(); } } @Test(dataProvider="createTimer") public void testLowerBounds2(TimeScheduler timer) throws Exception { try { 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); } finally { timer.stop(); } } @Test(dataProvider="createTimer") public void test8(TimeScheduler timer) throws Exception { try { 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); } finally { timer.stop(); } } @Test(dataProvider="createTimer") public void testAdd(TimeScheduler timer) throws Exception { try { 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); } finally { timer.stop(); } } @Test(dataProvider="createTimer") public void test9(TimeScheduler timer) throws Exception { try { 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); assert win.get(2) != null; 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); } finally { timer.stop(); } } @Test(dataProvider="createTimer") public void testHighestDelivered(TimeScheduler timer) throws Exception { try { 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); } finally { timer.stop(); } } @Test(dataProvider="createTimer") public void testMissingMessages(TimeScheduler timer) throws Exception { try { 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); } finally { timer.stop(); } } @Test(dataProvider="createTimer") public void testMissingMessages2(TimeScheduler timer) throws Exception { try { 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); } finally { timer.stop(); } } @Test(dataProvider="createTimer") public void testMissingMessages3(TimeScheduler timer) throws Exception { try { 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); } finally { timer.stop(); } } @Test(dataProvider="createTimer") public void testMissingMessages4(TimeScheduler timer) throws Exception { try { 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); } finally { timer.stop(); } } @Test(dataProvider="createTimer") public void testMissingMessages5(TimeScheduler timer) throws Exception { try { 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); } finally { timer.stop(); } } @Test(dataProvider="createTimer") public void test10(TimeScheduler timer) throws Exception { try { 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); } finally { timer.stop(); } } @Test(dataProvider="createTimer") public void test10a(TimeScheduler timer) throws Exception { try { 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); } finally { timer.stop(); } } @Test(dataProvider="createTimer") public void test11(TimeScheduler timer) throws Exception { try { 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.destroy(); check(win, 0, 0, 0); } finally { timer.stop(); } } @Test(dataProvider="createTimer") public void test12(TimeScheduler timer) throws Exception { try { 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))); Assert.assertEquals(1, ((Integer)win.remove().getObject()).intValue()); Assert.assertEquals(2, ((Integer)win.remove().getObject()).intValue()); Assert.assertEquals(3, ((Integer)win.remove().getObject()).intValue()); } finally { timer.stop(); } } @Test(dataProvider="createTimer") public void test13(TimeScheduler timer) throws Exception { try { 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); } finally { timer.stop(); } } @Test(dataProvider="createTimer") public void testAddOOBAtHead(TimeScheduler timer) throws Exception { try { NakReceiverWindow win=new NakReceiverWindow(sender, cmd, 0, timer); boolean rc; rc=win.add(0, oob()); assert !(rc); rc=win.add(1, oob()); assert rc; rc=win.add(1, oob()); assert !(rc); } finally { timer.stop(); } } @Test(dataProvider="createTimer") public void testAddOOBAtTail(TimeScheduler timer) throws Exception { try { NakReceiverWindow win=new NakReceiverWindow(sender, cmd, 0, timer); boolean rc; rc=win.add(1, oob()); assert rc; rc=win.add(2, oob()); assert rc; rc=win.add(2, oob()); assert !(rc); } finally { timer.stop(); } } @Test(dataProvider="createTimer") public void testAddOOBInTheMiddle(TimeScheduler timer) throws Exception { try { NakReceiverWindow win=new NakReceiverWindow(sender, cmd, 0, timer); boolean rc; rc=win.add(3, oob()); assert rc; rc=win.add(3, oob()); assert !(rc); rc=win.add(1, oob()); assert rc; rc=win.add(1, oob()); assert !(rc); rc=win.add(2, oob()); assert rc; rc=win.add(2, oob()); assert !(rc); } finally { timer.stop(); } } @Test(dataProvider="createTimer") public void testUpdateHighestSeen(TimeScheduler timer) { add(1000, timer); add(2000, timer); add(3000, timer); add(4000, timer); add(5000, timer); add(10000, timer); add(15000, timer); add(20000, timer); add(30000, timer); } @Test(dataProvider="createTimer") public void test1000(TimeScheduler timer) { add(1000, timer); } @Test(dataProvider="createTimer") public void test10000(TimeScheduler timer) { add(10000, timer); } @Test(dataProvider="createTimer") public void testRemoveRegularAndOOBMessages(TimeScheduler timer) { try { NakReceiverWindow win=new NakReceiverWindow(sender, cmd, 0, timer); win.add(1, msg()); System.out.println("win = " + win); win.remove(); System.out.println("win = " + win); assert win.getHighestDelivered() == 1; win.add(3, msg()); win.remove(); System.out.println("win = " + win); assert win.getHighestDelivered() == 1; win.add(2, oob()); System.out.println("win = " + win); assert win.getHighestDelivered() == 1 : "highest_delivered should be 2, but is " + win.getHighestDelivered(); } finally { timer.stop(); } } @Test(dataProvider="createTimer") public void testRemoveMany(TimeScheduler timer) { try { NakReceiverWindow win=new NakReceiverWindow(sender, cmd, 0, timer); win.add(1, msg()); win.add(2, msg()); win.add(3, msg()); win.add(5, msg()); win.add(6, msg()); System.out.println("win = " + win); List msgs=win.removeMany(null); System.out.println("msgs = " + msgs); assert msgs.size() == 3; win.add(4, msg()); msgs=win.removeMany(null); System.out.println("msgs = " + msgs); assert msgs.size() == 3; } finally { timer.stop(); } } @Test(dataProvider="createTimer") public void testRemoveMany2(TimeScheduler timer) { try { NakReceiverWindow win=new NakReceiverWindow(sender, cmd, 4, timer); win.add(5, msg()); win.add(6, msg()); win.add(7, msg()); win.add(9, msg()); win.add(10, msg()); System.out.println("win = " + win); List msgs=win.removeMany(null); System.out.println("msgs = " + msgs); assert msgs.size() == 3; win.add(8, msg()); msgs=win.removeMany(null); System.out.println("msgs = " + msgs); assert msgs.size() == 3; } finally { timer.stop(); } } @Test(dataProvider="createTimer") public void testRetransmitter(TimeScheduler timer) { try { NakReceiverWindow win=new NakReceiverWindow(sender, cmd, 0, timer); win.add(1, msg()); win.add(2, msg()); win.add(3, msg()); win.add(5, msg()); win.add(6, msg()); System.out.println("win = " + win); int num_pending_xmits=win.getPendingXmits(); assert num_pending_xmits == 1; win.add(4, msg()); num_pending_xmits=win.getPendingXmits(); assert num_pending_xmits == 0; } finally { timer.stop(); } } private static void add(int num_msgs, TimeScheduler timer) { try { 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"); } finally { timer.stop(); } } private static Message oob() { Message retval=new Message(); retval.setFlag(Message.OOB); return retval; } private static Message msg() { return new Message(); } private static void check(NakReceiverWindow win, long lowest, long highest_received, long highest_delivered) { Assert.assertEquals(win.getLowestSeen(), lowest, "lowest=" + lowest + ", win.lowest=" + win.getLowestSeen()); Assert.assertEquals(win.getHighestReceived(), highest_received, "highest_received=" + highest_received + ", win.highest_received=" + win.getHighestReceived()); Assert.assertEquals(win.getHighestDelivered(), highest_delivered, "highest_delivered=" + highest_delivered + ", win.highest_delivered=" + win.getHighestDelivered()); } private static class MyRetransmitCommand implements Retransmitter.RetransmitCommand { public void retransmit(long first_seqno, long last_seqno, Address sender) { } } } ././@LongLink0000000000000000000000000000014700000000000011567 Lustar rootrootlibjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/tests/NakReceiverWindowTest2.javalibjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/tests/NakReceiverWindowTest2.ja0000644000175000017500000002173511647260573033470 0ustar moellermoellerpackage org.jgroups.tests; import org.jgroups.Address; import org.jgroups.Global; import org.jgroups.Message; import org.jgroups.protocols.pbcast.NakAckHeader; import org.jgroups.stack.NakReceiverWindow; import org.jgroups.stack.Retransmitter; import org.jgroups.util.DefaultTimeScheduler; import org.jgroups.util.Util; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Set; import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CyclicBarrier; import java.util.concurrent.atomic.AtomicInteger; /** * Stresses the NakReceiverWindow in isolation(https://jira.jboss.org/jira/browse/JGRP-1103) * @author Bela Ban */ @Test(groups=Global.FUNCTIONAL, sequential=true) public class NakReceiverWindowTest2 { final static int NUM_THREADS=200; final static int NUM_MSGS=5000; static final short NAKACK_ID=100; final Address self=Util.createRandomAddress(); final Address sender=Util.createRandomAddress(); final CyclicBarrier barrier=new CyclicBarrier(NUM_THREADS +1); NakReceiverWindow win; @BeforeMethod void init() { win=new NakReceiverWindow(self, new Retransmitter.RetransmitCommand() { public void retransmit(long first_seqno, long last_seqno, Address sender) { } }, 0, 0, new DefaultTimeScheduler(2)); } @AfterMethod void cleanup() { win.destroy(); } /** * Has NUM_THREAD threads insert NUM_MSGS messages concurrently, checks whether messages are added only once * @throws BrokenBarrierException * @throws InterruptedException */ @Test(invocationCount=10) public void testConcurrentInsertions() throws BrokenBarrierException, InterruptedException { Sender[] senders=new Sender[NUM_THREADS]; ConcurrentMap successful_adds=new ConcurrentHashMap(); for(int i=1; i <= NUM_MSGS; i++) successful_adds.put((long)i, new AtomicInteger(0)); for(int i=0; i < senders.length; i++) { senders[i]=new Sender(NUM_MSGS, win, sender, barrier, successful_adds); senders[i].start(); } Util.sleep(2000); System.out.println("Concurrently inserting " + NUM_MSGS + " messages with " + NUM_THREADS + " threads"); barrier.await(); for(int i=0; i < senders.length; i++) senders[i].join(20000); System.out.println("OK: " + NUM_MSGS + " were added to the NakReceiverWindow concurrently by " + NUM_THREADS + " threads"); Set keys=successful_adds.keySet(); System.out.println("checking for missing or duplicate seqnos in " + keys.size() + " seqnos:"); for(int i=1; i <= NUM_MSGS; i++) { AtomicInteger val=successful_adds.get((long)i); if(val.get() != 1) System.err.println(i + " was not added exactly once (successful insertions=" + val.get() + ")"); } for(int i=1; i <= NUM_MSGS; i++) { AtomicInteger val=successful_adds.get((long)i); assert val != null : i + " is missing in " + successful_adds.keySet(); assert val.get() == 1 : i + " was not added exactly once (successful insertions=" + val.get() + ")"; } System.out.println("OK: " + keys.size() + " seqnos were added exactly once"); } @Test(invocationCount=5) public void testConcurrentRandomInsertions() throws BrokenBarrierException, InterruptedException { Sender[] senders=new RandomSender[NUM_THREADS]; ConcurrentMap successful_adds=new ConcurrentHashMap(); for(int i=1; i <= NUM_MSGS; i++) successful_adds.put((long)i, new AtomicInteger(0)); for(int i=0; i < senders.length; i++) { senders[i]=new RandomSender(NUM_MSGS, win, sender, barrier, successful_adds); senders[i].start(); } Util.sleep(2000); System.out.println("Concurrently inserting " + NUM_MSGS + " messages with " + NUM_THREADS + " threads"); barrier.await(); for(int i=0; i < senders.length; i++) senders[i].join(20000); System.out.println("OK: " + NUM_MSGS + " were added to the NakReceiverWindow concurrently by " + NUM_THREADS + " threads"); Set keys=successful_adds.keySet(); System.out.println("checking for missing or duplicate seqnos in " + keys.size() + " seqnos:"); for(int i=1; i <= NUM_MSGS; i++) { AtomicInteger val=successful_adds.get((long)i); if(val.get() != 1) System.err.println(i + " was not added exactly once (successful insertions=" + val.get() + ")"); } for(int i=1; i <= NUM_MSGS; i++) { AtomicInteger val=successful_adds.get((long)i); assert val != null : i + " is missing in " + successful_adds.keySet(); assert val.get() == 1 : i + " was not added exactly once (successful insertions=" + val.get() + ")"; } System.out.println("OK: " + keys.size() + " seqnos were added exactly once"); } @Test(invocationCount=5) public void testConcurrentInsertionOfSameSeqno() throws BrokenBarrierException, InterruptedException { Sender[] senders=new SameSeqnoSender[NUM_THREADS]; ConcurrentMap successful_adds=new ConcurrentHashMap(); for(int i=1; i <= NUM_MSGS; i++) successful_adds.put((long)i, new AtomicInteger(0)); for(int i=0; i < senders.length; i++) { senders[i]=new SameSeqnoSender(NUM_MSGS, win, sender, barrier, successful_adds); senders[i].start(); } Util.sleep(2000); System.out.println("Concurrently inserting 1 message with " + NUM_THREADS + " threads"); barrier.await(); for(int i=0; i < senders.length; i++) senders[i].join(20000); System.out.println("OK: 1 message was added to the NakReceiverWindow concurrently by " + NUM_THREADS + " threads"); Set keys=successful_adds.keySet(); System.out.println("checking for missing or duplicate seqnos in " + keys.size() + " seqnos:"); AtomicInteger val=successful_adds.get(1L); if(val.get() != 1) System.err.println("1 was not added exactly once (successful insertions=" + val.get() + ")"); assert val.get() == 1 : "1 was not added exactly once (successful insertions=" + val.get() + ")"; System.out.println("OK: 1 seqno was added exactly once"); } static class Sender extends Thread { final int num; final NakReceiverWindow win; final Address sender; final CyclicBarrier barrier; final ConcurrentMap map; public Sender(int num, NakReceiverWindow win, Address sender, CyclicBarrier barrier, ConcurrentMap map) { this.num=num; this.win=win; this.sender=sender; this.barrier=barrier; this.map=map; } public void run() { waitForBarrier(); for(int i=1; i <= num; i++) add(i); } protected void add(long seqno) { NakAckHeader hdr=NakAckHeader.createMessageHeader(seqno); Message msg=new Message(null, sender, "hello"); msg.putHeader(NAKACK_ID, hdr); boolean added=win.add(seqno, msg); if(added) { AtomicInteger val=map.get((long)seqno); val.incrementAndGet(); } } protected void waitForBarrier() { try { barrier.await(); } catch(Exception e) { e.printStackTrace(); } } } static class RandomSender extends Sender { public RandomSender(int num, NakReceiverWindow win, Address sender, CyclicBarrier barrier, ConcurrentMap map) { super(num, win, sender, barrier, map); } public void run() { final List seqnos; seqnos=new ArrayList(num); for(long i=1; i <= num; i++) seqnos.add(i); // now randomize the seqnos: Collections.shuffle(seqnos); waitForBarrier(); for(long seqno: seqnos) add(seqno); } } /** * Inserts seqno 1 NUM_MSGS times */ static class SameSeqnoSender extends Sender { public SameSeqnoSender(int num, NakReceiverWindow win, Address sender, CyclicBarrier barrier, ConcurrentMap map) { super(num, win, sender, barrier, map); } public void run() { waitForBarrier(); for(int i=1; i <= num; i++) add(1L); } } } libjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/tests/NakackTest.java0000644000175000017500000003022511647260573031531 0ustar moellermoeller package org.jgroups.tests; import org.jgroups.*; import org.jgroups.debug.Simulator; import org.jgroups.protocols.pbcast.NAKACK; import org.jgroups.stack.Protocol; import org.jgroups.util.UUID; import org.jgroups.util.MutableDigest; import org.jgroups.util.Digest; import org.testng.Assert; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.Hashtable; import java.util.Vector; import java.util.Arrays; /** * Tests the reliable FIFO (NAKACK) protocol *

                * Two sender peers send 1000 messages to the group, where each message contains * a long value, mirroring seqnos used. A receiver peer receives the messages * from each sender and checks that seqnos are received in the correct order. *

                * This test makes use of Simulator to test the protocol in * isolation from a JChannel. Each peer is wrapped in a Simulator instance and * the instances are linked together to form the group. *

                * An object all_msgs_recd is used to allow the main test thread to discover when * all sent messages have been received. *

                * The test case passes if the expected number of messages is received, and messages * are received in order from each sender. This implies that: * (i) all messages from each peer were received (reliable) and * (ii) all messages from each peer are received in order (FIFO) * @author Richard Achmatowicz * @author Bela Ban */ @Test(groups=Global.FUNCTIONAL, sequential=true) public class NakackTest { final static int NUM_PEERS=3; final static int NUM_MSGS=1000; final static int WAIT_TIMEOUT=10; // secs final static int MSGS_PER_STATUS_LINE=100; // convey assertion failure from thread to main framework static boolean notFIFO=false; static boolean allMsgsReceived=false; Address[] addresses=new Address[NUM_PEERS]; Vector

                members=null; View view; Simulator[] simulators=new Simulator[NUM_PEERS]; NAKACK[] layers=new NAKACK[NUM_PEERS]; Protocol[][] stacks=new Protocol[NUM_PEERS][]; Thread[] threads=new Thread[NUM_PEERS]; //define senders and receivers boolean[] isSender=new boolean[NUM_PEERS]; // used to wait for signal that all messages received static final Object all_msgs_recd=new Object(); /** * Set up a number of simulator instances wrapping NAKACK */ @BeforeMethod public void setUp() throws Exception { // define the senders and the receivers isSender[0]=false; isSender[1]=true; isSender[2]=true; // dummy IP addresses and ports for(int i=0; i < addresses.length; i++) { UUID uuid=UUID.randomUUID(); UUID.add(uuid, "node-" + i); addresses[i]=uuid; } // dummy set of members which works for all three simulators members=new Vector
                (); members.addAll(Arrays.asList(addresses).subList(0, NUM_PEERS)); // create a dummy View(creator, timestamp, member set) view=new View(addresses[0], 1, members); // create new simulator instances for(int i=0; i < NUM_PEERS; i++) { // create the simulator instance // at this stage, the ProtocolAdapter should be created and the timer present simulators[i]=new Simulator(); simulators[i].setLocalAddress(addresses[i]); simulators[i].setView(view); // set up the protocol under test layers[i]=new NAKACK(); // set up its properties layers[i].setUseMcastXmit(true); // our protocol stack under test consists of one protocol stacks[i]=new Protocol[]{layers[i]}; // initalise the protocol stack simulators[i].setProtocolStack(stacks[i]); } // describe the configuration of the three simulators for(int i=0; i < NUM_PEERS; i++) { for(int j=0; j < NUM_PEERS; j++) { if(i == j) simulators[i].addMember(addresses[j]); else simulators[i].addMember(addresses[j], simulators[j]); } } // set up the receiver callbacks for each simulator Simulator.Receiver[] receivers=new Simulator.Receiver[NUM_PEERS]; // set up the sender and the receiver callbacks, according to whether // the peer is a sender or a receiver for(int i=0; i < NUM_PEERS; i++) { if(isSender[i]) receivers[i]=new SenderPeer(simulators[i]); else receivers[i]=new ReceiverPeer(simulators[i]); simulators[i].setReceiver(receivers[i]); } // start the simulators for(int i=0; i < NUM_PEERS; i++) simulators[i].start(); MutableDigest digest=new MutableDigest(NUM_PEERS); for(Address addr: addresses) digest.add(new Digest(addr, 0, 0)); for(int i=0; i < NUM_PEERS; i++) { layers[i].down(new Event(Event.SET_DIGEST, digest)); } } @AfterMethod public void tearDown() throws Exception { // stop the simulators for(int i=0; i < NUM_PEERS; i++) simulators[i].stop(); } /** * Test to see thyat NAKACK delivery is reliable and FIFO. */ public void testReceptionOfAllMessages() { // start the NAKACK peers and let them exchange messages for(int i=0; i < NUM_PEERS; i++) { threads[i]=new MyNAKACKPeer(simulators[i], isSender[i]); threads[i].start(); } // wait for the receiver peer to signal that it has received messages, or timeout synchronized(all_msgs_recd) { try { all_msgs_recd.wait(WAIT_TIMEOUT * 1000); } catch(InterruptedException e) { System.out.println("main thread interrupted"); } } // wait for the threads to terminate try { for(int i=0; i < NUM_PEERS; i++) { threads[i].join(); } } catch(InterruptedException e) { } // the test fails if: // - a seqno is received out of order (not FIFO), or // - not all messages are received in time allotted (allMsgsReceived) Assert.assertTrue(allMsgsReceived, "Incorrect number of messages received by the receiver thread"); Assert.assertFalse(notFIFO, "Sequenece numbers for a peer not in correct order"); } /** * This is called by the Simulator when a message comes back up the stack. * Used by message senders to simply display messages received from other peers. */ static class SenderPeer implements Simulator.Receiver { Simulator simulator=null; int num_mgs_received=0; SenderPeer(Simulator s) { this.simulator=s; } // keep track of how many messages were received public void receive(Event evt) { if(evt.getType() == Event.MSG) { num_mgs_received++; if(num_mgs_received % MSGS_PER_STATUS_LINE == 0) System.out.println("<" + simulator.getLocalAddress() + ">:" + "<== " + num_mgs_received); } } public int getNumberOfReceivedMessages() { return num_mgs_received; } } /** * This is called by the Simulator when a message comes back up the stack. * This method should do the following: * - receive messages from senders * - check that sequence numbers for each sender are in order (with no gaps) * - terminate when correct number of messages have been received */ static class ReceiverPeer implements Simulator.Receiver { Simulator simulator=null; int num_mgs_received=0; long starting_seqno=1; long last_seqno=starting_seqno; Hashtable senders=new Hashtable(); Message msg; Address sender; Long s; long received_seqno; ReceiverPeer(Simulator s) { this.simulator=s; } public synchronized void receive(Event evt) { if(evt.getType() == Event.MSG) { // keep track of seqno ordering of messages received msg=(Message)evt.getArg(); sender=msg.getSrc(); // get the expected next seqno for this sender s=senders.get(sender); if(s == null) { s=new Long(starting_seqno); senders.put(sender, s); } last_seqno=s.longValue(); try { s=(Long)msg.getObject(); received_seqno=s.longValue(); num_mgs_received++; // 1. check if sequence numbers are in sequence if(received_seqno == last_seqno) { // correct - update with next expected seqno senders.put(sender, new Long(last_seqno + 1)); } else { // error, terminate test notFIFO=true; Assert.fail("FAIL: received msg #" + received_seqno + ", expected " + last_seqno); } Address address=simulator.getLocalAddress(); if(received_seqno % MSGS_PER_STATUS_LINE == 0 && received_seqno > 0) System.out.println("<" + address + ">:" + "PASS: received msg #" + received_seqno + " from " + sender); // condition to terminate the test - all messages received (whether in // correct order or not) if(num_mgs_received >= NakackTest.NUM_MSGS * (NUM_PEERS - 1)) { // indicate that we have received the required number of messages // to differentiate between timeout and notifyAll cases on monitor allMsgsReceived=true; // signal that all messages have been received - this will allow the receiver // thread to terminate normally synchronized(all_msgs_recd) { all_msgs_recd.notifyAll(); } } } catch(Exception ex) { System.out.println(ex.toString()); // log.error("NakackTest.CheckNoGaps.up()", ex); } } } public int getNumberOfReceivedMessages() { return num_mgs_received; } } static class MyNAKACKPeer extends Thread { Simulator s=null; boolean sender=false; public MyNAKACKPeer(Simulator s, boolean sender) { this.s=s; this.sender=sender; } public void run() { // senders send NUM_MSGS messages to all peers, beginning with seqno 1 if(sender) { Address address=s.getLocalAddress(); // send a collection of dummy messages by mcast to the stack under test for(int i=1; i <= NUM_MSGS; i++) { Message msg=new Message(null, address, new Long(i)); Event evt=new Event(Event.MSG, msg); // call Simulator.send() to introduce the event into the stack under test s.send(evt); // status indicator if(i % MSGS_PER_STATUS_LINE == 0) System.out.println("<" + address + ">:" + " ==> " + i); } } if(!sender) { // wait for the receiver callback to signal that it has received messages, or timeout // this just causes this thread to block until its receiver has finished synchronized(all_msgs_recd) { try { all_msgs_recd.wait(WAIT_TIMEOUT * 1000); } catch(InterruptedException e) { System.out.println("main thread interrupted"); } } } } } } libjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/tests/OrderingTest.java0000644000175000017500000002031711647260573032113 0ustar moellermoellerpackage org.jgroups.tests; import org.jgroups.*; import org.jgroups.protocols.*; import org.jgroups.protocols.pbcast.GMS; import org.jgroups.protocols.pbcast.NAKACK; import org.jgroups.protocols.pbcast.STABLE; import org.jgroups.stack.ProtocolStack; import org.jgroups.util.Util; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicInteger; /** * Tests message ordering * @author Bela Ban */ @Test(groups=Global.FUNCTIONAL,sequential=true) public class OrderingTest { protected static final int NUM_MSGS=200000; protected static final int NUM_SENDERS=2; protected static final int TOTAL_NUM_MSGS=NUM_MSGS * NUM_SENDERS; protected JChannel[] channels=new JChannel[NUM_SENDERS]; protected MySender[] senders=new MySender[NUM_SENDERS]; @BeforeMethod void init() throws Exception { System.out.println("creating " + NUM_SENDERS + " channels"); for(int i=0; i < channels.length; i++) { channels[i]=createChannel(); channels[i].setReceiver(new MyReceiver()); senders[i]=new MySender(channels[i]); channels[i].connect("OrderingTest.testFIFOOrder"); } System.out.println("done"); System.out.println("\nwaiting for a cluster of " + NUM_SENDERS + " to form:"); boolean done=true; for(int i=0; i < 20; i++) { for(JChannel ch: channels) { if(ch.getView().size() != NUM_SENDERS) { done=false; break; } } if(!done) Util.sleep(1000); else break; } } @AfterMethod void destroy() { for(int i=channels.length-1; i >= 0; i--) { Util.close(channels[i]); } } protected static JChannel createChannel() throws Exception { JChannel ch=new JChannel(false); ProtocolStack stack=new ProtocolStack(); ch.setProtocolStack(stack); stack.addProtocol(new SHARED_LOOPBACK().setValue("oob_thread_pool_rejection_policy", "run") .setValue("thread_pool_rejection_policy", "run") .setValue("thread_pool_queue_max_size", 100000)) .addProtocol(new PING()) .addProtocol(new MERGE2()) .addProtocol(new FD_SOCK()) .addProtocol(new VERIFY_SUSPECT()) .addProtocol(new BARRIER()) .addProtocol(new NAKACK().setValue("use_mcast_xmit", false).setValue("discard_delivered_msgs", true)) .addProtocol(new UNICAST2().setValue("stable_interval", 10000).setValue("max_bytes", 50000)) .addProtocol(new STABLE().setValue("max_bytes", 50000)) .addProtocol(new GMS().setValue("print_local_addr", false)) .addProtocol(new UFC().setValue("max_credits", 2000000)) .addProtocol(new MFC().setValue("max_credits", 2000000)) .addProtocol(new FRAG2()); stack.init(); return ch; } /*protected static JChannel createChannel() throws ChannelException { return new JChannel("/home/bela/fast.xml"); }*/ public void testFIFOOrdering() throws Exception { assert channels[0].getView().size() == NUM_SENDERS : "view[0] is " + channels[0].getView().size(); System.out.println("done, view is " + channels[0].getView()); System.out.println("\nstarting to send " + NUM_MSGS + " messages"); for(int i=0; i < senders.length; i++) senders[i].start(); for(int i=0; i < senders.length; i++) { MySender sender=senders[i]; sender.join(); } System.out.println("senders done"); System.out.println("\nwaiting for message reception by all receivers:"); boolean done; for(int i=0; i < 50; i++) { done=true; for(JChannel ch: channels) { MyReceiver receiver=(MyReceiver)ch.getReceiver(); int received=receiver.getReceived(); System.out.println(ch.getAddress() + ": " + received); STABLE stable=(STABLE)ch.getProtocolStack().findProtocol(STABLE.class); stable.runMessageGarbageCollection(); if(received != TOTAL_NUM_MSGS) { done=false; break; } } if(!done) Util.sleep(1000); else break; } for(JChannel ch: channels) { MyReceiver receiver=(MyReceiver)ch.getReceiver(); System.out.println(ch.getAddress() + ": " + receiver.getReceived()); } for(JChannel ch: channels) { MyReceiver receiver=(MyReceiver)ch.getReceiver(); assert receiver.getReceived() == TOTAL_NUM_MSGS : "receiver had " + receiver.getReceived() + " messages (expected=" + TOTAL_NUM_MSGS + ")"; } System.out.println("done"); System.out.println("\nchecking message order"); for(JChannel ch: channels) { MyReceiver receiver=(MyReceiver)ch.getReceiver(); System.out.print(ch.getAddress() + ": "); boolean ok=receiver.getNumberOfErrors() == 0; System.out.println(ok? "OK" : "FAIL (" + receiver.getNumberOfErrors() + " errors)"); assert ok : receiver.getNumberOfErrors() + " errors"; } System.out.println("done"); } /* private static boolean checkOrder(ConcurrentMap> map, boolean print_incorrect_elements) { boolean retval=true; for(Map.Entry> entry: map.entrySet()) { Address sender=entry.getKey(); List list=entry.getValue(); int curr=1; for(Integer num: list) { if(!num.equals(curr)) { retval=false; if(!print_incorrect_elements) return false; System.err.println("element " + num + " != " + curr); } curr++; } } return retval; }*/ protected static class MySender extends Thread { protected final JChannel ch; public MySender(JChannel ch) { this.ch=ch; } public void run() { for(int i=1; i <= NUM_MSGS; i++) { try { Message msg=new Message(null, null, new Integer(i)); ch.send(msg); if(i % 100000 == 0) System.out.println(Thread.currentThread().getId() + ": " + i + " sent"); } catch(Exception e) { e.printStackTrace(); } } } } protected static class MyReceiver extends ReceiverAdapter { protected final ConcurrentMap map=new ConcurrentHashMap(); final AtomicInteger received=new AtomicInteger(0); protected int num_errors=0; public int getNumberOfErrors() { return num_errors; } public int getReceived() { return received.intValue(); } public void receive(Message msg) { Integer num=(Integer)msg.getObject(); Address sender=msg.getSrc(); Integer current_seqno=map.get(sender); if(current_seqno == null) { current_seqno=new Integer(1); Integer tmp=map.putIfAbsent(sender, current_seqno); if(tmp != null) current_seqno=tmp; } if(current_seqno.intValue() == num) map.put(sender, current_seqno + 1); else num_errors++; if(received.incrementAndGet() % 100000 == 0) System.out.println("received " + received); } } /*public static void main(String[] args) throws Exception { OrderingTest test=new OrderingTest(); test.init(); test.testFIFOOrdering(); test.destroy(); }*/ } libjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/tests/ProgrammaticApiTest.java0000644000175000017500000000754611647260573033432 0ustar moellermoellerpackage org.jgroups.tests; import org.jgroups.*; import org.jgroups.protocols.*; import org.jgroups.protocols.pbcast.GMS; import org.jgroups.protocols.pbcast.NAKACK; import org.jgroups.protocols.pbcast.STABLE; import org.jgroups.stack.Protocol; import org.jgroups.stack.ProtocolStack; import org.jgroups.util.Util; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.net.InetAddress; /** * @author Bela Ban */ @Test(groups=Global.FUNCTIONAL,sequential=true) public class ProgrammaticApiTest { JChannel c1, c2; @BeforeMethod void init() { c1=new JChannel(false); c1.setName("A"); c2=new JChannel(false); c2.setName("B"); } @AfterMethod void destroy() { Util.close(c2, c1); } public void testChannelCreation() throws Exception { MyReceiver receiver=new MyReceiver(null); c1.setReceiver(receiver); ProtocolStack stack=new ProtocolStack(); c1.setProtocolStack(stack); stack.addProtocol(new SHARED_LOOPBACK()).addProtocol(new MockProtocol1()).addProtocol(new MockProtocol2()); stack.init(); c1.connect("demo"); Protocol transport=stack.getTransport(); transport.up(new Event(Event.MSG, new Message(null, Util.createRandomAddress(), "hello world"))); assert receiver.getNumMsgsReceived() == 1; } public void testSharedTransport() throws Exception { ProtocolStack stack1=new ProtocolStack(), stack2=new ProtocolStack(); c1.setProtocolStack(stack1); c2.setProtocolStack(stack2); MyReceiver receiver1=new MyReceiver("A"), receiver2=new MyReceiver("B"); UDP shared_transport=(UDP)new UDP().setValue("singleton_name", "shared"); stack1.addProtocol(shared_transport).addProtocols(createProtocols()); stack2.addProtocol(shared_transport).addProtocols(createProtocols()); stack1.init(); stack2.init(); c1.setReceiver(receiver1); c2.setReceiver(receiver2); c1.connect("cluster-one"); c2.connect("cluster-two"); for(int i=0; i < 10; i++) c1.send(new Message(null, null, "hello-" + i)); for(int i=0; i < 5; i++) c2.send(new Message(null, null, "hello-" + i)); for(int i =0; i < 20; i++) { if(receiver1.getNumMsgsReceived() == 10 && receiver2.getNumMsgsReceived() == 5) break; Util.sleep(500); } assert receiver1.getNumMsgsReceived() == 10 : "num msgs for A: " + receiver1.getNumMsgsReceived() + " (expected=10)"; assert receiver2.getNumMsgsReceived() == 5 : "num msgs for B: " + receiver1.getNumMsgsReceived() + " (expected=5)"; } protected static class MockProtocol1 extends Protocol { } protected static class MockProtocol2 extends Protocol { } static Protocol[] createProtocols() { return new Protocol[] { new PING(), new MERGE2(), new FD_SOCK(), new FD_ALL().setValue("timeout", 12000).setValue("interval", 3000), new VERIFY_SUSPECT(), new BARRIER(), new NAKACK(), new UNICAST2(), new STABLE(), new GMS(), new UFC(), new MFC(), new FRAG2() }; } static class MyReceiver extends ReceiverAdapter { int num_msgs_received=0; final String name; public MyReceiver(String name) { this.name=name; } public int getNumMsgsReceived() { return num_msgs_received; } public void receive(Message msg) { System.out.println((name != null? "[" + name + "]" : "") + "<< " + msg.getObject()); num_msgs_received++; } } } libjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/tests/PromiseTest.java0000644000175000017500000000562611647260573031766 0ustar moellermoeller package org.jgroups.tests; import org.jgroups.Global; import org.jgroups.TimeoutException; import org.jgroups.util.Promise; import org.jgroups.util.Util; import org.testng.Assert; import org.testng.annotations.Test; /** * Various test cases for Promise * @author Bela Ban */ @Test(groups=Global.FUNCTIONAL) public class PromiseTest { public static void testGetResultNoTimeout() { final Promise p=new Promise(); Object result; new ResultSetter(p, 500).start(); result=p.getResult(0); Assert.assertEquals(Boolean.TRUE, result); } public static void testGetResultNoTimeout_ResultAlreadySet() { final Promise p=new Promise(); Object result; new ResultSetter(p, 1).start(); Util.sleep(100); result=p.getResult(0); Assert.assertEquals(Boolean.TRUE, result); } @Test(expectedExceptions=TimeoutException.class) public static void testGetResultWithTimeout() throws TimeoutException { final Promise p=new Promise(); p.getResultWithTimeout(500); } public static void testGetResultWithTimeoutNoException() { final Promise p=new Promise(); Object ret=p.getResult(500); assert ret == null; } public static void testGetResultWithTimeoutAndInterrupt() { final Promise p=new Promise(); new Interrupter(Thread.currentThread(), 100).start(); Object result=p.getResult(500); assert result == null; } public static void testGetResultWithTimeoutAndResultSetter() { final Promise p=new Promise(); 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); assert result != null; Assert.assertEquals("Bela", result); assert !(p.hasResult()) : "promise was reset after getResult()"; } 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(); } } } ././@LongLink0000000000000000000000000000014700000000000011567 Lustar rootrootlibjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/tests/PropertyConvertersTest.javalibjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/tests/PropertyConvertersTest.ja0000644000175000017500000000464611647260573033721 0ustar moellermoellerpackage org.jgroups.tests; import org.jgroups.Global; import org.jgroups.conf.PropertyConverter; import org.jgroups.conf.PropertyConverters; import org.jgroups.stack.Protocol; import org.testng.annotations.Test; import java.util.Arrays; import java.util.List; /** * @author Bela Ban */ @Test(groups=Global.FUNCTIONAL, sequential=false) public class PropertyConvertersTest { public static void testPrimitiveTypes() throws Exception { PropertyConverter conv=new PropertyConverters.Default(); check(null, Boolean.TYPE, "true", true, conv); check(null, Integer.TYPE, "322649", 322649, conv); check(null, Long.TYPE, "322649", 322649L, conv); } public static void testLongArray() throws Exception { PropertyConverter conv=new PropertyConverters.LongArray(); long[] array={1,2,3,4,5}; checkArray(null, array.getClass(), "1,2,3,4,5", array, conv); } /** Cannot really test list of eth0,eth1,lo, because the list differs from host to host * * @throws Exception */ public static void testNetworkList() throws Exception { PropertyConverter conv=new PropertyConverters.NetworkInterfaceList(); Object tmp=conv.convert(null, List.class, "bela", "lo", false); Object str=conv.toString(tmp); System.out.println("str = " + str); assert str.equals("lo"); } private static void check(Protocol protocol, Class type, String prop, Object result, PropertyConverter converter) throws Exception { Object tmp=converter.convert(protocol, type, "bela", prop, false); assert tmp.equals(result) : " conversion result: " + tmp + " (" + tmp.getClass() + ")" + ", expected result: " + result + " (" + result.getClass() + ")"; String output=converter.toString(tmp); assert output.equals(prop) : "output=" + output + ", prop=" + prop; } private static void checkArray(Protocol protocol, Class type, String prop, Object result, PropertyConverter converter) throws Exception { Object tmp=converter.convert(protocol, type, "bela", prop, false); assert Arrays.equals((long[])tmp, (long[])result) : " conversion result: " + tmp + " (" + tmp.getClass() + ")" + ", expected result: " + result + " (" + result.getClass() + ")"; String output=converter.toString(tmp); assert output.equals(prop) : "output=" + output + ", prop=" + prop; } } ././@LongLink0000000000000000000000000000015200000000000011563 Lustar rootrootlibjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/tests/ProtocolConfigurationTest.javalibjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/tests/ProtocolConfigurationTest0000644000175000017500000002415711647260573033761 0ustar moellermoellerpackage org.jgroups.tests; import org.jgroups.Event; import org.jgroups.Global; import org.jgroups.conf.ProtocolConfiguration; import org.jgroups.util.StackType; import org.jgroups.annotations.Property; import org.jgroups.conf.PropertyConverters; import org.jgroups.stack.Configurator; import org.jgroups.stack.IpAddress; import org.jgroups.stack.Protocol; import org.jgroups.stack.ProtocolStack; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.net.InetAddress; import java.util.LinkedList; import java.util.List; import java.util.Vector; /** * Tests the use of @Property dependency processing and default assignment. * @author Richard Achmatowicz */ @Test(groups=Global.FUNCTIONAL,sequential=true) public class ProtocolConfigurationTest { ProtocolStack stack = null; Protocol protocol = null ; static final String orderProps="org.jgroups.tests.ProtocolConfigurationTest$ORDERING(a=1;b=2;c=3)"; static final String refsProps="org.jgroups.tests.ProtocolConfigurationTest$REFS(a=1;b=2;c=3)"; static final String defaultProps="org.jgroups.tests.ProtocolConfigurationTest$DEFAULTS(b=333)"; static final String addressProps="org.jgroups.tests.ProtocolConfigurationTest$INETADDRESSES(" + "inetAddressField=127.0.0.1;inet_address_method=192.168.0.100;" + "ipAddressListField=127.0.0.1[8080],127.0.0.1[8081];" + "ip_address_list_method=192.168.0.100[5678],192.168.0.101[2345];port_range=1)" ; static final String configurableObjectsProps="org.jgroups.tests.ProtocolConfigurationTest$CONFIGOBJPROTOCOL(" + "config_object_class=org.jgroups.tests.ProtocolConfigurationTest$ConfigurableObject;" + "string_property=test)" ; List order = new LinkedList() ; @BeforeMethod void setUp() { stack=new ProtocolStack(); } /* * Checks that missing dependencies are flagged */ @Test(expectedExceptions=IllegalArgumentException.class) public void testResolutionOfDependencies() throws Exception { // create the layer described by REFS try { protocol = Configurator.createProtocol(refsProps, stack) ; } catch(IllegalArgumentException e) { System.out.println("exception thrown (expected): " + e.getMessage()); // rethrow to make sure testNG does not fail the test throw e ; } } /* * Checks that dependency ordering works */ public void testDependencyOrdering() throws Exception { // create a List describing correct Property ordering List correctOrder = new LinkedList() ; correctOrder.add("c") ; correctOrder.add("b") ; correctOrder.add("a") ; // create the layer described by ORDERING protocol = Configurator.createProtocol(orderProps, stack) ; // check that the list elements are in the right order List actualOrder = ((ORDERING)protocol).getList() ; assert actualOrder.equals(correctOrder) ; } /* * Checks assignment of defaults */ public void testDefaultAssignment() throws Exception { Vector protocol_configs = new Vector() ; Vector protocols = new Vector() ; // create the layer described by DEFAULTS protocol = Configurator.createProtocol(defaultProps, stack) ; // process the defaults protocol_configs.add(new ProtocolConfiguration(defaultProps)) ; protocols.add(protocol) ; Configurator.setDefaultValues(protocol_configs, protocols, StackType.IPv4) ; // get the value which should have been assigned a default int a = ((DEFAULTS)protocol).getA() ; System.out.println("value of a = " + a) ; // get the value which should not have been assigned a default int b = ((DEFAULTS)protocol).getB() ; System.out.println("value of b = " + b) ; // assert b == 333 ; if (b != 333) { throw new RuntimeException("default property value set when it should not have been") ; } // get the value which should not have been assigned a default InetAddress c = ((DEFAULTS)protocol).getC() ; System.out.println("value of c = " + c) ; assert c != null; } /* * Checks InetAddress and IpAddress processing */ public void testAssignmentInetAddresses() throws Exception { Vector protocol_configs = new Vector() ; Vector protocols = new Vector() ; // create the layer described by INETADDRESSES protocol = Configurator.createProtocol(addressProps, stack) ; // get the value which should have been assigned a default InetAddress a = ((INETADDRESSES)protocol).getInetAddressField() ; System.out.println("value of inetAddressField = " + a) ; // get the value which should not have been assigned a default InetAddress b = ((INETADDRESSES)protocol).getInetAddressMethod() ; System.out.println("value of inetAddressMethod = " + b) ; // get the value which should have been assigned a default List c = ((INETADDRESSES)protocol).getIpAddressListField() ; System.out.println("value of ipAddressListField = " + c) ; // get the value which should not have been assigned a default List d = ((INETADDRESSES)protocol).getIpAddressListMethod() ; System.out.println("value of ipAddressListMethod = " + d) ; } /* * Checks InetAddress and IpAddress processing */ public void testConfigurableObject() throws Exception { Vector protocol_configs = new Vector() ; Vector protocols = new Vector() ; // create the layer described by INETADDRESSES protocol = Configurator.createProtocol(configurableObjectsProps, stack) ; // process the defaults (want this eventually) protocol_configs.add(new ProtocolConfiguration(configurableObjectsProps)) ; protocols.add(protocol) ; // get the value which should have been assigned a default List configObjs = ((CONFIGOBJPROTOCOL)protocol).getConfigurableObjects() ; assert configObjs.size() == 1 ; Object configObj = configObjs.get(0) ; assert configObj instanceof ConfigurableObject ; assert ((ConfigurableObject)configObj).getStringProp().equals("test") ; } public static class ORDERING extends Protocol { List list = new LinkedList() ; @Property(name="a", dependsUpon="b") public void setA(int a) { list.add("a") ; } @Property(name="b", dependsUpon="c") public void setB(int b) { list.add("b") ; } @Property(name="c") public void setC(int c) { list.add("c") ; } List getList() { return list ; } public String getName() { return name ; } // do nothing public Object down(Event evt) { return down_prot.down(evt); } // do nothing public Object up(Event evt) { return up_prot.up(evt); } } public static class REFS extends Protocol { @Property(name="a", dependsUpon="b") public void setA(int a) { } @Property(name="b", dependsUpon="d") public void setB(int b) { } @Property(name="c") public void setC(int c) { } public String getName() { return name ; } // do nothing public Object down(Event evt) { return down_prot.down(evt); } // do nothing public Object up(Event evt) { return up_prot.up(evt); } } public static class DEFAULTS extends Protocol { int a ; int b ; InetAddress c ; @Property(name="a") public void setA(int a) { this.a = a ; } @Property(name="b") public void setB(int b) { this.b = b ; } @Property(name="c", defaultValueIPv4="192.168.1.10") public void setC(InetAddress ia) { this.c = ia ; } public int getA() { return a ; } public int getB() { return b ; } public InetAddress getC() { return c ; } public String getName() { return name ; } // do nothing public Object down(Event evt) { return down_prot.down(evt); } // do nothing public Object up(Event evt) { return up_prot.up(evt); } } public static class INETADDRESSES extends Protocol { InetAddress inetAddressMethod ; @Property(name="inetAddressField") InetAddress inetAddressField ; public InetAddress getInetAddressField() { return inetAddressField ; } @Property(name="inetAddressMethod") public void setInetAddressMethod(InetAddress ia) { this.inetAddressMethod = ia ; } public InetAddress getInetAddressMethod() { return inetAddressMethod ; } @Property(description="fred") int port_range = 0 ; // List - uses InitialHosts converter List ipAddressListMethod ; @Property(name="ipAddressListField", converter=PropertyConverters.InitialHosts.class) List ipAddressListField ; public List getIpAddressListField() { return ipAddressListField ; } @Property(name="ipAddressListMethod", converter=PropertyConverters.InitialHosts.class, dependsUpon="port_range") public void setIpAddressListMethod(List ia) { this.ipAddressListMethod = ia ; } public List getIpAddressListMethod() { return ipAddressListMethod ; } public String getName() { return name ; } // do nothing public Object down(Event evt) { return down_prot.down(evt); } // do nothing public Object up(Event evt) { return up_prot.up(evt); } } public static class CONFIGOBJPROTOCOL extends Protocol { private Object configObjInstance=null; @Property(name="config_object_class") public void setConfigurableObjectClass(String class_name) throws Exception { configObjInstance=Class.forName(class_name).newInstance(); } protected List getConfigurableObjects() { List retval=new LinkedList(); if(configObjInstance != null) retval.add(configObjInstance); return retval; } public String getName() { return name ; } // do nothing public Object down(Event evt) { return down_prot.down(evt); } // do nothing public Object up(Event evt) { return up_prot.up(evt); } } public static class ConfigurableObject { @Property(name="string_property") String stringProp = null ; public String getStringProp() { return stringProp ; } public void setStringProp(String s) { this.stringProp = s ; } } } libjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/tests/QueueTest.java0000644000175000017500000006655411647260573031443 0ustar moellermoeller package org.jgroups.tests; import org.jgroups.Global; import org.jgroups.TimeoutException; import org.jgroups.util.Queue; import org.jgroups.util.QueueClosedException; import org.jgroups.util.Util; import org.testng.annotations.Test; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; /** * @author Bela Ban */ @Test(groups=Global.FUNCTIONAL,sequential=false) public class QueueTest { public static void testQueue() throws QueueClosedException { final Queue queue=new Queue(); queue.add("Q1"); queue.add("Q2"); queue.add("Q3"); assert queue.peek().equals("Q1"); assert queue.remove().equals("Q1"); assert queue.peek().equals("Q2"); assert queue.remove().equals("Q2"); queue.add("Q5"); queue.close(true); try { queue.add("Q6"); assert false : "should not get here"; } catch(org.jgroups.util.QueueClosedException qc) { } int size=queue.size(); queue.removeElement("Q5"); assert queue.size() == size -1; assert queue.peek().equals("Q3"); assert queue.remove().equals("Q3"); assert queue.closed(); } @Test(expectedExceptions=QueueClosedException.class) public static void testCloseWithoutFlush() throws QueueClosedException { final Queue queue=new Queue(); queue.close(false); queue.remove(); } @Test(expectedExceptions=QueueClosedException.class) public static void testCloseWithFlush() throws QueueClosedException { final Queue queue=new Queue(); queue.close(true); queue.remove(); } @Test(expectedExceptions=QueueClosedException.class) public static void testCloseWithFlush2() throws QueueClosedException { final Queue queue=new Queue(); queue.add(new Integer(1)); queue.add(new Integer(2)); queue.add(new Integer(3)); queue.close(true); for(int i=1; i <= 3; i++) { Object obj=queue.remove(); assert obj != null; assert new Integer(i).equals(obj); } queue.remove(); } public static void testValues() throws QueueClosedException { final Queue queue=new Queue(); 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(); assert size == 4; LinkedList values=queue.values(); assert values.size() == size; } public static void testLargeInsertion() throws QueueClosedException { String element="MyElement"; long start, stop; final Queue queue=new Queue(); 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"); } public static void testEmptyQueue() { final Queue queue=new Queue(); assert queue.getFirst() == null; assert queue.getLast() == null; } public static void testAddAll() throws QueueClosedException { final Queue queue=new Queue(); List l=new ArrayList(); l.add("one"); l.add("two"); l.add("three"); queue.addAll(l); System.out.println("queue is " + queue); assert queue.size() == 3; assert queue.remove().equals("one"); assert queue.size() == 2; assert queue.remove().equals("two"); assert queue.size() == 1; assert queue.remove().equals("three"); assert queue.size() == 0; } public static void testInsertionAndRemoval() throws Exception { final Queue queue=new Queue(); String s1="Q1", s2="Q2"; queue.add(s1); assert queue.getFirst() != null; assert queue.getLast() != null; assert queue.getLast().equals(queue.getFirst()); queue.add(s2); assert 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)); assert queue.getFirst().equals(queue.peek()); queue.remove(); assert queue.size() == 1; assert queue.getLast().equals(queue.getFirst()); queue.remove(); assert queue.size() == 0; assert queue.getFirst() == null; assert queue.getLast() == null; } public static void testWaitUntilClosed() { final Queue queue=new Queue(); queue.close(true); queue.waitUntilClosed(0); assert queue.size() == 0; } public static void testWaitUntilClosed2() { final Queue queue=new Queue(); queue.close(true); try { queue.peek(); assert false : "peek() should throw a QueueClosedException"; } catch(QueueClosedException e) { assert e != null; } assert queue.size() == 0; } public static void testWaitUntilClosed3() throws QueueClosedException { final Queue queue=new Queue(); queue.add("one"); queue.close(true); Object obj=queue.peek(); assert obj.equals("one"); assert queue.size() == 1; queue.remove(); try { queue.peek(); assert false : "peek() should throw a QueueClosedException"; } catch(QueueClosedException e) { assert e != null; } assert queue.size() == 0; } public static void testWaitUntilClosed4() throws QueueClosedException { final Queue queue=new Queue(); 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); assert queue.size() == 0; } public static void testWaitUntilClosed5() throws QueueClosedException { final Queue queue=new Queue(); 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); assert queue.size() > 0; } public static void testRemoveElementNoElement() { final Queue queue=new Queue(); String s1="Q1"; try { queue.removeElement(s1); assert !(queue.closed()); assert queue.size() == 0; } catch(QueueClosedException ex) { assert false : ex.toString(); } } public static void testRemoveElementOneElement() { final Queue queue=new Queue(); String s1="Q1"; try { queue.add(s1); queue.removeElement(s1); assert queue.size() == 0; assert queue.getFirst() == null; assert queue.getLast() == null; } catch(QueueClosedException ex) { assert false : ex.toString(); } } public static void testRemoveElementTwoElementsFirstFound() { String s1="Q1", s2="Q2"; final Queue queue=new Queue(); try { queue.add(s1); queue.add(s2); queue.removeElement(s1); assert queue.size() == 1; assert queue.getFirst().equals(s2); assert queue.getLast().equals(s2); assert queue.getFirst().equals(queue.getLast()); } catch(QueueClosedException ex) { assert false : ex.toString(); } } public static void testRemoveElementTwoElementsSecondFound() { String s1="Q1", s2="Q2"; final Queue queue=new Queue(); try { queue.add(s1); queue.add(s2); queue.removeElement(s2); assert queue.size() == 1; assert queue.getFirst().equals(s1); assert queue.getLast().equals(s1); assert queue.getFirst().equals(queue.getLast()); } catch(QueueClosedException ex) { assert false : ex.toString(); } } public static void testRemoveElementThreeElementsFirstFound() { String s1="Q1", s2="Q2", s3="Q3"; final Queue queue=new Queue(); try { queue.add(s1); queue.add(s2); queue.add(s3); queue.removeElement(s1); assert queue.size() == 2; assert queue.getFirst().equals(s2); assert queue.getLast().equals(s3); } catch(QueueClosedException ex) { assert false : ex.toString(); } } public static void testRemoveElementThreeElementsSecondFound() { String s1="Q1", s2="Q2", s3="Q3"; final Queue queue=new Queue(); try { queue.add(s1); queue.add(s2); queue.add(s3); queue.removeElement(s2); assert queue.size() == 2; assert queue.getFirst().equals(s1); assert queue.getLast().equals(s3); } catch(QueueClosedException ex) { assert false : ex.toString(); } } public static void testRemoveElementThreeElementsThirdFound() { String s1="Q1", s2="Q2", s3="Q3"; final Queue queue=new Queue(); try { queue.add(s1); queue.add(s2); queue.add(s3); queue.removeElement(s3); assert queue.size() == 2; assert queue.getFirst().equals(s1); assert queue.getLast().equals(s2); } catch(QueueClosedException ex) { assert false : ex.toString(); } } @Test(expectedExceptions=QueueClosedException.class) public static void testRemoveAndClose() throws QueueClosedException { final Queue queue=new Queue(); new Thread() { public void run() { Util.sleep(1000); queue.close(true); // close gracefully } }.start(); queue.remove(); } @Test(expectedExceptions=QueueClosedException.class) public static void testRemoveAndCloseWithTimeout() throws QueueClosedException, TimeoutException { final Queue queue=new Queue(); new Thread() { public void run() { Util.sleep(1000); queue.close(true); // close gracefully } }.start(); queue.remove(5000); } @Test(expectedExceptions=TimeoutException.class) public static void testInterruptAndRemove() throws QueueClosedException, TimeoutException { final Queue queue=new Queue(); Thread.currentThread().interrupt(); queue.remove(2000); } @Test(expectedExceptions=QueueClosedException.class) public static void testRemoveAndInterrupt() throws QueueClosedException { final Queue queue=new Queue(); 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"); queue.remove(); } public static void testClear() throws QueueClosedException { Queue queue=new Queue(); queue.add("one"); queue.add("two"); assert queue.size() == 2; queue.close(true); assert queue.size() == 2; queue.clear(); assert queue.size() == 0; queue=new Queue(); queue.add("one"); queue.add("two"); queue.clear(); assert queue.size() == 0; queue.add("one"); queue.add("two"); assert queue.size() == 2; queue.clear(); assert queue.size() == 0; } // 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 static void testBarrier() throws QueueClosedException { RemoveOneItem[] removers=new RemoveOneItem[10]; final Queue queue=new Queue(); int num_dead=0; for(int i=0; i < removers.length; i++) { removers[i]=new RemoveOneItem(i, queue); removers[i].start(); } Util.sleep(200); System.out.println("-- adding element 99"); queue.add(new Long(99)); System.out.println("-- adding element 100"); queue.add(new Long(100)); long target_time=System.currentTimeMillis() + 10000L; do { int num=0; for(int i=0; i < removers.length; i++) { if(!removers[i].isAlive()) num++; } if(num == 2) break; Util.sleep(500); } while(target_time > System.currentTimeMillis()); 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++; } } assert num_dead == 2 : "num_dead was " + num_dead + ", but expected 2"; queue.close(false); } /** Multiple threads call remove(), one threads then adds an element. Only 1 thread should actually terminate * (the one that has the element) */ public static void testBarrierWithTimeOut() throws QueueClosedException { final Queue queue=new Queue(); RemoveOneItemWithTimeout[] removers=new RemoveOneItemWithTimeout[10]; int num_dead=0; for(int i=0; i < removers.length; i++) { removers[i]=new RemoveOneItemWithTimeout(i, 15000, queue); removers[i].start(); } System.out.println("-- adding element 99"); queue.add(new Long(99)); System.out.println("-- adding element 100"); queue.add(new Long(100)); long target_time=System.currentTimeMillis() + 10000L; do { int num_rsps=0; for(int i=0; i < removers.length; i++) { if(removers[i].getRetval() != null) num_rsps++; } if(num_rsps == 2) break; Util.sleep(500); } while(target_time > System.currentTimeMillis()); Util.sleep(3000); 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++; } } assert num_dead == 2 : "num_dead should have been 2 but was " + num_dead; System.out.println("closing queue - causing all remaining threads to terminate"); queue.close(false); // will cause all threads still blocking on remove() to return Util.sleep(500); 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++; } } assert num_dead == 10 : "num_dead should have been 10 but was " + num_dead; } /** Multiple threads add one element, one thread read them all. * (the one that has the element) */ public static void testMultipleWriterOneReader() throws QueueClosedException { final Queue queue=new Queue(); 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, queue); adders[i].start(); } Util.sleep(500); while(num_items < (adders.length * items)) { queue.remove(); num_items++; } 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++; } } assert num_dead == 10 : "num_dead should have been 10 but was " + 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 static void testConcurrentAddRemove() throws QueueClosedException { final Queue queue=new Queue(); 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) { ret=queue.remove(); if(ret != null) num_received++; } assert num_received == NUM; 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 static void testConcurrentAccess() { final Queue queue=new Queue(); final int NUM_THREADS=10; final int INTERVAL=5000; 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, queue); readers[i].start(); writers[i]=new Writer(i, writes, queue); 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)); } static class AddOneItem extends Thread { Long retval=null; int rank=0; int iteration=0; Queue queue; AddOneItem(int rank, int iteration, Queue queue) { super("AddOneItem thread #" + rank); this.rank=rank; this.iteration=iteration; setDaemon(true); this.queue=queue; } 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"); } } } static class RemoveOneItem extends Thread { Long retval=null; int rank=0; Queue queue; RemoveOneItem(int rank, Queue queue) { super("RemoveOneItem thread #" + rank); this.rank=rank; this.queue=queue; 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; } } static class RemoveOneItemWithTimeout extends Thread { Long retval=null; final int rank; final long timeout; final Queue queue; RemoveOneItemWithTimeout(int rank, long timeout, Queue queue) { super("RemoveOneItem thread #" + rank); this.rank=rank; this.timeout=timeout; this.queue=queue; setDaemon(true); } public void run() { try { retval=(Long)queue.removeWait(timeout); System.out.println("Thread #" + rank + ": retrieved " + retval); } catch(QueueClosedException closed) { System.out.println("Thread #" + rank + ": queue was closed"); } catch(TimeoutException e) { System.out.println("Thread #" + rank + ": timeout occurred"); } } Long getRetval() { return retval; } } static class Writer extends Thread { int rank=0; int num_writes=0; boolean running=true; int[] writes=null; Queue queue; Writer(int i, int[] writes, Queue queue) { super("WriterThread"); rank=i; this.writes=writes; this.queue=queue; 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; } } static class Reader extends Thread { int rank; int num_reads=0; int[] reads=null; boolean running=true; Queue queue; Reader(int i, int[] reads, Queue queue) { super("ReaderThread"); rank=i; this.reads=reads; this.queue=queue; 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()); } assert el != null; 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; } } } libjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/tests/ReentrantLockTest.java0000644000175000017500000000317411647260573033117 0ustar moellermoellerpackage org.jgroups.tests; import org.testng.Assert; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import org.jgroups.Global; import java.util.concurrent.locks.ReentrantLock; /** * Tests the ReentrantLock * @author Bela Ban */ @Test(groups=Global.FUNCTIONAL,sequential=true) public class ReentrantLockTest { ReentrantLock lock; @BeforeMethod public void setUp() throws Exception { lock=new ReentrantLock(); } @AfterMethod public void tearDown() throws Exception { releaseAll(lock); lock=null; } public void testAcquireLock() { lock.lock(); Assert.assertEquals(1, lock.getHoldCount()); lock.lock(); Assert.assertEquals(2, lock.getHoldCount()); release(lock); Assert.assertEquals(1, lock.getHoldCount()); release(lock); Assert.assertEquals(0, lock.getHoldCount()); } public void testAcquireLock2() { lock.lock(); Assert.assertEquals(1, lock.getHoldCount()); lock.lock(); Assert.assertEquals(2, lock.getHoldCount()); releaseAll(lock); Assert.assertEquals(0, lock.getHoldCount()); } private static void release(ReentrantLock lock) { if(lock != null && lock.getHoldCount() > 0) lock.unlock(); } private static void releaseAll(ReentrantLock lock) { if(lock != null) { long holds=lock.getHoldCount(); if(holds > 0) { for(int i=0; i < holds; i++) lock.unlock(); } } } } libjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/tests/RequestOptionsTest.java0000644000175000017500000000344711647260573033353 0ustar moellermoellerpackage org.jgroups.tests; import org.jgroups.Global; import org.jgroups.JChannel; import org.jgroups.blocks.*; import org.jgroups.blocks.mux.MuxRpcDispatcher; import org.jgroups.blocks.mux.NoMuxHandlerRspFilter; import org.jgroups.util.Util; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; @Test(groups=Global.FUNCTIONAL,sequential=true) public class RequestOptionsTest { protected JChannel channel; protected RequestOptions reqOpt = new RequestOptions(Request.GET_ALL, 5000); protected static final String simple_props="SHARED_LOOPBACK:PING(timeout=1000):" + "pbcast.NAKACK(log_discard_msgs=false;log_not_found_msgs=false)" + ":UNICAST:pbcast.STABLE(stability_delay=200):pbcast.GMS:MFC:UFC:FRAG2"; @BeforeMethod protected void start() throws Exception { channel=new JChannel(simple_props); } protected void stop() throws Exception { Util.close(channel); } /** * Tests https://issues.jboss.org/browse/JGRP-1369 */ public void testRequestOptionsChaining() throws Exception { MuxRpcDispatcher muxRpc = new MuxRpcDispatcher((short) 1, channel, null, null, new Server()); channel.connect("group"); for(int i=0; i < 20; i++) muxRpc.callRemoteMethods(null, new MethodCall(Server.class.getMethod("foo", null)), reqOpt); RspFilter filter=reqOpt.getRspFilter(); int count=count(filter); System.out.println("count=" + count); assert count == 1; } protected static int count(RspFilter filter) { if(filter instanceof NoMuxHandlerRspFilter) return 1 + count(((NoMuxHandlerRspFilter)filter).getFilter()); return 0; } static public class Server { public static void foo() {System.out.println("Entering foo"); } } } ././@LongLink0000000000000000000000000000014600000000000011566 Lustar rootrootlibjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/tests/ResponseCollectorTest.javalibjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/tests/ResponseCollectorTest.jav0000644000175000017500000000707211647260573033651 0ustar moellermoeller package org.jgroups.tests; import org.jgroups.Address; import org.jgroups.Global; import org.jgroups.util.ResponseCollector; import org.jgroups.util.Util; import org.testng.annotations.Test; import java.util.Map; /** * @author Bela Ban */ @Test(groups=Global.FUNCTIONAL,sequential=false) public class ResponseCollectorTest { static final Address a=Util.createRandomAddress(), b=Util.createRandomAddress(), c=Util.createRandomAddress(); public static void testAdd() { ResponseCollector coll=new ResponseCollector(a, b, c); coll.add(a, 1); System.out.println("coll = " + coll); assert coll.size() == 3; assert !coll.hasAllResponses(); coll.add(c, 3); coll.add(b, 2); System.out.println("coll = " + coll); assert coll.size() == 3; assert coll.hasAllResponses(); } public static void testAddNonExistentKeys() { ResponseCollector coll=new ResponseCollector(a, b); coll.add(a, 1); System.out.println("coll = " + coll); assert coll.size() == 2; assert !coll.hasAllResponses(); coll.add(c, 3); // will get dropped coll.add(b, 2); System.out.println("coll = " + coll); assert coll.size() == 2; assert coll.hasAllResponses(); } public static void testWaitForAllResponses() { final ResponseCollector coll=new ResponseCollector(a, b, c); boolean rc=coll.waitForAllResponses(500); assert !rc; new Thread() { public void run() { coll.add(a, 1); Util.sleep(500); coll.add(b, 2); coll.add(c, 3); } }.start(); rc=coll.waitForAllResponses(5000); System.out.println("coll = " + coll); assert rc; assert coll.hasAllResponses(); } public static void testWaitForAllResponsesAndTimeout() { final ResponseCollector coll=new ResponseCollector(a, b, c); new Thread() { public void run() { coll.add(a, 1); Util.sleep(1000); coll.add(b, 2); Util.sleep(1000); coll.add(c, 3); } }.start(); boolean rc=coll.waitForAllResponses(400); System.out.println("coll = " + coll); assert !rc; assert !coll.hasAllResponses() : "collector had all responses (not expected)"; } public static void testWaitForAllResponsesAndReset() { final ResponseCollector coll=new ResponseCollector(a, b, c); new Thread() { public void run() { Util.sleep(1000); coll.add(a, 1); coll.reset(); } }.start(); boolean rc=coll.waitForAllResponses(5000); System.out.println("coll = " + coll); assert rc; assert coll.hasAllResponses(); } public static void testWaitForAllResponsesAndGetResults() throws InterruptedException { final ResponseCollector coll=new ResponseCollector(a, b, c); coll.add(a, 1); coll.add(b, 2); coll.add(c, 3); Map results=coll.getResults(); System.out.println("results = " + results); Thread thread=new Thread() { public void run() { coll.reset(); } }; thread.start(); thread.join(); System.out.println("results = " + results); assert coll.size() == 0; } }libjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/tests/RetransmitTableTest.java0000644000175000017500000002052211647260573033440 0ustar moellermoellerpackage org.jgroups.tests; import org.jgroups.Global; import org.jgroups.Message; import org.jgroups.util.RetransmitTable; import org.jgroups.util.Util; import org.testng.annotations.Test; import java.util.ArrayList; import java.util.List; /** Tests {@link org.jgroups.util.RetransmitTable} * @author Bela Ban */ @Test(groups=Global.FUNCTIONAL,sequential=false) public class RetransmitTableTest { static final Message MSG=new Message(null, null, "test"); public static void testCreation() { RetransmitTable table=new RetransmitTable(3, 10, 0); int size=table.size(); assert size == 0; assert table.get(15) == null; } public static void testAddition() { RetransmitTable table=new RetransmitTable(3, 10, 0); addAndGet(table, 0, "0"); addAndGet(table, 1, "1"); addAndGet(table, 5, "5"); addAndGet(table, 9, "9"); addAndGet(table, 10, "10"); addAndGet(table, 11, "11"); addAndGet(table, 19, "19"); addAndGet(table, 20, "20"); addAndGet(table, 29, "29"); System.out.println("table: " + table.dump()); assert table.size() == 9; assert table.size() == table.computeSize(); assert table.capacity() == 30; } public static void testAdditionWithOffset() { RetransmitTable table=new RetransmitTable(3, 10, 100); addAndGet(table, 100, "100"); addAndGet(table, 101, "101"); addAndGet(table, 105, "105"); addAndGet(table, 109, "109"); addAndGet(table, 110, "110"); addAndGet(table, 111, "111"); addAndGet(table, 119, "119"); addAndGet(table, 120, "120"); addAndGet(table, 129, "129"); System.out.println("table: " + table.dump()); assert table.size() == 9; assert table.capacity() == 30; } public static void testDuplicateAddition() { RetransmitTable table=new RetransmitTable(3, 10, 0); addAndGet(table, 0, "0"); addAndGet(table, 1, "1"); addAndGet(table, 5, "5"); addAndGet(table, 9, "9"); addAndGet(table, 10, "10"); assert !table.put(5, new Message()); assert table.get(5).getObject().equals("5"); assert table.size() == 5; } public static void testDumpMatrix() { RetransmitTable table=new RetransmitTable(3, 10, 1); long[] seqnos={1,3,5,7,9,12,14,16,18,20,21,22,23,24}; for(long seqno: seqnos) table.put(seqno, MSG); System.out.println("matrix:\n" + table.dumpMatrix()); } public static void testMassAddition() { RetransmitTable table=new RetransmitTable(3, 10, 0); final int NUM_MSGS=10005; final Message MSG=new Message(null, null, "hello world"); for(int i=0; i < NUM_MSGS; i++) table.put(i, MSG); System.out.println("table = " + table); assert table.size() == NUM_MSGS; assert table.capacity() == 10010; } public static void testResize() { RetransmitTable table=new RetransmitTable(3, 10, 0); assert table.capacity() == 30; addAndGet(table, 30, "30"); addAndGet(table, 35, "35"); assert table.capacity() == 40; addAndGet(table, 500, "500"); assert table.capacity() == 510; addAndGet(table, 515, "515"); assert table.capacity() == 520; } public void testResizeWithPurge() { RetransmitTable table=new RetransmitTable(3, 10, 0); for(long i=1; i <= 100; i++) addAndGet(table, i, "hello-" + i); System.out.println("table: " + table); // now remove 60 messages for(long i=1; i <= 60; i++) { Message msg=table.remove(i); assert msg.getObject().equals("hello-" + i); } System.out.println("table after removal of seqno 60: " + table); table.purge(50); System.out.println("now triggering a resize() by addition of seqno=120"); addAndGet(table, 120, "120"); } public void testResizeWithPurgeAndGetOfNonExistingElement() { RetransmitTable table=new RetransmitTable(3, 10, 0); for(long i=0; i < 50; i++) addAndGet(table, i, "hello-" + i); System.out.println("table: " + table); // now remove 15 messages for(long i=0; i <= 15; i++) { Message msg=table.remove(i); assert msg.getObject().equals("hello-" + i); } System.out.println("table after removal of seqno 15: " + table); table.purge(15); System.out.println("now triggering a resize() by addition of seqno=55"); addAndGet(table, 55, "hello-55"); // now we have elements 40-49 in row 1 and 55 in row 2: List list=new ArrayList(20); for(int i=16; i < 50; i++) list.add("hello-" + i); list.add("hello-55"); for(long i=table.getOffset(); i < table.capacity() + table.getOffset(); i++) { Message msg=table.get(i); if(msg != null) { String message=(String)msg.getObject(); System.out.println(i + ": " + message); list.remove(message); } } System.out.println("table:\n" + table.dumpMatrix()); assert list.isEmpty() : " list: " + Util.print(list); } public void testResizeWithPurge2() { RetransmitTable table=new RetransmitTable(3, 10, 0); for(long i=0; i < 50; i++) addAndGet(table, i, "hello-" + i); System.out.println("table = " + table); assert table.size() == 50; assert table.capacity() == 50; assert table.getHighestPurged() == 0; assert table.getHighest() == 49; table.purge(43); addAndGet(table, 52, "hello-52"); assert table.get(43) == null; for(long i=44; i < 50; i++) { Message msg=table.get(i); assert msg != null && msg.getObject().equals("hello-" + i); } assert table.get(50) == null; assert table.get(51) == null; Message msg=table.get(52); assert msg != null && msg.getObject().equals("hello-52"); assert table.get(53) == null; } public static void testMove() { RetransmitTable table=new RetransmitTable(3, 10, 0); for(long i=0; i < 50; i++) addAndGet(table, i, "hello-" + i); table.purge(49); assert table.isEmpty(); addAndGet(table, 50, "50"); assert table.size() == 1; assert table.capacity() == 50; } public static void testPurge() { RetransmitTable table=new RetransmitTable(5, 10, 0); for(long seqno=0; seqno < 25; seqno++) table.put(seqno, MSG); long[] seqnos={30,31,32,37,38,39, 40,41,42,47,48,49}; for(long seqno: seqnos) table.put(seqno, MSG); System.out.println("table (before remove):\n" + table.dump()); for(long seqno=0; seqno <= 22; seqno++) table.remove(seqno); System.out.println("\ntable (after remove 22, before purge):\n" + table.dump()); table.purge(22); System.out.println("\ntable: (after purge 22):\n" + table.dump()); assert table.size() == 2 + seqnos.length; } public void testCompact() { RetransmitTable table=new RetransmitTable(3, 10, 0); for(long i=0; i < 80; i++) addAndGet(table, i, "hello-" + i); assert table.size() == 80; table.purge(59); assert table.size() == 20; table.compact(); assert table.size() == 20; assert table.capacity() == 40; } public void testCompactWithAutomaticPurging() { RetransmitTable table=new RetransmitTable(3, 10, 0); table.setAutomaticPurging(true); for(long i=0; i < 80; i++) addAndGet(table, i, "hello-" + i); assert table.size() == 80; for(long i=0; i <= 59; i++) table.remove(i); assert table.size() == 20; table.compact(); assert table.size() == 20; assert table.capacity() == 40; } protected static void addAndGet(RetransmitTable table, long seqno, String message) { boolean added=table.put(seqno, new Message(null, null, message)); assert added; Message msg=table.get(seqno); assert msg != null && msg.getObject().equals(message); } } libjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/tests/RetransmitterTest.java0000644000175000017500000000641311647260573033206 0ustar moellermoeller package org.jgroups.tests; import org.jgroups.Address; import org.jgroups.Global; import org.jgroups.stack.DefaultRetransmitter; import org.jgroups.stack.Retransmitter; import org.jgroups.stack.StaticInterval; import org.jgroups.util.DefaultTimeScheduler; import org.jgroups.util.TimeScheduler; import org.jgroups.util.Util; import org.testng.Assert; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; @Test(groups=Global.FUNCTIONAL,sequential=true) public class RetransmitterTest { private final Address sender=Util.createRandomAddress(); private TimeScheduler timer; private Retransmitter xmitter; @BeforeMethod void initTimer() { timer=new DefaultTimeScheduler(); xmitter=new DefaultRetransmitter(sender, new MyXmitter(), timer); xmitter.setRetransmitTimeouts(new StaticInterval(1000,2000,4000,8000)); xmitter.reset(); } @AfterMethod void destroyTimer() throws InterruptedException { timer.stop(); } public void testNoEntry() { int size=xmitter.size(); System.out.println("xmitter: " + xmitter); Assert.assertEquals(0, size); } public void testSingleEntry() { xmitter.add(1, 1); int size=xmitter.size(); System.out.println("xmitter: " + xmitter); Assert.assertEquals(1, size); } public void testEntry() { xmitter.add(1, 10); int size=xmitter.size(); System.out.println("xmitter: " + xmitter); Assert.assertEquals(10, size); } public void testMultipleEntries() { xmitter.add(1, 10); int size=xmitter.size(); System.out.println("xmitter: " + xmitter); Assert.assertEquals(10, size); xmitter.add(12,13); size=xmitter.size(); System.out.println("xmitter: " + xmitter); Assert.assertEquals(12, size); xmitter.remove(5); size=xmitter.size(); System.out.println("xmitter: " + xmitter); Assert.assertEquals(11, size); xmitter.remove(13); size=xmitter.size(); System.out.println("xmitter: " + xmitter); Assert.assertEquals(10, size); xmitter.remove(1); size=xmitter.size(); System.out.println("xmitter: " + xmitter); Assert.assertEquals(9, size); xmitter.remove(13); size=xmitter.size(); System.out.println("xmitter: " + xmitter); Assert.assertEquals(9, size); xmitter.remove(12); size=xmitter.size(); System.out.println("xmitter: " + xmitter); Assert.assertEquals(8, size); for(int i=8; i >= 0; i--) xmitter.remove(i); size=xmitter.size(); System.out.println("xmitter: " + xmitter); Assert.assertEquals(2, size); xmitter.remove(10); size=xmitter.size(); System.out.println("xmitter: " + xmitter); Assert.assertEquals(1, size); xmitter.remove(9); size=xmitter.size(); System.out.println("xmitter: " + xmitter); Assert.assertEquals(0, size); } static class MyXmitter implements Retransmitter.RetransmitCommand { public void retransmit(long first_seqno, long last_seqno, Address sender) { } } } libjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/tests/RspListTest.java0000644000175000017500000001150111647260573031735 0ustar moellermoellerpackage org.jgroups.tests; import org.jgroups.Address; import org.jgroups.Global; import org.jgroups.util.Rsp; import org.jgroups.util.RspList; import org.jgroups.util.Util; import org.testng.Assert; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.*; @Test(groups=Global.FUNCTIONAL,sequential=true) public class RspListTest { RspList rl; Address a1, a2, a3, a4, a5; Rsp rsp1, rsp2, rsp3, rsp4, rsp5; @BeforeMethod public void setUp() throws Exception { rl=new RspList(); a1=Util.createRandomAddress(); a2=Util.createRandomAddress(); a3=Util.createRandomAddress(); a4=Util.createRandomAddress(); a5=Util.createRandomAddress(); 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); } @AfterMethod protected void tearDown() throws Exception { rl.clear(); } public void testConstructor() { Collection c=new LinkedList(); c.add(rsp1); c.add(rsp2); c.add(rsp3); RspList tmp=new RspList(c); Assert.assertEquals(c.size(), tmp.size()); assert tmp.containsKey(a1); assert tmp.containsKey(a2); assert tmp.containsKey(a3); assert tmp.containsValue(rsp1); assert tmp.containsValue(rsp2); assert tmp.containsValue(rsp3); } public void testIsEmpty() { RspList tmp=new RspList(); assert tmp.isEmpty(); tmp.addRsp(a1, rsp1); assert !(tmp.isEmpty()); } public void testContainsKey() { assert rl.containsKey(a1); assert rl.containsKey(a3); } public void testContainsValue() { assert rl.containsValue(rsp1); assert rl.containsValue(rsp3); } public void testGet() { Rsp rsp=rl.get(a1); Assert.assertEquals(rsp, rsp1); rsp=rl.get(a3); Assert.assertEquals(rsp, rsp3); } public void testPut() { Rsp rsp; rsp=rl.put(Util.createRandomAddress(), new Rsp(Util.createRandomAddress(), true)); assert rsp == null; rsp=rl.put(a2, rsp2); Assert.assertEquals(rsp, rsp2); Assert.assertEquals(6, rl.size()); } public void testRemove() { Rsp rsp; rsp=rl.remove(Util.createRandomAddress()); assert rsp == null; rsp=rl.remove(a2); Assert.assertEquals(rsp, rsp2); Assert.assertEquals(4, rl.size()); } public void testClear() { rl.clear(); Assert.assertEquals(0, rl.size()); } public static void testKeySet() { RspList tmp=new RspList(); Set keys=tmp.keySet(); assert keys != null; Assert.assertEquals(0, keys.size()); } public void testKeySet2() { Set keys=rl.keySet(); assert keys != null; Assert.assertEquals(rl.size(), keys.size()); } public void testAddRsp() { Address tmp=Util.createRandomAddress(); rl.addRsp(tmp, new Integer(322649)); Assert.assertEquals(6, rl.size()); Rsp rsp=rl.get(tmp); assert rsp != null; assert rsp.wasReceived(); assert !(rsp.wasSuspected()); Assert.assertEquals(new Integer(322649), rsp.getValue()); } public void testAddRsp2() { rl.addRsp(a1, new Integer(322649)); Assert.assertEquals(5, rl.size()); Rsp rsp=rl.get(a1); assert rsp != null; assert rsp.wasReceived(); assert !(rsp.wasSuspected()); Assert.assertEquals(new Integer(322649), rsp.getValue()); } public void testNumSuspectedMembers() { Assert.assertEquals(2, rl.numSuspectedMembers()); } public void testGetFirst() { Object obj=rl.getFirst(); System.out.println("-- first (non-null) value is " + obj); assert obj != null; } public void testGetResults() { Vector v=rl.getResults(); assert v != null; Assert.assertEquals(2, v.size()); } public void testElementAt() { Rsp rsp; Set
                s=new HashSet
                (); s.addAll(rl.keySet()); // for(int i=0; i < rl.size(); i++) { // rsp=(Rsp)rl.elementAt(i); // s.add(rsp.getSender()); // } System.out.println("-- set is " + s); Assert.assertEquals(rl.size(), s.size()); } public void testElementAtWithOOBEx() { try { rl.elementAt(6); assert false : "this should have thrown an ArrayIndexOutOfBoundsException"; } catch(ArrayIndexOutOfBoundsException ex) { } } } libjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/tests/SeqnoComparatorTest.java0000644000175000017500000000422311647260573033455 0ustar moellermoellerpackage org.jgroups.tests; import org.testng.annotations.Test; import org.jgroups.Global; import org.jgroups.util.SeqnoComparator; import org.jgroups.util.Seqno; import org.jgroups.util.SeqnoRange; /** * @author Bela Ban */ @Test(groups=Global.FUNCTIONAL, sequential=false) public class SeqnoComparatorTest { static final SeqnoComparator comp=new SeqnoComparator(); public static void testTwoSeqnos() { Seqno s1=new Seqno(10), s2=new Seqno(10); assert comp.compare(s1, s2) == 0; s1=new Seqno(9); assert comp.compare(s1, s2) == -1; s2=new Seqno(5); assert comp.compare(s1, s2) == 1; } public static void compareDummyWithSeqnoRange() { Seqno s1=new Seqno(10, true), s2=new SeqnoRange(1, 100); assert comp.compare(s1, s2) == 0; s1=new Seqno(1, true); assert comp.compare(s1, s2) == 0; s1=new Seqno(100, true); assert comp.compare(s1, s2) == 0; s1=new Seqno(0, true); assert comp.compare(s1, s2) == -1; s1=new Seqno(101, true); assert comp.compare(s1, s2) == 1; } public static void compareDummyWithSeqno() { Seqno s1=new Seqno(10, true), s2=new Seqno(10); assert comp.compare(s1, s2) == 0; s1=new Seqno(9, true); assert comp.compare(s1, s2) == -1; s1=new Seqno(11, true); assert comp.compare(s1, s2) == 1; } public static void compareSeqnoRangeWithDummy() { Seqno s1=new SeqnoRange(1, 100), s2=new Seqno(10, true); assert comp.compare(s1, s2) == 0; s2=new Seqno(1, true); assert comp.compare(s1, s2) == 0; s2=new Seqno(100, true); assert comp.compare(s1, s2) == 0; s2=new Seqno(0, true); assert comp.compare(s1, s2) == 1; s2=new Seqno(101, true); assert comp.compare(s1, s2) == -1; } public static void compareSeqnoWithDummy() { Seqno s1=new Seqno(10), s2=new Seqno(10, true); assert comp.compare(s1, s2) == 0; s2=new Seqno(9, true); assert comp.compare(s1, s2) == 1; s2=new Seqno(11, true); assert comp.compare(s1, s2) == -1; } } libjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/tests/SeqnoTableTest.java0000644000175000017500000000750711647260573032405 0ustar moellermoeller package org.jgroups.tests; import org.jgroups.Address; import org.jgroups.Global; import org.jgroups.util.SeqnoTable; import org.jgroups.util.Util; import org.testng.Assert; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; import java.net.UnknownHostException; @Test(groups=Global.FUNCTIONAL) public class SeqnoTableTest { private static Address MBR=null; @BeforeClass private static void init() throws UnknownHostException { MBR=Util.createRandomAddress(); } public static void testInit() { SeqnoTable tab=new SeqnoTable(0); tab.add(MBR, 0); Assert.assertEquals(0, tab.getHighestReceived(MBR)); Assert.assertEquals(1, tab.getNextToReceive(MBR)); tab.clear(); tab=new SeqnoTable(50); tab.add(MBR, 50); Assert.assertEquals(50, tab.getHighestReceived(MBR)); Assert.assertEquals(51, tab.getNextToReceive(MBR)); } public static void testAdd() { SeqnoTable tab=new SeqnoTable(0); tab.add(MBR, 0); tab.add(MBR, 1); tab.add(MBR, 2); Assert.assertEquals(2, tab.getHighestReceived(MBR)); Assert.assertEquals(3, tab.getNextToReceive(MBR)); } public static void testAddWithGaps() { SeqnoTable tab=new SeqnoTable(0); boolean rc=tab.add(MBR, 0); assert rc; rc=tab.add(MBR, 1); assert rc; rc=tab.add(MBR, 2); assert rc; rc=tab.add(MBR, 4); assert rc; rc=tab.add(MBR, 5); assert rc; System.out.println("tab: " + tab); Assert.assertEquals(5, tab.getHighestReceived(MBR)); Assert.assertEquals(3, tab.getNextToReceive(MBR)); rc=tab.add(MBR, 3); assert rc; System.out.println("tab: " + tab); Assert.assertEquals(5, tab.getHighestReceived(MBR)); Assert.assertEquals(6, tab.getNextToReceive(MBR)); } public static void testAddWithGaps2() { SeqnoTable tab=new SeqnoTable(0); boolean rc=tab.add(MBR, 5); System.out.println("tab: " + tab); assert rc; Assert.assertEquals(5, tab.getHighestReceived(MBR)); Assert.assertEquals(0, tab.getNextToReceive(MBR)); rc=tab.add(MBR, 4); System.out.println("tab: " + tab); assert rc; Assert.assertEquals(5, tab.getHighestReceived(MBR)); Assert.assertEquals(0, tab.getNextToReceive(MBR)); rc=tab.add(MBR, 3); System.out.println("tab: " + tab); assert rc; Assert.assertEquals(5, tab.getHighestReceived(MBR)); Assert.assertEquals(0, tab.getNextToReceive(MBR)); rc=tab.add(MBR, 2); System.out.println("tab: " + tab); assert rc; Assert.assertEquals(5, tab.getHighestReceived(MBR)); Assert.assertEquals(0, tab.getNextToReceive(MBR)); rc=tab.add(MBR, 1); System.out.println("tab: " + tab); assert rc; Assert.assertEquals(5, tab.getHighestReceived(MBR)); Assert.assertEquals(0, tab.getNextToReceive(MBR)); rc=tab.add(MBR, 0); System.out.println("tab: " + tab); assert rc; Assert.assertEquals(5, tab.getHighestReceived(MBR)); Assert.assertEquals(6, tab.getNextToReceive(MBR)); } public static void testInsertionOfDuplicates() { SeqnoTable tab=new SeqnoTable(0); boolean rc=tab.add(MBR, 0); assert rc; rc=tab.add(MBR, 0); assert !(rc); rc=tab.add(MBR, 1); assert rc; rc=tab.add(MBR, 2); assert rc; rc=tab.add(MBR, 4); assert rc; rc=tab.add(MBR, 5); assert rc; System.out.println("tab: " + tab); rc=tab.add(MBR, 2); assert !rc; rc=tab.add(MBR, 3); assert rc; rc=tab.add(MBR, 3); assert !rc; } }libjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/tests/SeqnoTest.java0000644000175000017500000002760111647260573031432 0ustar moellermoellerpackage org.jgroups.tests; import org.jgroups.Global; import org.jgroups.util.*; import org.testng.annotations.Test; import java.util.*; import java.util.concurrent.ConcurrentHashMap; /** * @author Bela Ban */ @Test(groups=Global.FUNCTIONAL, sequential=false) public class SeqnoTest { public static void testConstructor() { SeqnoRange range=new SeqnoRange(10, 10); System.out.println(print(range)); assert range.size() == 1; assert range.getLow() == 10; assert range.getHigh() == 10; assert range.contains(10); assert !range.contains(11); range=new SeqnoRange(10, 15); System.out.println(print(range)); assert range.size() == 6; assert range.getLow() == 10; assert range.getHigh() == 15; assert range.contains(10); assert range.contains(14); } public static void testSetAndGetWith1Seqno() { Seqno range=new SeqnoRange(10, 10); assert range.getNumberOfMissingMessages() == 1; assert range.getNumberOfReceivedMessages() == 0; range.set(10); assert range.getNumberOfMissingMessages() == 0; assert range.getNumberOfReceivedMessages() == 1; assert range.get(10); range.clear(10); assert !range.get(10); assert range.getNumberOfMissingMessages() == 1; assert range.getNumberOfReceivedMessages() == 0; } public static void testSetAndGetWith5Seqnos() { SeqnoRange range=new SeqnoRange(10, 15); System.out.println("range=" + print(range)); assert range.size() == 6; assert range.getNumberOfMissingMessages() == 6; assert range.getNumberOfReceivedMessages() == 0; range.set(10); assert range.getNumberOfMissingMessages() == 5; assert range.getNumberOfReceivedMessages() == 1; assert range.get(10); range.set(13); assert range.size() == 6; assert range.getNumberOfMissingMessages() == 4; assert range.getNumberOfReceivedMessages() == 2; range.set(13); assert range.size() == 6; assert range.getNumberOfMissingMessages() == 4; assert range.getNumberOfReceivedMessages() == 2; System.out.println("range=" + print(range)); Collection xmits=range.getMessagesToRetransmit(); Collection cleared_bits=range.getBits(false); System.out.println("xmits = " + xmits); System.out.println("cleared_bits = " + cleared_bits); assert xmits.equals(cleared_bits); } public static void testGetBits() { SeqnoRange range=new SeqnoRange(1, 100); System.out.println("range = " + range); assert range.size() == 100; Collection bits=range.getBits(false); assert bits.size() == 1; Range tmp=bits.iterator().next(); assert tmp.low == 1 && tmp.high == 100; range.set(1,2); assert range.size() == 100; bits=range.getBits(true); assert bits != null && bits.size() == 1; // 1 range: [1-2] tmp=bits.iterator().next(); assert tmp.low == 1 && tmp.high == 2; for(int i=1; i < 100; i++) range.set(i); bits=range.getBits(false); assert bits.size() == 1; tmp=bits.iterator().next(); assert tmp.low == 100 && tmp.high == 100; for(int i=1; i <= range.size(); i++) range.clear(i); for(int i=2; i <= 99; i++) range.set(i); bits=range.getBits(true); assert bits.size() == 1; tmp=bits.iterator().next(); assert tmp.low == 2 && tmp.high == 99; bits=range.getBits(false); assert bits.size() == 2; tmp=bits.iterator().next(); assert tmp.low == 1 && tmp.high == 1; Iterator it=bits.iterator(); it.next(); tmp=it.next(); assert tmp.low == 100 && tmp.high == 100; } public static void testSet() { SeqnoRange range=new SeqnoRange(10, 15); range.set(11, 12, 13, 14); System.out.println("range=" + print(range)); assert range.size() == 6; assert range.getNumberOfReceivedMessages() == 4; assert range.getNumberOfMissingMessages() == 2; Collection xmits=range.getMessagesToRetransmit(); assert xmits.size() == 2; Iterator it=xmits.iterator(); Range r=it.next(); assert r.low == 10 && r.high == 10; r=it.next(); assert r.low == 15 && r.high == 15; range=new SeqnoRange(10, 15); range.set(10,11,12,13,14); System.out.println("range=" + print(range)); assert range.size() == 6; assert range.getNumberOfReceivedMessages() == 5; assert range.getNumberOfMissingMessages() == 1; xmits=range.getMessagesToRetransmit(); assert xmits.size() == 1; it=xmits.iterator(); r=it.next(); assert r.low == 15 && r.high == 15; range=new SeqnoRange(10, 15); range.set(11,12,13,14,15); System.out.println("range=" + print(range)); assert range.size() == 6; assert range.getNumberOfReceivedMessages() == 5; assert range.getNumberOfMissingMessages() == 1; xmits=range.getMessagesToRetransmit(); assert xmits.size() == 1; it=xmits.iterator(); r=it.next(); assert r.low == 10 && r.high == 10; range=new SeqnoRange(10, 15); range.set(10,11,12,13,14,15); System.out.println("range=" + print(range)); assert range.size() == 6; assert range.getNumberOfReceivedMessages() == 6; assert range.getNumberOfMissingMessages() == 0; xmits=range.getMessagesToRetransmit(); assert xmits.isEmpty(); range=new SeqnoRange(10, 15); range.set(11,12,14,15); System.out.println("range=" + print(range)); assert range.size() == 6; assert range.getNumberOfReceivedMessages() == 4; assert range.getNumberOfMissingMessages() == 2; xmits=range.getMessagesToRetransmit(); assert xmits.size() == 2; it=xmits.iterator(); r=it.next(); assert r.low == 10 && r.high == 10; r=it.next(); assert r.low == 13 && r.high == 13; range.set(13); assert range.getNumberOfReceivedMessages() == 5; assert range.getNumberOfMissingMessages() == 1; xmits=range.getMessagesToRetransmit(); it=xmits.iterator(); r=it.next(); assert r.low == 10 && r.high == 10; range.set(10); System.out.println("range=" + print(range)); assert range.getNumberOfReceivedMessages() == 6; assert range.getNumberOfMissingMessages() == 0; xmits=range.getMessagesToRetransmit(); assert xmits.isEmpty(); } @Test(expectedExceptions=IllegalArgumentException.class) public static void testSetOfInvalidIndex() { SeqnoRange range=new SeqnoRange(10, 10); range.set(9); } public static void testCompareTo() { TreeMap map=new TreeMap(new SeqnoComparator()); Seqno[] ranges=new Seqno[]{new SeqnoRange(900,905), new Seqno(222), new SeqnoRange(700,800), new SeqnoRange(23,200)}; for(Seqno range: ranges) map.put(range, range); System.out.println("map = " + map.keySet()); assert map.size() == ranges.length; for(long num: new long[]{0, 1, 201, 202, 223, 1000}) { checkNull(map, num); } checkInRange(map, 23, 23, 200); checkInRange(map, 100, 23, 200); checkInRange(map, 200, 23, 200); checkInRange(map, 222, 222, 222); checkInRange(map, 750, 700, 800); checkInRange(map, 905, 900, 905); } public static void testCompareTo2() { TreeMap map=new TreeMap(new SeqnoComparator()); Seqno[] ranges=new Seqno[]{new SeqnoRange(900,905), new Seqno(550), new Seqno(222), new SeqnoRange(700,800), new Seqno(650), new SeqnoRange(23,200)}; for(Seqno range: ranges) map.put(range, range); System.out.println("map = " + map.keySet()); assert map.size() == 6; for(long num: new long[]{0, 1, 201, 202, 223, 1000}) { checkNull(map, num); } checkInRange(map, 550, 550, 550); checkInRange(map, 650, 650, 650); checkInRange(map, 23, 23, 200); checkInRange(map, 100, 23, 200); checkInRange(map, 200, 23, 200); checkInRange(map, 222, 222, 222); checkInRange(map, 750, 700, 800); checkInRange(map, 905, 900, 905); } public static void testLargeRange() { SeqnoRange range=new SeqnoRange(0, 1500); Set sorted_set=new TreeSet(); for(int i=0; i < 500; i++) { int num=(int)Util.random(1499); sorted_set.add(num); } for(int num: sorted_set) range.set(num); int num_set=sorted_set.size(); System.out.println("set " + num_set + " bits"); assert range.getNumberOfReceivedMessages() == num_set; Collection missing=range.getMessagesToRetransmit(); System.out.println("missing = " + missing); } public static void testRemovalFromTreeMap() { Map map=new TreeMap(new SeqnoComparator()); Seqno[] ranges=new Seqno[]{new SeqnoRange(900,905), new Seqno(222), new Seqno(500), new SeqnoRange(700,800), new Seqno(801), new SeqnoRange(23,200)}; for(Seqno range: ranges) map.put(range, range); System.out.println("map = " + map.keySet()); assert map.size() == ranges.length; for(Seqno r: ranges) { Seqno range=map.get(r); assert range != null; assert range == r; // must point to the same object in memory } for(Seqno r: ranges) { Seqno range=map.remove(r); assert range != null; assert range == r; } assert map.isEmpty(); } public static void testRemovalFromHashMap() { Map map=new ConcurrentHashMap(); Seqno[] ranges=new Seqno[]{new SeqnoRange(900,905), new Seqno(222), new SeqnoRange(700,800), new SeqnoRange(23,200), new Seqno(201), new Seqno(205)}; for(Seqno range: ranges) map.put(range, range); System.out.println("map = " + map.keySet()); assert map.size() == ranges.length; for(Seqno r: ranges) { Seqno range=map.get(r); assert range != null; assert range == r; // must point to the same object in memory } for(Seqno r: ranges) { Seqno range=map.remove(r); assert range != null; assert range == r; } assert map.isEmpty(); } private static void checkInRange(Map map, long seqno, long from, long to) { Seqno val=map.get(new Seqno(seqno, true)); System.out.println("seqno=" + seqno + ", val = " + val); assert val != null; assert val.contains(seqno); assert val.getLow() == from; if(val instanceof SeqnoRange) assert ((SeqnoRange)val).getHigh() == to; } private static void checkNull(Map map, long seqno) { Seqno val=map.get(new Seqno(seqno, true)); assert val == null; } private static String print(Seqno seqno) { StringBuilder sb=new StringBuilder(); sb.append(seqno.toString()); sb.append(", size= " + seqno.size()); if(seqno instanceof SeqnoRange) { sb.append(", received=" + ((SeqnoRange)seqno).printBits(true) + " (" + seqno.getNumberOfReceivedMessages() + ")"); sb.append(", missing=" + ((SeqnoRange)seqno).printBits(false) + " (" + seqno.getNumberOfMissingMessages() + ")"); } return sb.toString(); } } libjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/tests/SetPropertyTest.java0000644000175000017500000000167111647260573032644 0ustar moellermoellerpackage org.jgroups.tests; import org.jgroups.JChannel; import org.jgroups.Global; import org.jgroups.ChannelException; import org.jgroups.util.Util; import org.jgroups.protocols.TP; import org.testng.annotations.Test; import org.testng.annotations.BeforeClass; import org.testng.annotations.AfterClass; /** * @author Bela Ban */ @Test(groups=Global.FUNCTIONAL) public class SetPropertyTest { JChannel ch; @BeforeClass void init() throws ChannelException { ch=new JChannel(); } @AfterClass void destroy() { Util.close(ch); } public void testSetter() { TP transport=ch.getProtocolStack().getTransport(); int port=transport.getBindPort(); System.out.println("port = " + port); transport.setBindPort(port +20); int old_port=port; port=transport.getBindPort(); System.out.println("port = " + port); assert old_port + 20 == port; } } libjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/tests/SimulatorTest.java0000644000175000017500000003157211647260573032326 0ustar moellermoeller package org.jgroups.tests; import org.testng.annotations.Test ; import org.testng.annotations.BeforeMethod ; import org.testng.annotations.AfterMethod ; import org.testng.Assert; import org.jgroups.*; import org.jgroups.debug.Simulator; import org.jgroups.protocols.DELAY; import org.jgroups.stack.IpAddress; import org.jgroups.stack.Protocol; import java.util.Properties; import java.util.Vector; /** * Tests for the fault-injection features of Simulator. * * @author Richard Achmatowicz */ @Test(groups=Global.FUNCTIONAL, sequential=true) public class SimulatorTest { final static int NUM_PEERS = 3; final static int NUM_MSGS = 5; final static int WAIT_TIMEOUT = 5; // secs final static int MSGS_PER_STATUS_LINE = 1; // convey assertion failure from thread to main framework static boolean allMsgsReceived = false ; IpAddress[] addresses = new IpAddress[NUM_PEERS] ; Vector
                members = null ; View view = null ; Simulator[] simulators = new Simulator[NUM_PEERS] ; DELAY[] layers = new DELAY[NUM_PEERS] ; Protocol[][] stacks = new Protocol[NUM_PEERS][] ; Thread[] threads = new Thread[NUM_PEERS] ; //define senders and receivers boolean[] isSender = new boolean[NUM_PEERS] ; // used to wait for signal that all messages received static Object all_msgs_recd = new Object() ; /** * Set up a number of simulator instances wrapping NAKACK */ @BeforeMethod(alwaysRun=true) public void setUp() throws Exception { System.out.println("calling setUp()") ; // define the senders and the receivers isSender[0] = false ; isSender[1] = true ; isSender[2] = true ; // dummy IP addresses and ports addresses[0] = new IpAddress(1111); addresses[1] = new IpAddress(2222); addresses[2] = new IpAddress(3333); // dummy set of members which works for all three simulators members = new Vector
                (); for (int i = 0 ; i < NUM_PEERS; i++) { members.add(addresses[i]); } // create a dummy View(creator, timestamp, member set) view = new View(addresses[0], 1, members); // create new simulator instances for (int i = 0; i < NUM_PEERS; i++) { createSimulator(simulators, view, addresses, layers, stacks, i) ; } // describe the configuration of the three simulators for (int i = 0; i < NUM_PEERS; i++) { for (int j = 0; j < NUM_PEERS; j++) { if (i == j) simulators[i].addMember(addresses[j]) ; else simulators[i].addMember(addresses[j], simulators[j]) ; } } // set up the receiver callbacks for each simulator Simulator.Receiver[] receivers = new Simulator.Receiver[NUM_PEERS] ; // set up the sender and the receiver callbacks, according to whether // the peer is a sender or a receiver for (int i = 0; i < NUM_PEERS; i++) { if (isSender[i]) receivers[i] = new SenderPeer(simulators[i]) ; else receivers[i] = new ReceiverPeer(simulators[i]) ; simulators[i].setReceiver(receivers[i]); } // start the simulators for (int i = 0; i < NUM_PEERS; i++) simulators[i].start(); System.out.println("Ending setUp()") ; } @AfterMethod(alwaysRun=true) public void tearDown() throws Exception { System.out.println("Calling tearDown()") ; // reset messages received flag allMsgsReceived = false ; // stop the simulators for (int i = 0; i < NUM_PEERS; i++) simulators[i].stop(); System.out.println("Ending tearDown()") ; } private void createSimulator(Simulator[] simulators, View view, Address[] addresses, DELAY[] layers, Protocol[][] stacks, int i) { // create the simulator instance simulators[i] = new Simulator(); simulators[i].setLocalAddress(addresses[i]); simulators[i].setView(view); // set up a dummy passthrough layer using DELAY layers[i] = new DELAY(); // set up its properties layers[i].setInDelay(0); layers[i].setOutDelay(0); // our protocol stack under test consists of one protocol stacks[i] = new Protocol[]{layers[i]}; simulators[i].setProtocolStack(stacks[i]); } /* * Drop messages from our address to destination address a */ class MyDropMessage implements Simulator.DropMessage { Address address = null ; MyDropMessage(Address a) { this.address = a ; } public boolean drop(Message msg, Address dest) { // multicast messages have their source address set, and drop // gets called |view| times, each with a different dest parameter // here we drop that part of a multicast which is headed for dest if (msg.getDest() == null && dest.equals(address)) { return true ; } // drop when sending specifically to address if (msg.getDest() != null && msg.getDest().equals(address)) { return true ; } return false ; } } /** * Test dropped messages modelling. */ public void testDroppedMessages() { System.out.println("Starting testDroppedMessages") ; // set up a drop function which drops all messages from 2 to 0 // (even those message instances which are part of a multicast) Simulator.DropMessage d = new MyDropMessage(addresses[0]) ; simulators[2].registerDropMessage(d) ; // start the NAKACK peers and let them exchange messages for (int i = 0; i < NUM_PEERS; i++) { threads[i] = new MyPeer(simulators[i], isSender[i]) ; threads[i].start() ; } // wait for the receiver peer to signal that it has received messages, or timeout synchronized(all_msgs_recd) { try { all_msgs_recd.wait(WAIT_TIMEOUT * 1000) ; } catch(InterruptedException e) { System.out.println("main thread interrupted") ; } } // wait for the threads to terminate try { for (int i = 0; i < NUM_PEERS; i++) { threads[i].join() ; } } catch(InterruptedException e) { } int receiver = ((ReceiverPeer)simulators[0].getReceiver()).getNumberOfReceivedMessages() ; int sender1 = ((SenderPeer)simulators[1].getReceiver()).getNumberOfReceivedMessages() ; int sender2 = ((SenderPeer)simulators[2].getReceiver()).getNumberOfReceivedMessages() ; Assert.assertFalse(allMsgsReceived, "receiver received all messages from both peers") ; Assert.assertTrue(receiver == NUM_MSGS, "receiver did not receive all messages from single peer: received " + receiver) ; Assert.assertTrue(sender1 == 2 * NUM_MSGS, "sender1 did not receive messages from itself and other sender: received " + sender1) ; Assert.assertTrue(sender2 == 2 * NUM_MSGS, "sender2 did not receive messages from itself and other sender: received " + sender2) ; } /** * Test crash failure modelling. */ public void testCrashFailure() { // simulate crash failure on sender System.out.println("Starting testCrashfailure") ; simulators[1].simulateCrashFailure() ; // start the NAKACK peers and let them exchange messages for (int i = 0; i < NUM_PEERS; i++) { threads[i] = new MyPeer(simulators[i], isSender[i]) ; threads[i].start() ; } // wait for the receiver peer to signal that it has received messages, or timeout synchronized(all_msgs_recd) { try { all_msgs_recd.wait(WAIT_TIMEOUT * 1000) ; } catch(InterruptedException e) { System.out.println("main thread interrupted") ; } } // wait for the threads to terminate try { for (int i = 0; i < NUM_PEERS; i++) { threads[i].join() ; } } catch(InterruptedException e) { } int receiver = ((ReceiverPeer)simulators[0].getReceiver()).getNumberOfReceivedMessages() ; int sender1 = ((SenderPeer)simulators[1].getReceiver()).getNumberOfReceivedMessages() ; int sender2 = ((SenderPeer)simulators[2].getReceiver()).getNumberOfReceivedMessages() ; Assert.assertFalse(allMsgsReceived, "receiver received all messages from both peers") ; Assert.assertTrue(receiver == NUM_MSGS, "receiver did not receive all messages from single peer") ; Assert.assertTrue(sender1 == 0, "sender1 received messages") ; Assert.assertTrue(sender2 == NUM_MSGS, "sender2 did not receive messages only from itself") ; } /** * Test network partition modelling. */ public void testNetworkPartition() { System.out.println("Starting testNetworkPartition") ; Address[] part1 = {addresses[0], addresses[1]} ; Address[] part2 = {addresses[2]} ; simulators[0].simulatePartition(part1) ; simulators[1].simulatePartition(part1) ; simulators[2].simulatePartition(part2) ; // start the NAKACK peers and let them exchange messages for (int i = 0; i < NUM_PEERS; i++) { threads[i] = new MyPeer(simulators[i], isSender[i]) ; threads[i].start() ; } // wait for the receiver peer to signal that it has received messages, or timeout synchronized(all_msgs_recd) { try { all_msgs_recd.wait(WAIT_TIMEOUT * 1000) ; } catch(InterruptedException e) { System.out.println("main thread interrupted") ; } } // wait for the threads to terminate try { for (int i = 0; i < NUM_PEERS; i++) { threads[i].join() ; } } catch(InterruptedException e) { } int receiver = ((ReceiverPeer)simulators[0].getReceiver()).getNumberOfReceivedMessages() ; int sender1 = ((SenderPeer)simulators[1].getReceiver()).getNumberOfReceivedMessages() ; int sender2 = ((SenderPeer)simulators[2].getReceiver()).getNumberOfReceivedMessages() ; Assert.assertFalse(allMsgsReceived, "receiver received all messages from both peers") ; Assert.assertTrue(receiver == NUM_MSGS, "receiver did not receive all messages from single peer") ; Assert.assertTrue(sender1 == NUM_MSGS, "sender1 did not receive messages only from itself") ; Assert.assertTrue(sender2 == NUM_MSGS, "sender2 did not receive messages only from itself") ; } /** * This is called by the Simulator when a message comes back up the stack. * Used by message senders to simply display messages received from other peers. */ class SenderPeer implements Simulator.Receiver { Simulator simulator = null ; int num_mgs_received=0; SenderPeer(Simulator s) { this.simulator = s ; } // keep track of how many messages were received by this peer public void receive(Event evt) { if(evt.getType() == Event.MSG) { num_mgs_received++; if(num_mgs_received % MSGS_PER_STATUS_LINE == 0) System.out.println("<" + simulator.getLocalAddress() + ">:" + "<== " + num_mgs_received); } } public int getNumberOfReceivedMessages() { return num_mgs_received; } } /** * This is called by the Simulator when a message comes back up the stack. * This method should do the following: * - receive messages from senders * - terminate when correct number of messages have been received */ class ReceiverPeer implements Simulator.Receiver { Simulator simulator = null ; int num_mgs_received=0; Message msg ; Address sender ; ReceiverPeer(Simulator s) { this.simulator = s ; } public synchronized void receive(Event evt) { if (evt.getType() == Event.MSG) { // keep track of seqno ordering of messages received msg=(Message)evt.getArg(); sender=msg.getSrc(); try { num_mgs_received++ ; Address address = simulator.getLocalAddress() ; if(num_mgs_received % MSGS_PER_STATUS_LINE == 0) System.out.println("<" + address + ">:" + "PASS: received msg #" + num_mgs_received + " from " + sender); // condition to terminate the test - all messages received (whether in // correct order or not) if(num_mgs_received >= SimulatorTest.NUM_MSGS * (NUM_PEERS-1)) { // indicate that we have received the required number of messages // to differentiate between timeout and notifyAll cases on monitor allMsgsReceived = true ; // signal that all messages have been received - this will allow the receiver // thread to terminate normally synchronized(all_msgs_recd) { all_msgs_recd.notifyAll() ; } } } catch(Exception ex) { System.out.println("SimulatorTest.receive()" + ex.toString()); } } } public int getNumberOfReceivedMessages() { return num_mgs_received; } } static class MyPeer extends Thread { Simulator s = null ; boolean sender = false ; public MyPeer(Simulator s, boolean sender) { this.s = s ; this.sender = sender ; } public void run() { // senders send NUM_MSGS messages to all peers, beginning with seqno 1 if (sender) { Address address = s.getLocalAddress() ; // send a collection of dummy messages by mcast to the stack under test for(int i=1; i <= NUM_MSGS; i++) { Message msg=new Message(null, address, new Long(i)); Event evt=new Event(Event.MSG, msg); // call Simulator.send() to introduce the event into the stack under test s.send(evt); // status indicator if(i % MSGS_PER_STATUS_LINE == 0) System.out.println("<" + address + ">:" + " ==> " + i); } } if (!sender) { // wait for the receiver callback to signal that it has received messages, or timeout // this just causes this thread to block until its receiver has finished synchronized(all_msgs_recd) { try { all_msgs_recd.wait(WAIT_TIMEOUT * 1000) ; } catch(InterruptedException e) { System.out.println("thread interrupted") ; } } } } } } libjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/tests/SizeTest.java0000644000175000017500000007162211647260573031261 0ustar moellermoeller package org.jgroups.tests; import org.jgroups.*; import org.jgroups.blocks.RequestCorrelator; import org.jgroups.mux.MuxHeader; import org.jgroups.mux.ServiceInfo; import org.jgroups.protocols.*; import org.jgroups.protocols.pbcast.*; import org.jgroups.stack.GossipData; import org.jgroups.stack.IpAddress; import org.jgroups.util.*; import org.jgroups.util.UUID; import org.testng.Assert; import org.testng.annotations.Test; import java.io.*; import java.util.*; /** * Tests whether method size() of a header and its serialized size correspond * @author Bela Ban */ @Test(groups=Global.FUNCTIONAL) public class SizeTest { public static void testTpHeader() throws Exception { _testSize(new TpHeader("DemoChannel")); } public static void testPingHeader() throws Exception { _testSize(new PingHeader(PingHeader.GET_MBRS_REQ, "bla")); _testSize(new PingHeader(PingHeader.GET_MBRS_RSP, new PingData())); _testSize(new PingHeader(PingHeader.GET_MBRS_RSP, (PingData)null)); _testSize(new PingHeader(PingHeader.GET_MBRS_RSP, (String)null)); _testSize(new PingHeader(PingHeader.GET_MBRS_RSP, new PingData(Util.createRandomAddress(), null, true))); Address self=Util.createRandomAddress(); PingData rsp=new PingData(self, Util.createView(self, 1, self), true); _testSize(new PingHeader(PingHeader.GET_MBRS_RSP, rsp)); rsp=new PingData(self, Util.createView(self, 1, self, Util.createRandomAddress(), Util.createRandomAddress()), true); _testSize(new PingHeader(PingHeader.GET_MBRS_RSP, rsp)); } public static void testPingData() throws Exception { PingData data; final Address own=org.jgroups.util.UUID.randomUUID(); final Address coord=org.jgroups.util.UUID.randomUUID(); final PhysicalAddress physical_addr_1=new IpAddress("127.0.0.1", 7500); final PhysicalAddress physical_addr_2=new IpAddress("192.168.1.5", 6000); final PhysicalAddress physical_addr_3=new IpAddress("192.134.2.1", 6655); final Address self=Util.createRandomAddress(); data=new PingData(null, null, false); _testSize(data); data=new PingData(own, Util.createView(coord, 22, coord, Util.createRandomAddress()), false); _testSize(data); data=new PingData(null, null, false, "node-1", null); _testSize(data); data=new PingData(own, Util.createView(coord, 22, coord), false, "node-1", null); _testSize(data); data=new PingData(own, Util.createView(coord, 22, coord), false, "node-1", new ArrayList(7)); _testSize(data); data=new PingData(null, null, false, "node-1", new ArrayList(7)); _testSize(data); List list=new ArrayList(); list.add(physical_addr_1); list.add(physical_addr_2); list.add(physical_addr_3); data=new PingData(null, null, false, "node-1", list); _testSize(data); list.clear(); list.add(new IpAddress("127.0.0.1", 7500)); data=new PingData(null, null, false, "node-1", list); _testSize(data); View view=Util.createView(coord, 322649, coord, own, UUID.randomUUID()); data.setView(view); _testSize(data); data=new PingData(self, Util.createView(self, 1, self), true, "logical-name", null); _testSize(data); } public static void testGossipData() throws Exception { GossipData data; final Address own=org.jgroups.util.UUID.randomUUID(); final Address coord=org.jgroups.util.UUID.randomUUID(); UUID.add((UUID)own, "own"); UUID.add((UUID)coord, "coord"); final PhysicalAddress physical_addr_1=new IpAddress("127.0.0.1", 7500); final PhysicalAddress physical_addr_2=new IpAddress("192.168.1.5", 6000); final PhysicalAddress physical_addr_3=new IpAddress("192.134.2.1", 6655); _testSize(new GossipData()); data=new GossipData((byte)1); _testSize(data); data=new GossipData((byte)1, "DemoCluster", own, (List
                )null, null); _testSize(data); data=new GossipData((byte)1, "DemoCluster", own, Arrays.asList(own, coord), null); _testSize(data); data=new GossipData((byte)1, "DemoCluster", own, Arrays.asList(own, coord), Arrays.asList(physical_addr_1, physical_addr_2, physical_addr_3)); _testSize(data); List list=new ArrayList(); list.add(physical_addr_1); list.add(physical_addr_2); list.add(physical_addr_3); data=new GossipData((byte)1, "DemoCluster", own, Arrays.asList(own, coord), list); _testSize(data); data=new GossipData((byte)1, "demo", own, "logical_name", null); _testSize(data); data=new GossipData((byte)1, "demo", own, new byte[]{'b', 'e', 'l', 'a'}); _testSize(data); byte[] buffer=new byte[10]; buffer[2]='B'; buffer[3]='e'; buffer[4]='l'; buffer[5]='a'; data=new GossipData((byte)1, "demo", own, buffer, 2, 4); _testSize(data); } public static void testDigest() throws Exception { Address addr=Util.createRandomAddress(); MutableDigest mutableDigest=new MutableDigest(2); mutableDigest.add(addr, 100, 200, 205); mutableDigest.add(Util.createRandomAddress(), 102, 104, 105); _testSize(mutableDigest); Digest digest=new Digest(); _testSize(digest); digest=new Digest(10); _testSize(digest); digest=new Digest(Util.createRandomAddress(), 10, 45, 50); _testSize(digest); } public static void testNakackHeader() throws Exception { _testSize(NakAckHeader.createMessageHeader(322649)); _testSize(NakAckHeader.createXmitRequestHeader(100, 104, Util.createRandomAddress())); _testSize(NakAckHeader.createXmitResponseHeader()); } public static 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); Map cache=new Hashtable(); cache.put(a1, a2); cache.put(a2, a1); sockhdr=new FD_SOCK.FdHeader(FD_SOCK.FdHeader.SUSPECT, cache); _testSize(sockhdr); } public static 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 static void testUnicastHeader() throws Exception { UNICAST.UnicastHeader hdr=UNICAST.UnicastHeader.createDataHeader(322649, (short)127, false); _testSize(hdr); hdr=UNICAST.UnicastHeader.createDataHeader(322649, Short.MAX_VALUE, false); _testSize(hdr); hdr=UNICAST.UnicastHeader.createDataHeader(322649, (short)(Short.MAX_VALUE -10), true); _testSize(hdr); hdr=UNICAST.UnicastHeader.createAckHeader(322649); _testSize(hdr); hdr=UNICAST.UnicastHeader.createSendFirstSeqnoHeader(); _testSize(hdr); } public static void testStableHeader() throws Exception { org.jgroups.protocols.pbcast.STABLE.StableHeader hdr; Address addr=UUID.randomUUID(); 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 static 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 static 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 static 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 static 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 static void testMergeId() throws Exception { MergeId id=MergeId.create(UUID.randomUUID()); System.out.println("id = " + id); _testSize(id); id=MergeId.create(UUID.randomUUID()); System.out.println("id = " + id); _testSize(id); Address addr=UUID.randomUUID(); id=MergeId.create(addr); System.out.println("id = " + id); _testSize(id); id=MergeId.create(addr); System.out.println("id = " + id); _testSize(id); id=MergeId.create(addr); System.out.println("id = " + id); _testSize(id); } public static void testView() throws Exception { View v=new View(); _testSize(v); ViewId vid=new ViewId(UUID.randomUUID(), 322649); Vector
                mbrs=new Vector
                (); v=new View(vid, mbrs); _testSize(v); mbrs.add(UUID.randomUUID()); _testSize(v); mbrs.add(UUID.randomUUID()); _testSize(v); } public static void testViewPayload() throws Exception { View v=new View(); v.addPayload("name", "Bela Ban"); _testSize(v); ViewId vid=new ViewId(UUID.randomUUID(), 322649); Vector
                mbrs=new Vector
                (); v=new View(vid, mbrs); v.addPayload("id", 322649); v.addPayload("name", "Michelle"); _testSize(v); mbrs.add(UUID.randomUUID()); _testSize(v); mbrs.add(UUID.randomUUID()); _testSize(v); } public static void testMergeView() throws Exception { View v=new MergeView(); _testSize(v); ViewId vid=new ViewId(UUID.randomUUID(), 322649); Vector
                mbrs=new Vector
                (); v=new MergeView(vid, mbrs, null); _testSize(v); mbrs.add(UUID.randomUUID()); _testSize(v); mbrs.add(UUID.randomUUID()); _testSize(v); } public static void testMergeView2() throws Exception { Vector
                m1, m2 , m3, all; Vector subgroups; Address a,b,c,d,e,f; View v1, v2, v3, view_all; a=Util.createRandomAddress(); b=Util.createRandomAddress(); c=Util.createRandomAddress(); d=Util.createRandomAddress(); e=Util.createRandomAddress(); f=Util.createRandomAddress(); 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 static void testMergeView3() throws Exception { Vector
                m1, m2 , m3, all; Vector 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 static 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 static void testGmsHeader() throws Exception { Address addr=UUID.randomUUID(); 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); Collection
                mbrs=new ArrayList
                (); Collections.addAll(mbrs, UUID.randomUUID(), UUID.randomUUID(), UUID.randomUUID()); hdr=new GMS.GmsHeader(GMS.GmsHeader.MERGE_REQ, mbrs); } public static void testFCHeader() throws Exception { FcHeader hdr=new FcHeader(FcHeader.REPLENISH); _testSize(hdr); } public static void testFragHeader() throws Exception { FragHeader hdr=new FragHeader(322649, 1, 10); _testSize(hdr); } public static void testCompressHeader() throws Exception { COMPRESS.CompressHeader hdr=new COMPRESS.CompressHeader(2002); _testSize(hdr); } public static void testStompHeader() throws Exception { STOMP.StompHeader hdr=STOMP.StompHeader.createHeader(STOMP.StompHeader.Type.MESSAGE, "destination", "/topics/chat", "sender", UUID.randomUUID().toString()); _testSize(hdr); hdr=STOMP.StompHeader.createHeader(STOMP.StompHeader.Type.ENDPOINT, "endpoint", "192.168.1.5:8787"); _testSize(hdr); } public static void testRelayHeader() throws Exception { RELAY.RelayHeader hdr=RELAY.RelayHeader.create(RELAY.RelayHeader.Type.FORWARD); _testSize(hdr); hdr=RELAY.RelayHeader.createDisseminateHeader(Util.createRandomAddress("A")); _testSize(hdr); Map uuid_cache=new HashMap(); uuid_cache.put(Util.createRandomAddress("A"), "A"); uuid_cache.put(Util.createRandomAddress("B"), "B"); uuid_cache.put(Util.createRandomAddress("B"), "B"); // hdr=RELAY.RelayHeader.create(RELAY.RelayHeader.Type.UUIDS); // _testSize(hdr); } public static 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 static 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 static void testIpAddress() throws Exception { IpAddress addr=new IpAddress(); _testSize(addr); } public static void testIpAddress1() throws Exception { IpAddress addr=new IpAddress("127.0.0.1", 5555); _testSize(addr); } public static void testIpAddressWithHighPort() throws Exception { IpAddress addr=new IpAddress("127.0.0.1", 65535); _testSize(addr); } public static void testIpAddress2() throws Exception { IpAddress addr=new IpAddress(3456); _testSize(addr); } public static void testIpAddress3() throws Exception { IpAddress addr=new IpAddress(5555, false); _testSize(addr); } public static void testIpAddressWithAdditionalData() throws Exception { IpAddress addr=new IpAddress(5555, false); addr.setAdditionalData("bela".getBytes()); _testSize(addr); } public static void testWriteAddress() throws IOException, IllegalAccessException, InstantiationException { Address uuid=UUID.randomUUID(); _testWriteAddress(uuid); ((UUID)uuid).setAdditionalData("Bela Ban".getBytes()); _testWriteAddress(uuid); Address addr=new IpAddress(7500); _testWriteAddress(addr); addr=new IpAddress("127.0.0.1", 5678); _testWriteAddress(addr); ((IpAddress)addr).setAdditionalData("Bela Ban".getBytes()); _testWriteAddress(addr); } private static void _testWriteAddress(Address addr) throws IOException, InstantiationException, IllegalAccessException { int len=Util.size(addr); ByteArrayOutputStream output=new ByteArrayOutputStream(); DataOutputStream out=new DataOutputStream(output); Util.writeAddress(addr, out); out.flush(); byte[] buf=output.toByteArray(); out.close(); System.out.println("\nlen=" + len + ", serialized length=" + buf.length); assert len == buf.length; DataInputStream in=new DataInputStream(new ByteArrayInputStream(buf)); Address new_addr=Util.readAddress(in); System.out.println("old addr=" + addr + "\nnew addr=" + new_addr); assert addr.equals(new_addr); } public static void testWriteAddresses() throws IOException, IllegalAccessException, InstantiationException { List
                list=new ArrayList
                (); for(int i=0; i < 3; i++) list.add(UUID.randomUUID()); _testWriteAddresses(list); list.clear(); list.add(new IpAddress(7500)); list.add(new IpAddress("192.168.1.5", 4444)); list.add(new IpAddress("127.0.0.1", 5674)); _testWriteAddresses(list); } private static void _testWriteAddresses(List
                list) throws IOException, InstantiationException, IllegalAccessException { long len=Util.size(list); ByteArrayOutputStream output=new ByteArrayOutputStream(); DataOutputStream out=new DataOutputStream(output); Util.writeAddresses(list, out); out.flush(); byte[] buf=output.toByteArray(); out.close(); System.out.println("\nlen=" + len + ", serialized length=" + buf.length); assert len == buf.length; DataInputStream in=new DataInputStream(new ByteArrayInputStream(buf)); Collection new_list=Util.readAddresses(in, ArrayList.class); System.out.println("old list=" + list + "\nnew list=" + new_list); assert list.equals(new_list); } public static void testUUID() throws Exception { org.jgroups.util.UUID uuid=org.jgroups.util.UUID.randomUUID(); System.out.println("uuid = " + uuid); _testSize(uuid); uuid=org.jgroups.util.UUID.randomUUID(); byte[] buf=Util.streamableToByteBuffer(uuid); org.jgroups.util.UUID uuid2=(org.jgroups.util.UUID)Util.streamableFromByteBuffer(org.jgroups.util.UUID.class, buf); System.out.println("uuid: " + uuid); System.out.println("uuid2: " + uuid2); assert uuid.equals(uuid2); int hash1=uuid.hashCode(), hash2=uuid2.hashCode(); System.out.println("hash 1: " + hash1); System.out.println("hash 2: " + hash2); assert hash1 == hash2; uuid.setAdditionalData("bela ban".getBytes()); _testSize(uuid); } public static void testRequestCorrelatorHeader() throws Exception { RequestCorrelator.Header hdr; hdr=new RequestCorrelator.Header(RequestCorrelator.Header.REQ, 322649, false, (short)1000); _testSize(hdr); hdr=new RequestCorrelator.Header(RequestCorrelator.Header.RSP, 322649, true, (short)356); 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); Assert.assertEquals(322649, hdr.id); assert hdr.rsp_expected; Assert.assertEquals((short)356, hdr.corrId); Assert.assertEquals(RequestCorrelator.Header.RSP, hdr.type); hdr=new RequestCorrelator.Header(RequestCorrelator.Header.RSP, 322649, true, (short)356); output=new ByteArrayOutputStream(); out=new DataOutputStream(output); hdr.writeTo(out); out.flush(); buf=output.toByteArray(); out.close(); input=new ByteArrayInputStream(buf); in=new DataInputStream(input); hdr=new RequestCorrelator.Header(); hdr.readFrom(in); Assert.assertEquals(322649, hdr.id); assert hdr.rsp_expected; Assert.assertEquals(356, hdr.corrId); Assert.assertEquals(RequestCorrelator.Header.RSP, hdr.type); } public static void testServiceInfo() throws Exception { ServiceInfo si=new ServiceInfo(); _testSize(si); } public static 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(Digest digest) throws Exception { long len=digest.serializedSize(); byte[] serialized_form=Util.streamableToByteBuffer(digest); System.out.println("digest = " + digest); System.out.println("size=" + len + ", serialized size=" + serialized_form.length); assert len == serialized_form.length; } private static void _testSize(Header hdr) throws Exception { long size=hdr.size(); byte[] serialized_form=Util.streamableToByteBuffer(hdr); System.out.println("size=" + size + ", serialized size=" + serialized_form.length); Assert.assertEquals(serialized_form.length, size); } 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); Assert.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); Assert.assertEquals(serialized_form.length, size); } private static void _testSize(MergeId id) throws Exception { long size=id.size(); byte[] serialized_form=Util.streamableToByteBuffer(id); System.out.println("size=" + size + ", serialized size=" + serialized_form.length); assert 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); Assert.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); Assert.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); Assert.assertEquals(serialized_form.length, size); } private static void _testSize(PingData data) throws Exception { System.out.println("\ndata: " + data); long size=data.size(); byte[] serialized_form=Util.streamableToByteBuffer(data); System.out.println("size=" + size + ", serialized size=" + serialized_form.length); assert serialized_form.length == size : "serialized length=" + serialized_form.length + ", size=" + size; } private static void _testSize(GossipData data) throws Exception { System.out.println("\ndata: " + data); long size=data.size(); byte[] serialized_form=Util.streamableToByteBuffer(data); System.out.println("size=" + size + ", serialized size=" + serialized_form.length); assert serialized_form.length == size : "serialized length=" + serialized_form.length + ", size=" + 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); Assert.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); Assert.assertEquals(serialized_form.length, size); } } libjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/tests/StreamableTest.java0000644000175000017500000001670311647260573032425 0ustar moellermoeller package org.jgroups.tests; import org.jgroups.*; import org.jgroups.conf.ClassConfigurator; import org.jgroups.protocols.*; import org.jgroups.util.UUID; import org.jgroups.util.Util; import org.testng.Assert; import org.testng.annotations.Test; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.util.Vector; @Test(groups=Global.FUNCTIONAL) public class StreamableTest { static final short PING_ID=100; static final short UDP_ID=101; public static void testStreamable() throws Exception { byte[] buf={'b', 'e', 'l', 'a', 'b', 'a', 'n'}; byte[] tmp; Message m1=new Message(null, null, buf, 0, 4); Message 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); Assert.assertEquals(4, m3.getLength()); Assert.assertEquals(4, m3.getRawBuffer().length); Assert.assertEquals(4, m3.getBuffer().length); Assert.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=new Message(); m4.readFrom(in); Assert.assertEquals(3, m4.getLength()); Assert.assertEquals(3, m4.getBuffer().length); Assert.assertEquals(3, m4.getRawBuffer().length); Assert.assertEquals(0, m4.getOffset()); } public static 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 static 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 static void testNullBuffer() throws Exception { Message msg=new Message(); stream(msg); } public static void testNonNullBuffer() throws Exception { Message msg=new Message(null, null, "Hello world".getBytes()); stream(msg); } public static void testNonNullAddress() throws Exception { stream(new Message(null, UUID.randomUUID(), "Hello world".getBytes())); } public static void testHeaders() throws Exception { Address dest=UUID.randomUUID(); Address src=UUID.randomUUID(); Message msg=new Message(dest, src, "Hello world".getBytes()); PingHeader hdr=new PingHeader(PingHeader.GET_MBRS_REQ, new PingData(src, Util.createView(src, 1, src), true)); msg.putHeader(PING_ID, hdr); TpHeader udp_hdr=new TpHeader("bla"); msg.putHeader(UDP_ID, udp_hdr); stream(msg); } public static void testAdditionalData() throws Exception { UUID dest=UUID.randomUUID(); dest.setAdditionalData("foo".getBytes()); UUID src=UUID.randomUUID(); src.setAdditionalData("foobar".getBytes()); Message msg=new Message(dest, src, "Hello world".getBytes()); PingHeader hdr=new PingHeader(PingHeader.GET_MBRS_REQ, new PingData(src, Util.createView(src, 1, src), false)); msg.putHeader(PING_ID, hdr); TpHeader udp_hdr=new TpHeader("bla"); msg.putHeader(UDP_ID, udp_hdr); stream(msg); } public static 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=UUID.randomUUID(); b=UUID.randomUUID(); c=UUID.randomUUID(); d=UUID.randomUUID(); e=UUID.randomUUID(); f=UUID.randomUUID(); 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(); assert sub.get(0) instanceof View; assert sub.get(1) instanceof MergeView; assert sub.get(2) instanceof View; assert sub.get(3) instanceof MergeView; assert sub.get(4) instanceof View; byte[] buf=Util.streamableToByteBuffer(view_all); assert buf != null; assert buf.length > 0; MergeView merge_view=(MergeView)Util.streamableFromByteBuffer(MergeView.class, buf); assert merge_view != null; System.out.println("MergeView: " + merge_view); sub=merge_view.getSubgroups(); assert sub.get(0) instanceof View; assert sub.get(1) instanceof MergeView; assert sub.get(2) instanceof View; assert sub.get(3) instanceof MergeView; assert sub.get(4) instanceof View; } private static void stream(Message msg) throws Exception { int length, bufLength; byte[] tmp; Message msg2; Address src, dest=msg.getDest(); 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); Assert.assertEquals(length, msg2.getLength()); Assert.assertEquals(bufLength, getBufLength(msg2)); assert match(dest, msg2.getDest()); assert match(src, msg2.getSrc()); Assert.assertEquals(num_headers, getNumHeaders(msg2)); } private static int getNumHeaders(Message msg) { return msg.getNumHeaders(); } private static 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 static int getBufLength(Message msg) { return msg.getBuffer() != null? msg.getBuffer().length : 0; } } libjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/tests/TimeSchedulerTest.java0000644000175000017500000005453411647260573033107 0ustar moellermoellerpackage org.jgroups.tests; import org.jgroups.Global; import org.jgroups.TimeoutException; import org.jgroups.stack.Interval; import org.jgroups.stack.StaticInterval; import org.jgroups.util.DefaultTimeScheduler; import org.jgroups.util.Promise; import org.jgroups.util.TimeScheduler; import org.jgroups.util.Util; import org.testng.Assert; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; /** * Test cases for TimeScheduler * @author Bela Ban */ @Test(groups=Global.TIME_SENSITIVE,dataProvider="createTimer",sequential=true) public class TimeSchedulerTest { 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 ? @DataProvider(name="createTimer") Object[][] createTimer() { return Util.createTimer(); } @Test(dataProvider="createTimer") public void testCancel(TimeScheduler timer) throws InterruptedException { for(int i=0; i < 10; i++) timer.scheduleWithDynamicInterval(new OneTimeTask(1000)); assert timer.size() == 10; timer.stop(); assert timer.size() == 0 : "stopped timer should have no tasks"; } /** * Tests creating many tasks at the same time and then cancelling every second task. Asserts that not all tasks are cancelled; * this was the case in an early implementation of {@link org.jgroups.util.TimeScheduler2}. * @param timer */ @Test(dataProvider="createTimer") public void testSchedulingTasksThenCancellingEverySecondTask(TimeScheduler timer) { final int NUM=20; Future[] futures=new Future[NUM]; try { for(int i=0; i < NUM; i++) { futures[i]=timer.schedule(new MyRunnable(i), 1000, TimeUnit.MILLISECONDS); if(i % 2 == 0) futures[i].cancel(false); } Util.sleep(3000); for(int i=0; i < NUM; i++) { Future future=futures[i]; System.out.println("[" + i + "] done=" + future.isDone() + ", cancelled=" + future.isCancelled()); } for(int i=0; i < NUM; i++) { Future future=futures[i]; assert future.isDone(); if(i % 2 == 0) assert future.isCancelled(); else assert !future.isCancelled(); } } finally { timer.stop(); } } @Test(dataProvider="createTimer") public void testTaskCancellationBeforeTaskHasRun(TimeScheduler timer) throws InterruptedException { Future future; StressTask task=new StressTask(); try { future=timer.scheduleWithDynamicInterval(task); assert timer.size() == 1; future.cancel(true); assert timer.size() == 1; Util.sleep(200); int num_executions=task.getNumExecutions(); System.out.println("number of task executions=" + num_executions); assert num_executions ==0 : "task should never have executed as it was cancelled before execution"; if(timer instanceof DefaultTimeScheduler) ((DefaultTimeScheduler)timer).purge(); // removes cancelled tasks else Util.sleep(1000); assert timer.size() == 0; } finally { timer.stop(); } } @Test(dataProvider="createTimer") public void testTaskCancellationAfterHasRun(TimeScheduler timer) throws InterruptedException { Future future; StressTask task=new StressTask(); try { future=timer.scheduleWithDynamicInterval(task); assert timer.size() == 1; Util.sleep(500); // wait until task has executed future.cancel(true); int size=timer.size(); assert size == 1 : " timer size should be 1, but is " + size; int num_executions=task.getNumExecutions(); System.out.println("number of task executions=" + num_executions); assert num_executions >= 1 : "task should have executed at least 1 time, as it was cancelled after 500ms"; if(timer instanceof DefaultTimeScheduler) ((DefaultTimeScheduler)timer).purge(); // removes cancelled tasks else Util.sleep(1000); assert timer.size() == 0 : " timer size should be 0, but is " + size; } finally { timer.stop(); } } @Test(dataProvider="createTimer") public void testRepeatingTask(TimeScheduler timer) throws InterruptedException { Future future; RepeatingTask task=new RepeatingTask(300); try { future=timer.scheduleAtFixedRate(task, 0, 300, TimeUnit.MILLISECONDS); Util.sleep(3200); System.out.println("<<< cancelling task"); future.cancel(true); Util.sleep(1000); int num=task.getNum(); System.out.println("task executed " + num + " times"); assert num >= 8 && num <= 11 : "number of executions is " + num + ", but should be >= 8 and <= 11\n" + "Execution times: " + printExecutionTimes(task); } finally { timer.stop(); } } private static String printExecutionTimes(RepeatingTask task) { StringBuilder sb=new StringBuilder(); List times=task.getExecutionTimes(); if(times.isEmpty()) return "[]"; int cnt=1; for(Long time: times) { sb.append("#" + cnt++ + ": ").append(time).append("\n"); } return sb.toString(); } @Test(dataProvider="createTimer") public void testStress(TimeScheduler timer) throws InterruptedException { StressTask t; final int NUM_A=500, NUM_B=1000; int cnt=0, print=NUM_A * NUM_B / 10; try { System.out.println("-- adding "+ (NUM_A * NUM_B) + " tasks..."); for(int i=0; i < NUM_A; i++) { for(int j=0; j < NUM_B; j++) { t=new StressTask(); Future future=timer.scheduleWithDynamicInterval(t); future.cancel(true); cnt++; if(cnt > 0 && cnt % print == 0) System.out.println(cnt); } } System.out.println("-- added "+ (NUM_A * NUM_B) + " tasks, waiting for termination"); Util.sleep(1000); for(int i=0; i < 10; i++) { int size=timer.size(); System.out.println(size); if(size == 0) break; Util.sleep(500); } assert timer.size() == 0; } finally { timer.stop(); } } @Test(dataProvider="createTimer") public void testDynamicTask(TimeScheduler timer) throws InterruptedException { TimeScheduler.Task task=new DynamicTask(); try { Future future=timer.scheduleWithDynamicInterval(task); assert !(future.isCancelled()); assert !(future.isDone()); Thread.sleep(3000); assert !(future.isCancelled()); assert !(future.isDone()); future.cancel(true); assert future.isCancelled(); assert future.isDone(); } finally { timer.stop(); } } @Test(dataProvider="createTimer") public void testDynamicTaskCancel(TimeScheduler timer) throws InterruptedException { try { TimeScheduler.Task task=new DynamicTask(); Future future=timer.scheduleWithDynamicInterval(task); assert !(future.isCancelled()); assert !(future.isDone()); Thread.sleep(3000); assert !(future.isCancelled()); assert !(future.isDone()); assert future.cancel(true); assert future.isCancelled(); assert future.isDone(); assert future.cancel(true) == false; } finally { timer.stop(); } } @Test(dataProvider="createTimer") public void testIsDone(TimeScheduler timer) throws InterruptedException { try { TimeScheduler.Task task=new DynamicTask(); Future future=timer.scheduleWithDynamicInterval(task); assert !(future.isCancelled()); assert !(future.isDone()); Thread.sleep(3000); assert !(future.isCancelled()); assert !(future.isDone()); future.cancel(true); assert future.isCancelled(); assert future.isDone(); } finally { timer.stop(); } } @Test(dataProvider="createTimer") public void testIsDone2(TimeScheduler timer) throws InterruptedException { try { TimeScheduler.Task task=new DynamicTask(new long[]{1000,2000,-1}); Future future=timer.scheduleWithDynamicInterval(task); assert !future.isCancelled(); assert !future.isDone(); Thread.sleep(3500); assert !future.isCancelled(); assert future.isDone(); assert !future.cancel(true); // cancel() fails because the task is done assert future.isCancelled(); assert future.isDone(); assert !future.cancel(true); } finally { timer.stop(); } } @Test(dataProvider="createTimer") public void testIsDone3(TimeScheduler timer) throws InterruptedException { try { TimeScheduler.Task task=new DynamicTask(new long[]{-1}); Future future=timer.scheduleWithDynamicInterval(task); Thread.sleep(100); assert !future.isCancelled(); assert future.isDone(); assert !future.cancel(true); assert future.isCancelled(); assert future.isDone(); } finally { timer.stop(); } } @Test(dataProvider="createTimer") public void testImmediateExecution(TimeScheduler timer) throws InterruptedException { try { Promise p=new Promise(); ImmediateTask task=new ImmediateTask(p); timer.execute(task); try { long start=System.currentTimeMillis(), stop; p.getResultWithTimeout(500); stop=System.currentTimeMillis(); System.out.println("task took " + (stop-start) + "ms"); } catch(TimeoutException e) { assert false : "ran into timeout - task should have executed immediately"; } } finally { timer.stop(); } } @Test(dataProvider="createTimer") public void test2Tasks(TimeScheduler timer) throws InterruptedException { int size; try { System.out.println(System.currentTimeMillis() + ": adding task"); timer.schedule(new MyTask(), 500, TimeUnit.MILLISECONDS); size=timer.size(); System.out.println("queue size=" + size); Assert.assertEquals(1, size); Thread.sleep(1000); size=timer.size(); System.out.println("queue size=" + size); Assert.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); Assert.assertEquals(3, size); Thread.sleep(1000); size=timer.size(); System.out.println("queue size=" + size); Assert.assertEquals(0, size); } finally { timer.stop(); } } /** * 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 */ @Test(dataProvider="createTimer") public void testRetransmits(TimeScheduler timer) throws InterruptedException { Entry entry; int num_non_correct_entries=0; Map msgs=new ConcurrentHashMap(); // keys=seqnos (Long), values=Entries try { // 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 all retransmits"); // 3. Check whether all Entries have correct retransmission times long end_time=System.currentTimeMillis() + 20000L, start=System.currentTimeMillis(); while(System.currentTimeMillis() < end_time) { num_non_correct_entries=check(msgs, false); if(num_non_correct_entries == 0) break; Util.sleep(1000); } System.out.println("-- waited for " + (System.currentTimeMillis() - start) + " ms"); num_non_correct_entries=check(msgs, true); if(num_non_correct_entries > 0) System.err.println("Number of incorrect retransmission timeouts: " + num_non_correct_entries); assert num_non_correct_entries == 0: "expected 0 incorrect entries but got " + num_non_correct_entries; } finally { timer.stop(); } } /** * Tests adding a task and - before it executes - adding tasks which are to be executed sooner * @param timer */ @Test(dataProvider="createTimer") public void testTasksPreemptingEachOther(TimeScheduler timer) { final List results=new ArrayList(3); long execution_time=4000; final long base=System.currentTimeMillis(); for(int num: new Integer[]{1,2,3}) { final int cnt=num; timer.schedule(new Runnable() { public void run() { results.add(cnt); System.out.println("[" + (System.currentTimeMillis() - base) + "] " + cnt); } }, execution_time, TimeUnit.MILLISECONDS); execution_time-=1300; Util.sleep(300); } for(int i=0; i < 10; i++) { if(results.size() == 3) break; Util.sleep(500); } System.out.println("results = " + results); assert results.size() == 3: "results = " + results; assert results.get(0) == 3: "results = " + results; assert results.get(1) == 2: "results = " + results; assert results.get(2) == 1: "results = " + results; } /** * Tests the initial-delay argument of * {@link TimeScheduler#scheduleWithFixedDelay(Runnable, long, long, java.util.concurrent.TimeUnit)} */ @Test(dataProvider="createTimer") public void testSchedulerWithFixedDelay(TimeScheduler timer) { final AtomicBoolean set=new AtomicBoolean(false); Future future=timer.scheduleWithFixedDelay(new Runnable() { public void run() { set.set(true); } }, 0, 3000, TimeUnit.MILLISECONDS); Util.sleep(500); future.cancel(true); System.out.println("variable was set: " + set); assert set.get(); future=timer.scheduleWithFixedDelay(new Runnable() { public void run() { set.set(true); } }, 300, 3000, TimeUnit.MILLISECONDS); Util.sleep(1000); future.cancel(true); System.out.println("variable was set: " + set); assert set.get(); } static int check(Map msgs, boolean print) { int retval=0; for(long i=0; i < NUM_MSGS; i++) { Entry entry=msgs.get(new Long(i)); if(!entry.isCorrect(print)) { retval++; } } return retval; } static private class ImmediateTask implements Runnable { Promise p; public ImmediateTask(Promise p) { this.p=p; } public void run() { p.setResult(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; List execution_times=new LinkedList(); private long base=0; RepeatingTask(long timeout) { super(timeout); } public int getNum() { return num; } public List getExecutionTimes() { return execution_times; } public void run() { if(base == 0) base=System.currentTimeMillis(); long time=System.currentTimeMillis() - base; System.out.println((num +1) + ": this is a repeating task (" + time + "ms after start)"); execution_times.add(time); num++; } } static class StressTask implements TimeScheduler.Task { int num_executions=0; public int getNumExecutions() { return num_executions; } public long nextInterval() { return 50; } public void run() { num_executions++; } } static class MyRunnable implements Runnable { final int num; public MyRunnable(int num) { this.num=num; } public void run() { System.out.println("[" + num + "] at " + System.currentTimeMillis()); } } 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 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(boolean print) { long t; long expected; long diff, delta; t=first_xmit - start_time; expected=xmit_timeouts[0]; diff=Math.abs(expected - t); delta=(long)(expected * PERCENTAGE_OFF); if(diff <= delta) return true; t=second_xmit - first_xmit; expected=xmit_timeouts[1]; diff=Math.abs(expected - t); delta=(long)(expected * PERCENTAGE_OFF); if(diff <= delta) return true; t=third_xmit - second_xmit; expected=xmit_timeouts[2]; diff=Math.abs(expected - t); delta=(long)(expected * PERCENTAGE_OFF); if(diff <= delta) return true; t=fourth_xmit - third_xmit; expected=xmit_timeouts[3]; diff=Math.abs(expected - t); delta=(long)(expected * PERCENTAGE_OFF); if(diff <= delta) return true; if(print) { System.err.println("#" + seqno + ": " + this + ": (" + "entry is more than " + PERCENTAGE_OFF + " percentage off "); } return false; } 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(); } } } libjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/tests/TupleTest.java0000644000175000017500000000267111647260573031436 0ustar moellermoellerpackage org.jgroups.tests; import org.jgroups.util.Tuple; import org.jgroups.Global; import org.testng.Assert; import org.testng.annotations.Test; import java.util.HashMap; import java.util.Map; /** * @author Bela Ban */ @Test(groups=Global.FUNCTIONAL) public class TupleTest { public static void testCreation() { Tuple tuple=new Tuple("Bela", 322649); System.out.println("tuple: " + tuple); Assert.assertEquals("Bela", tuple.getVal1()); Assert.assertEquals(322649, tuple.getVal2().intValue()); } public static void testSet() { Tuple tuple=new Tuple("Bela", 322649); System.out.println("tuple: " + tuple); tuple.setVal1("Michelle"); tuple.setVal2(7); Assert.assertEquals("Michelle", tuple.getVal1()); Assert.assertEquals(7, tuple.getVal2().intValue()); } public static 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); Assert.assertEquals("one", map.get(1).getVal1()); Assert.assertEquals(1, map.get(1).getVal2().intValue()); Assert.assertEquals("two", map.get(2).getVal1()); Assert.assertEquals(2, map.get(2).getVal2().intValue()); } } ././@LongLink0000000000000000000000000000015100000000000011562 Lustar rootrootlibjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/tests/UNICAST2_ConnectionTests.javalibjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/tests/UNICAST2_ConnectionTests.0000644000175000017500000001462611647260573033240 0ustar moellermoellerpackage org.jgroups.tests; import org.jgroups.*; import org.jgroups.protocols.UNICAST2; import org.jgroups.stack.Protocol; import org.jgroups.stack.ProtocolStack; import org.jgroups.util.Util; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.ArrayList; import java.util.List; /** * Tests unilateral closings of UNICAST2 connections. The test scenarios are described in doc/design.UNICAST.new.txt. * @author Bela Ban */ @Test(groups=Global.FUNCTIONAL,sequential=true) public class UNICAST2_ConnectionTests { private JChannel a, b; private Address a_addr, b_addr; private MyReceiver r1, r2; private UNICAST2 u1, u2; private static final String props="SHARED_LOOPBACK:UNICAST2(stable_interval=2000)"; private static final String CLUSTER="UNICAST2_ConnectionTests"; @BeforeMethod void start() throws Exception { r1=new MyReceiver("A"); r2=new MyReceiver("B"); a=new JChannel(props); a.connect(CLUSTER); a_addr=a.getAddress(); a.setReceiver(r1); u1=(UNICAST2)a.getProtocolStack().findProtocol(UNICAST2.class); b=new JChannel(props); b.connect(CLUSTER); b_addr=b.getAddress(); b.setReceiver(r2); u2=(UNICAST2)b.getProtocolStack().findProtocol(UNICAST2.class); System.out.println("A=" + a_addr + ", B=" + b_addr); } @AfterMethod void stop() {Util.close(b, a);} /** * 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, UNICAST2.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)); assert l1.size() == num; assert l2.size() == num; } 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(); assert size == num : "list has " + size + " elements"; } 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); } } }././@LongLink0000000000000000000000000000015000000000000011561 Lustar rootrootlibjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/tests/UNICAST_ConnectionTests.javalibjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/tests/UNICAST_ConnectionTests.j0000644000175000017500000001457611647260573033334 0ustar moellermoellerpackage 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 org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.List; import java.util.ArrayList; /** * Tests unilateral closings of UNICAST connections. The test scenarios are described in doc/design.UNICAST.new.txt. * @author Bela Ban */ @Test(groups=Global.FUNCTIONAL,sequential=true) public class UNICAST_ConnectionTests { 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"; @BeforeMethod void start() throws Exception { r1=new MyReceiver("A"); r2=new MyReceiver("B"); a=new JChannel(props); a.connect(CLUSTER); a_addr=a.getAddress(); a.setReceiver(r1); u1=(UNICAST)a.getProtocolStack().findProtocol(UNICAST.class); b=new JChannel(props); b.connect(CLUSTER); b_addr=b.getAddress(); b.setReceiver(r2); u2=(UNICAST)b.getProtocolStack().findProtocol(UNICAST.class); System.out.println("A=" + a_addr + ", B=" + b_addr); } @AfterMethod void stop() {Util.close(b, a);} /** * 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)); assert l1.size() == num; assert l2.size() == num; } 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(); assert size == num : "list has " + size + " elements"; } 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); } } } libjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/tests/UNICAST_Test.java0000644000175000017500000001202211647260573031601 0ustar moellermoeller package org.jgroups.tests; import org.jgroups.*; import org.jgroups.debug.Simulator; import org.jgroups.protocols.DISCARD; import org.jgroups.protocols.UNICAST; import org.jgroups.protocols.UNICAST2; import org.jgroups.stack.Protocol; import org.jgroups.util.Util; import org.testng.Assert; import org.testng.annotations.AfterMethod; import org.testng.annotations.Test; import org.testng.annotations.DataProvider; import java.nio.ByteBuffer; import java.util.Vector; /** * Tests the UNICAST protocol * @author Bela Ban * @version $id$ */ @Test(groups=Global.FUNCTIONAL,sequential=true) public class UNICAST_Test { Address a1, a2; Vector
                members; View v; Simulator simulator; static final int SIZE=1000; // bytes static final int NUM_MSGS=10000; @AfterMethod void tearDown() throws Exception { if(simulator != null) simulator.stop(); } @Test(dataProvider="configProvider") public void testReceptionOfAllMessages(Protocol prot) throws Throwable { System.out.println("prot=" + prot.getClass().getSimpleName()); Protocol[] stack=new Protocol[]{prot}; createStack(stack); _testReceptionOfAllMessages(); } @Test(dataProvider="configProvider") public void testReceptionOfAllMessagesWithDISCARD(Protocol prot) throws Throwable { System.out.println("prot=" + prot.getClass().getSimpleName()); DISCARD discard=new DISCARD(); discard.setDownDiscardRate(0.1); // discard all down message with 10% probability Protocol[] stack=new Protocol[]{prot, discard}; createStack(stack); _testReceptionOfAllMessages(); } @DataProvider public static Object[][] configProvider() { Object[][] retval=new Object[][] { {new UNICAST()}, {new UNICAST2()} }; ((UNICAST)retval[0][0]).setTimeout(new long[]{500,1000,2000,3000}); ((UNICAST2)retval[1][0]).setTimeout(new long[]{500,1000,2000,3000}); return retval; } 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 */ static 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; final 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); Assert.assertEquals(num_received, NUM_MSGS); } private void createStack(Protocol[] stack) throws Exception { a1=Util.createRandomAddress(); 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()); } } ././@LongLink0000000000000000000000000000014700000000000011567 Lustar rootrootlibjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/tests/UnmodifiableVectorTest.javalibjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/tests/UnmodifiableVectorTest.ja0000644000175000017500000000407111647260573033573 0ustar moellermoellerpackage org.jgroups.tests; import org.jgroups.Global; import org.jgroups.util.UnmodifiableVector; import org.testng.Assert; import org.testng.annotations.Test; import java.util.Arrays; import java.util.Iterator; import java.util.ListIterator; import java.util.Vector; @Test(groups=Global.FUNCTIONAL) public class UnmodifiableVectorTest { public static void testCreation() { Vector v=new Vector(Arrays.asList("one", "two")); UnmodifiableVector uv=new UnmodifiableVector(v); Assert.assertEquals(v.size(), uv.size()); assert uv.contains("two"); } @Test(expectedExceptions=UnsupportedOperationException.class) public static void testAddition() { Vector v=new Vector(Arrays.asList("one", "two")); UnmodifiableVector uv=new UnmodifiableVector(v); uv.add("three"); } @Test(expectedExceptions=UnsupportedOperationException.class) public static void testRemoval() { Vector v=new Vector(Arrays.asList("one", "two")); UnmodifiableVector uv=new UnmodifiableVector(v); uv.add("two"); } @Test(expectedExceptions=UnsupportedOperationException.class) public static void testIteration() { Vector v=new Vector(Arrays.asList("one", "two")); UnmodifiableVector uv=new UnmodifiableVector(v); 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(); it.remove(); } } @Test(expectedExceptions=UnsupportedOperationException.class) public static void testListIteration() { Vector v=new Vector(Arrays.asList("one", "two")); UnmodifiableVector uv=new UnmodifiableVector(v); 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(); it.remove(); } } } libjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/tests/UtilTest.java0000644000175000017500000010342011647260573031254 0ustar moellermoeller package org.jgroups.tests; import org.jgroups.*; import org.jgroups.util.Buffer; import org.jgroups.util.Util; import org.jgroups.util.UUID; import org.testng.Assert; import org.testng.annotations.Test; import java.io.*; import java.util.*; @Test(groups=Global.FUNCTIONAL) public class UtilTest { public static 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"); Assert.assertEquals("Michelle", retval); props.setProperty("name", "Bela"); props.setProperty("key", "val"); retval=Util.getProperty(new String[]{"name2", "name"}, props, "name", false, "Jeannette"); Assert.assertEquals("Nicole", retval); props.setProperty("name", "Bela"); props.setProperty("key", "val"); retval=Util.getProperty(new String[]{"name3", "name"}, props, "name", false, "Jeannette"); Assert.assertEquals("Michelle", retval); props.setProperty("name", "Bela"); props.setProperty("key", "val"); retval=Util.getProperty(new String[]{"name3", "name4"}, props, "name", false, "Jeannette"); Assert.assertEquals("Bela", retval); props.setProperty("name", "Bela"); props.setProperty("key", "val"); retval=Util.getProperty(new String[]{"name2", "name"}, props, "name", true, "Jeannette"); Assert.assertEquals("Bela", retval); props.setProperty("name", "Bela"); props.setProperty("key", "val"); retval=Util.getProperty(new String[]{"name2", "name"}, props, "name2", true, "Jeannette"); Assert.assertEquals("Jeannette", retval); props.setProperty("name", "Bela"); props.setProperty("key", "val"); retval=Util.getProperty(new String[]{"name2", "name"}, props, "name2", true, null); assert retval == null; props.setProperty("name", "Bela"); props.setProperty("key", "val"); } public static void testGetProperty2() { String input="foo, bar, foobar: 1000"; String result=Util.getProperty(input); assert result != null && result.equals("1000"); input="foo, bar, foobar"; result=Util.getProperty(input); assert result == null; System.setProperty("foobar", "900"); input="foo, bar, foobar: 1000"; result=Util.getProperty(input); assert result != null && result.equals("900"); input="foo, bar, foobar"; result=Util.getProperty(input); assert result != null && result.equals("900"); System.setProperty("bar", "500"); input="foo, bar, foobar: 1000"; result=Util.getProperty(input); assert result != null && result.equals("500"); input="foo, bar, foobar"; result=Util.getProperty(input); assert result != null && result.equals("500"); System.setProperty("foo", "200"); input="foo, bar, foobar: 1000"; result=Util.getProperty(input); assert result != null && result.equals("200"); input="foo, bar, foobar"; result=Util.getProperty(input); assert result != null && result.equals("200"); } public static void testFlags() { final byte ONE = 1; final byte FIVE = 16; final byte SEVEN = 64; byte flags=0; flags=Util.setFlag(flags, ONE); assert Util.isFlagSet(flags, ONE); flags=0; flags=Util.setFlag(flags, (byte)(ONE | SEVEN)); assert Util.isFlagSet(flags, ONE); assert Util.isFlagSet(flags, SEVEN); assert !Util.isFlagSet(flags, FIVE); flags=Util.clearFlags(flags, ONE); assert !Util.isFlagSet(flags, ONE); flags=Util.setFlag(flags, FIVE); assert Util.isFlagSet(flags, FIVE); assert Util.isFlagSet(flags, SEVEN); } public static void testIgnoreBindAddress() { boolean retval; retval=Util.isBindAddressPropertyIgnored(); assert !(retval); System.setProperty(Global.IGNORE_BIND_ADDRESS_PROPERTY, "true"); retval=Util.isBindAddressPropertyIgnored(); assert retval; System.setProperty(Global.IGNORE_BIND_ADDRESS_PROPERTY, "true2"); retval=Util.isBindAddressPropertyIgnored(); assert !(retval); System.setProperty(Global.IGNORE_BIND_ADDRESS_PROPERTY, "false"); retval=Util.isBindAddressPropertyIgnored(); assert !(retval); System.getProperties().remove(Global.IGNORE_BIND_ADDRESS_PROPERTY); System.setProperty(Global.IGNORE_BIND_ADDRESS_PROPERTY_OLD, "false"); retval=Util.isBindAddressPropertyIgnored(); assert !(retval); System.getProperties().remove(Global.IGNORE_BIND_ADDRESS_PROPERTY); System.setProperty(Global.IGNORE_BIND_ADDRESS_PROPERTY_OLD, "true"); retval=Util.isBindAddressPropertyIgnored(); assert retval; System.setProperty(Global.IGNORE_BIND_ADDRESS_PROPERTY, "true"); System.setProperty(Global.IGNORE_BIND_ADDRESS_PROPERTY_OLD, "true"); retval=Util.isBindAddressPropertyIgnored(); assert retval; } public static void testPrintBytes() { long num; String s; num=1; s=Util.printBytes(num); System.out.println(num + " is " + s); Assert.assertEquals("1b", s); num=999; s=Util.printBytes(num); System.out.println(num + " is " + s); Assert.assertEquals("999b", s); num=1000; s=Util.printBytes(num); System.out.println(num + " is " + s); Assert.assertEquals("1KB", s); num=1001; s=Util.printBytes(num); System.out.println(num + " is " + s); Assert.assertEquals("1KB", s); num=1010; s=Util.printBytes(num); System.out.println(num + " is " + s); Assert.assertEquals("1.01KB", s); num=1543; s=Util.printBytes(num); System.out.println(num + " is " + s); Assert.assertEquals("1.54KB", s); num=10000; s=Util.printBytes(num); System.out.println(num + " is " + s); Assert.assertEquals("10KB", s); num=150000; s=Util.printBytes(num); System.out.println(num + " is " + s); Assert.assertEquals("150KB", s); num=150023; s=Util.printBytes(num); System.out.println(num + " is " + s); Assert.assertEquals("150.02KB", s); num=1200000; s=Util.printBytes(num); System.out.println(num + " is " + s); Assert.assertEquals("1.2MB", s); num=150000000; s=Util.printBytes(num); System.out.println(num + " is " + s); Assert.assertEquals("150MB", s); num=150030000; s=Util.printBytes(num); System.out.println(num + " is " + s); Assert.assertEquals("150.03MB", s); num=1200000000; s=Util.printBytes(num); System.out.println(num + " is " + s); Assert.assertEquals("1.2GB", s); } public static void testReadBytes() { assert 10 == Util.readBytesInteger("10"); assert 10 == Util.readBytesInteger("10 "); assert 10 == Util.readBytesInteger(" 10"); assert 1000 == Util.readBytesInteger("1000"); assert 1000 == Util.readBytesInteger("1kb"); assert 1000 == Util.readBytesInteger("1 kb"); assert 1000 == Util.readBytesInteger("1k"); assert 1000 == Util.readBytesInteger("1KB"); assert 1000 == Util.readBytesInteger("1 K"); assert 1000 == Util.readBytesInteger("1K"); assert 1234 == Util.readBytesInteger("1.234K"); long M=1000*1000; assert M == Util.readBytesLong("1M"); assert M == Util.readBytesLong("1 M"); assert M == Util.readBytesLong("1MB"); assert M == Util.readBytesLong("1 mb"); assert M == Util.readBytesLong("1m"); assert M == Util.readBytesLong("1 m"); M=(long)(25.5 * 1000*1000); assert M == Util.readBytesLong("25.5M"); assert M == Util.readBytesLong("25.5m"); assert M == Util.readBytesLong("25.5 MB"); assert M == Util.readBytesLong("25.5 mB"); assert M == Util.readBytesLong("25.5 m"); assert M == Util.readBytesLong("25500K"); M=(long)(1.5 * 1000 * 1000 * 1000); assert M == Util.readBytesLong("1.5GB"); assert M == Util.readBytesLong("1.5gb"); assert M == Util.readBytesLong("1.5g"); assert M == Util.readBytesLong("1.5G"); assert M == Util.readBytesLong("1500m"); assert M == Util.readBytesLong("1500000K"); assert M == Util.readBytesLong("1.5 gb"); double D=3.123456789; assert D == Util.readBytesDouble("3.123456789"); assert D * 10 == Util.readBytesDouble("31.23456789"); assert D * 100 == Util.readBytesDouble("312.3456789"); assert D * 1000 == Util.readBytesDouble("3123.456789"); assert D * 1000 == Util.readBytesDouble("3.123456789K"); assert D * 1000000 == Util.readBytesDouble("3.123456789M"); assert D * 1000000 == Util.readBytesDouble("3123456.789"); } @SuppressWarnings("unchecked") public static void testObjectToFromByteBuffer() throws Exception { byte[] buf; Address addr=Util.createRandomAddress(), addr2; List list=new ArrayList(), list2; list.add("Bela"); list.add("Jeannette"); buf=Util.objectToByteBuffer(addr); addr2=(Address)Util.objectFromByteBuffer(buf); System.out.println("addr=" + addr + ", addr2=" + addr2); Assert.assertEquals(addr, addr2); buf=Util.objectToByteBuffer(list); list2=(List)Util.objectFromByteBuffer(buf); System.out.println("list=" + list + ", list2=" + list2); Assert.assertEquals(list, list2); byte[] buffer=new byte[]{'B', 'e', 'l', 'a', ' ', 'B', 'a', 'n'}; buf=Util.objectToByteBuffer(buffer); byte[] buffer2=(byte[])Util.objectFromByteBuffer(buf); assert buffer2 != null && buffer.length == buffer2.length; assert Arrays.equals(buffer, buffer2); Object obj=null; buf=Util.objectToByteBuffer(obj); assert buf != null; assert buf.length > 0; obj=Util.objectFromByteBuffer(buf); assert obj == null; 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 static void testMessageToByteBuffer() throws Exception { _testMessage(new Message()); _testMessage(new Message(null, null, "hello world")); _testMessage(new Message(null, Util.createRandomAddress(), null)); _testMessage(new Message(null, Util.createRandomAddress(), null)); _testMessage(new Message(null, Util.createRandomAddress(), "bela")); } private static void _testMessage(Message msg) throws Exception { Buffer buf=Util.messageToByteBuffer(msg); Message msg2=Util.byteBufferToMessage(buf.getBuf(), buf.getOffset(), buf.getLength()); Assert.assertEquals(msg.getSrc(), msg2.getSrc()); Assert.assertEquals(msg.getDest(), msg2.getDest()); Assert.assertEquals(msg.getLength(), msg2.getLength()); } public static void testObjectToByteArrayWithLargeString() throws Exception { marshalString(Short.MAX_VALUE ); } public static void testObjectToByteArrayWithLargeString2() throws Exception { marshalString(Short.MAX_VALUE - 100); } public static void testObjectToByteArrayWithLargeString3() throws Exception { marshalString(Short.MAX_VALUE + 1); } public static void testObjectToByteArrayWithLargeString4() throws Exception { marshalString(Short.MAX_VALUE + 100); } public static 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); assert buf != null; assert buf.length > 0; Object obj2=Util.objectFromByteBuffer(buf); System.out.println("obj=" + obj + ", obj2=" + obj2 + " (type=" + obj.getClass().getName() + ", length=" + buf.length + " bytes)"); Assert.assertEquals(obj, obj2); } public static void testWriteStreamable() throws IOException, IllegalAccessException, InstantiationException { Message m=new Message(null, null, "Hello"); ViewId vid=new ViewId(null, 12345); ViewId vid2=new ViewId(Util.createRandomAddress(), 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); assert m2.getBuffer() != null; Assert.assertEquals(m.getLength(), m2.getLength()); assert v3 != null; Assert.assertEquals(vid, v3); assert v4 != null; Assert.assertEquals(vid2, v4); } public static 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); Assert.assertEquals(vid, v4); } public static void testWriteView() throws IOException, IllegalAccessException, InstantiationException { ViewId vid=new ViewId(null, 12345); Vector
                members=new Vector
                (); View v; Address a1=Util.createRandomAddress(); Address a2=Util.createRandomAddress(); Address a4=Util.createRandomAddress(); 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); Assert.assertEquals(v, v2); v2=(View)Util.readStreamable(View.class, dis); Assert.assertEquals(v, v2); } public static 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); Assert.assertEquals(s1, s3); Assert.assertEquals(s2, s4); } public static void writeAddress() throws IOException, IllegalAccessException, InstantiationException { Address a1=Util.createRandomAddress(); Address a2=Util.createRandomAddress(); Address a4=Util.createRandomAddress(); ByteArrayOutputStream outstream=new ByteArrayOutputStream(); DataOutputStream dos=new DataOutputStream(outstream); Util.writeAddress(a1, dos); Util.writeAddress(a2, dos); Util.writeAddress(a4, dos); dos.close(); byte[] buf=outstream.toByteArray(); ByteArrayInputStream instream=new ByteArrayInputStream(buf); DataInputStream dis=new DataInputStream(instream); Assert.assertEquals(a1, Util.readAddress(dis)); Assert.assertEquals(a2, Util.readAddress(dis)); Assert.assertEquals(a4, Util.readAddress(dis)); } public static void writeNullAddress() throws IOException, IllegalAccessException, InstantiationException { Address 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); assert Util.readAddress(dis) == null; } public static 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); assert buf2 != null; Assert.assertEquals(buf.length, buf2.length); } public static 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}; assert Util.match(a,a); assert !(Util.match(a, b)); assert !(Util.match(a, c)); assert !(Util.match(a, d)); assert Util.match(a,e); assert Util.match(c,c); assert !(Util.match(c, a)); } public static void testLeftMembers() { final Address a=Util.createRandomAddress(), b=Util.createRandomAddress(), c=Util.createRandomAddress(), d=Util.createRandomAddress(); Vector
                v1=new Vector
                (); v1.add(a); v1.add(b); v1.add(c); v1.add(d); Vector
                v2=new Vector
                (); v2.add(c); v2.add(d); View one=new View(new ViewId(a, 1), v1), two=new View(new ViewId(b,2), v2); List
                left=Util.leftMembers(one, two); System.out.println("left = " + left); assert left != null; assert left.size() == 2; assert left.contains(a); assert left.contains(b); } public static void testLeftMembers2() { final Address a=Util.createRandomAddress(), b=Util.createRandomAddress(), c=Util.createRandomAddress(), d=Util.createRandomAddress(); Vector
                v1=new Vector
                (); v1.add(a); v1.add(b); v1.add(c); v1.add(d); Vector
                v2=new Vector
                (); v2.add(c); v2.add(d); v2.add(a); v2.add(b); View one=new View(new ViewId(a, 1), v1), two=new View(new ViewId(b,2), v2); List
                left=Util.leftMembers(one, two); System.out.println("left = " + left); assert left != null; assert left.isEmpty(); } public static void testNewMembers() { final Address a=Util.createRandomAddress(), b=Util.createRandomAddress(), c=Util.createRandomAddress(), d=Util.createRandomAddress(), e=Util.createRandomAddress(); List
                old=new ArrayList
                (); List
                new_list=new ArrayList
                (); old.add(a); old.add(b); old.add(c); new_list.add(b); new_list.add(a); new_list.add(c); new_list.add(d); new_list.add(e); System.out.println("old: " + old); System.out.println("new: " + new_list); List
                new_nodes=Util.newMembers(old, new_list); System.out.println("new_nodes = " + new_nodes); assert new_nodes.size() == 2 : "list should have d and e"; assert new_nodes.contains(d); assert new_nodes.contains(e); } public static 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); assert el.intValue() >= 0 && el.intValue() < 10; } } public static void testPickNext() { List list=new ArrayList(10); for(int i=0; i < 10; i++) list.add(i); Integer num=Util.pickNext(list, 5); System.out.println("number next to 5: " + num); assert num != null; assert num.equals(6); num=Util.pickNext(list, 9); System.out.println("number next to 9: " + num); assert num != null; assert num.equals(0); num=Util.pickNext(list, 11); assert num == null; } public static void testPickNextN() { List list=Arrays.asList(1,2,3,4); List result=Util.pickNext(list, 0, 1); assert result.isEmpty(); result=Util.pickNext(list, 1, 1); System.out.println("result = " + result); assert result.size() == 1; assert result.contains(2); result=Util.pickNext(list, 3, 2); System.out.println("result = " + result); assert result.size() == 2; assert result.contains(4) && result.contains(1); result=Util.pickNext(list, 4, 5); System.out.println("result = " + result); assert result.size() == 3; assert result.contains(1) && result.contains(2) && result.contains(3); } public static void testAll() { List l=new ArrayList(); l.add("one"); l.add("two"); l.add("one"); System.out.println("-- list is " + l); assert !(Util.all(l, "one")); l.remove("two"); System.out.println("-- list is " + l); assert Util.all(l, "one"); } public static 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); Assert.assertEquals(13, list.size()); Assert.assertEquals("1", list.get(0)); Assert.assertEquals("13", list.get(list.size() - 1)); } public static void testParseSemicolonDelimitedString() { String input="one;two ; three; four ; five;six"; List list=Util.parseStringList(input, ";"); System.out.println("list: " + list); Assert.assertEquals(6, list.size()); Assert.assertEquals("one", list.get(0)); Assert.assertEquals("six", list.get(list.size() - 1)); } public static void testParseSemicolonDelimitedString2() { String input=" myID1::subID1 ; myID2::mySubID2; myID3 ;myID4::blaSubID4"; List list=Util.parseStringList(input, ";"); System.out.println("list: " + list); Assert.assertEquals(4, list.size()); Assert.assertEquals("myID1::subID1", list.get(0)); Assert.assertEquals("myID4::blaSubID4", list.get(list.size() - 1)); } public static void testReadLine() throws IOException { final String input=" hello world\nthis is \r\n just an example\r\nthis is line 2 \r\n"; String line; InputStream in=new BufferedInputStream(new ByteArrayInputStream(input.getBytes())); List list=new ArrayList(4); for(int i=0; i < 4; i++) { line=Util.readLine(in); System.out.println("line = \"" + line + "\""); list.add(line); } assert list.size() == 4; } public static void testVariableSubstitution() { String val="hello world", replacement; replacement=Util.substituteVariable(val); Assert.assertEquals(val, replacement); val="my name is ${user.name}"; replacement=Util.substituteVariable(val); Assert.assertNotSame(val, replacement); assert !(val.equals(replacement)); val="my name is ${user.name} and ${user.name}"; replacement=Util.substituteVariable(val); assert !(val.equals(replacement)); Assert.assertEquals(-1, replacement.indexOf("${")); val="my name is ${unknown.var:Bela Ban}"; replacement=Util.substituteVariable(val); assert replacement.contains("Bela Ban"); Assert.assertEquals(-1, replacement.indexOf("${")); val="my name is ${unknown.var}"; replacement=Util.substituteVariable(val); assert replacement.contains("${"); val="here is an invalid ${argument because it doesn't contains a closing bracket"; try { replacement=Util.substituteVariable(val); assert false : "should be an IllegalArgumentException"; } catch(Throwable t) { Assert.assertEquals(IllegalArgumentException.class, t.getClass()); } } public static void testDetermineMergeParticipantsAndMergeCoords() { Address a=Util.createRandomAddress(), b=Util.createRandomAddress(), c=Util.createRandomAddress(); org.jgroups.util.UUID.add((UUID)a, "A"); org.jgroups.util.UUID.add((UUID)b, "B"); org.jgroups.util.UUID.add((UUID)c, "C"); View v1=Util.createView(b, 1, b, a, c); View v2=Util.createView(b, 2, b, c); View v3=Util.createView(b, 2, b, c); Map map=new HashMap(); map.put(a, v1); map.put(b, v2); map.put(c, v3); StringBuilder sb=new StringBuilder("map:\n"); for(Map.Entry entry: map.entrySet()) sb.append(entry.getKey() + ": " + entry.getValue() + "\n"); System.out.println(sb); Collection
                merge_participants=Util.determineMergeParticipants(map); System.out.println("merge_participants = " + merge_participants); assert merge_participants.size() == 2; assert merge_participants.contains(a) && merge_participants.contains(b); Collection
                merge_coords=Util.determineMergeCoords(map); System.out.println("merge_coords = " + merge_coords); assert merge_coords.size() == 1; assert merge_coords.contains(b); } public static void testDetermineMergeParticipantsAndMergeCoords2() { Address a=Util.createRandomAddress(), b=Util.createRandomAddress(), c=Util.createRandomAddress(), d=Util.createRandomAddress(); org.jgroups.util.UUID.add((UUID)a, "A"); org.jgroups.util.UUID.add((UUID)b, "B"); org.jgroups.util.UUID.add((UUID)c, "C"); org.jgroups.util.UUID.add((UUID)d, "D"); View v1=Util.createView(a, 1, a, b); View v2=Util.createView(a, 1, a, b); View v3=Util.createView(c, 2, c, d); View v4=Util.createView(c, 2, c, d); Map map=new HashMap(); map.put(a, v1); map.put(b, v2); map.put(c, v3); map.put(d, v4); StringBuilder sb=new StringBuilder("map:\n"); for(Map.Entry entry: map.entrySet()) sb.append(entry.getKey() + ": " + entry.getValue() + "\n"); System.out.println(sb); Collection
                merge_participants=Util.determineMergeParticipants(map); System.out.println("merge_participants = " + merge_participants); assert merge_participants.size() == 2; assert merge_participants.contains(a) && merge_participants.contains(c); Collection
                merge_coords=Util.determineMergeCoords(map); System.out.println("merge_coords = " + merge_coords); assert merge_coords.size() == 2; assert merge_coords.contains(a) && merge_coords.contains(c); } public static void testDetermineMergeParticipantsAndMergeCoords3() { Address a=Util.createRandomAddress(), b=Util.createRandomAddress(), c=Util.createRandomAddress(), d=Util.createRandomAddress(); org.jgroups.util.UUID.add((UUID)a, "A"); org.jgroups.util.UUID.add((UUID)b, "B"); org.jgroups.util.UUID.add((UUID)c, "C"); org.jgroups.util.UUID.add((UUID)d, "D"); View v1=Util.createView(a, 1, a, b, c, d); View v2=Util.createView(a, 1, a, b, c, d); View v3=Util.createView(a, 2, a, b, c, d); View v4=Util.createView(a, 3, a, b, c, d); Map map=new HashMap(); map.put(a, v1); map.put(b, v2); map.put(c, v3); map.put(d, v4); StringBuilder sb=new StringBuilder("map:\n"); for(Map.Entry entry: map.entrySet()) sb.append(entry.getKey() + ": " + entry.getValue() + "\n"); System.out.println(sb); Collection
                merge_participants=Util.determineMergeParticipants(map); System.out.println("merge_participants = " + merge_participants); assert merge_participants.size() == 1; assert merge_participants.contains(a); Collection
                merge_coords=Util.determineMergeCoords(map); System.out.println("merge_coords = " + merge_coords); assert merge_coords.size() == 1; assert merge_coords.contains(a); } public static void testDetermineMergeParticipantsAndMergeCoords4() { Address a=Util.createRandomAddress(), b=Util.createRandomAddress(), c=Util.createRandomAddress(), d=Util.createRandomAddress(); org.jgroups.util.UUID.add((UUID)a, "A"); org.jgroups.util.UUID.add((UUID)b, "B"); org.jgroups.util.UUID.add((UUID)c, "C"); org.jgroups.util.UUID.add((UUID)d, "D"); View v1=Util.createView(a, 1, a, b); View v2=Util.createView(c, 1, c, d); Map map=new HashMap(); map.put(a, v1); map.put(b, v1); map.put(d, v2); StringBuilder sb=new StringBuilder("map:\n"); for(Map.Entry entry: map.entrySet()) sb.append(entry.getKey() + ": " + entry.getValue() + "\n"); System.out.println(sb); Collection
                merge_participants=Util.determineMergeParticipants(map); System.out.println("merge_participants = " + merge_participants); assert merge_participants.size() == 3; assert merge_participants.contains(a) && merge_participants.contains(c) && merge_participants.contains(d); Collection
                merge_coords=Util.determineMergeCoords(map); System.out.println("merge_coords = " + merge_coords); assert merge_coords.size() == 2; assert merge_coords.contains(a) && merge_coords.contains(c); } public static void testAttributeNameToMethodName() { _testAttributeNameToMethodName("my_name", "MyName"); _testAttributeNameToMethodName("bela", "Bela"); _testAttributeNameToMethodName("oob_max_input_size", "OobMaxInputSize"); _testAttributeNameToMethodName("a_b_c", "ABC"); _testAttributeNameToMethodName("aa_bb_cc", "AaBbCc"); _testAttributeNameToMethodName("i", "I"); _testAttributeNameToMethodName("tmp", "Tmp"); _testAttributeNameToMethodName("inet_address_method", "InetAddressMethod"); } public static void testMethodNameToAttributeName() { _testMethodNameToAttributeName("setFoo", "foo"); _testMethodNameToAttributeName("getFoo", "foo"); _testMethodNameToAttributeName("isLogDiscardMessages", "log_discard_messages"); _testMethodNameToAttributeName("setOOBMinPoolSize", "oob_min_pool_size"); _testMethodNameToAttributeName("isOOBThreadPoolEnabled", "oob_thread_pool_enabled"); _testMethodNameToAttributeName("OOBMinPoolSize", "oob_min_pool_size"); _testMethodNameToAttributeName("inetAddressMethod", "inet_address_method"); } private static void _testMethodNameToAttributeName(String input, String expected_output) { String atttr_name=Util.methodNameToAttributeName(input); System.out.println("method name=" + input + ", attrname=" + atttr_name + ", expected output=" + expected_output); assert atttr_name.equals(expected_output) : "method name=" + input + ", attrname=" + atttr_name + ", expected output=" + expected_output; } private static void _testAttributeNameToMethodName(String input, String expected_output) { String method_name=Util.attributeNameToMethodName(input); System.out.println("attrname=" + input + ", method name=" + method_name + ", expected output=" + expected_output); assert method_name.equals(expected_output) : "attrname=" + input + ", method name=" + method_name + ", expected output=" + expected_output; } } libjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/tests/VersionTest.java0000644000175000017500000000432411647260573031767 0ustar moellermoellerpackage org.jgroups.tests; import org.jgroups.Version; import org.jgroups.Global; import org.testng.annotations.Test; /** * @author Bela Ban April 4 2003 * @version $Revision: 1.2 $ */ @Test(groups=Global.FUNCTIONAL) public class VersionTest { public static void testVersionPrint() { System.out.println("version is " +Version.printVersion()); assert true; } public static void testNullVersion() { assert !(Version.isSame((short)0)); } public static void testDifferentLengthVersion1() { short version2=Version.encode(2,0,7); assert !(Version.isSame(version2)); } public static void testDifferentVersion() { short version1=Version.encode(2,0,7), version2=Version.encode(2,0,6); assert !(version1 == version2); } public static void testSameVersion() { assert match(0,0,1, 0,0,1); assert match(1,0,0, 1,0,0); assert match(10,2,60, 10,2,60); assert !(match(1, 2, 3, 1, 2, 0)); assert !(match(0, 0, 0, 0, 0, 1)); assert !(match(2, 5, 0, 2, 5, 1)); } public static void testBinaryCompatibility() { assert isBinaryCompatible(0,0,0, 0,0,0); assert isBinaryCompatible(1,2,0, 1,2,1); assert isBinaryCompatible(1,2,0, 1,2,60); assert !(isBinaryCompatible(2, 5, 0, 2, 4, 1)); assert !(isBinaryCompatible(2, 5, 0, 2, 6, 0)); } private static 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 static 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 compatible to " + Version.print(version2) + (retval? " OK" : " FAIL")); return Version.isBinaryCompatible(version1, version2); } } libjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/tests/ViewIdTest.java0000644000175000017500000000205311647260573031526 0ustar moellermoeller package org.jgroups.tests; import org.jgroups.Global; import org.jgroups.ViewId; import org.jgroups.util.Util; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; import java.net.UnknownHostException; /** * Author: Bela Ban */ @Test(groups=Global.FUNCTIONAL) public class ViewIdTest { private ViewId v1, v2, v3; @BeforeClass public void setUp() throws UnknownHostException { v1=new ViewId(Util.createRandomAddress(), 22); v2=new ViewId(Util.createRandomAddress(), 21); v3=(ViewId)v1.clone(); } public void test0() { assert v1.equals(v2) == false; } public void test1() { assert v1.equals(v3) : "v1 and v3 should be the same view"; } public void test2() { ViewId v4=(ViewId)v1.clone(); assert v1.equals(v4); } public void test3() { assert v1.compareTo(v3) == 0; } public void test4() { assert v1.compareTo(v2) > 0; } public void test5() { assert v2.compareTo(v1) < 0; } } libjgroups-java-2.12.2.Final.orig/tests/junit-functional/org/jgroups/tests/ViewTest.java0000644000175000017500000000453711647260573031262 0ustar moellermoeller package org.jgroups.tests; import org.jgroups.Address; import org.jgroups.Global; import org.jgroups.View; import org.jgroups.ViewId; import org.jgroups.util.Util; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; import java.util.Arrays; import java.util.List; import java.util.Vector; @Test(groups=Global.FUNCTIONAL) public class ViewTest { Address a, b, c, d, e, f, g, h, i, j, k; View view; List
                members; @BeforeClass public void setUp() throws Exception { a=Util.createRandomAddress("A"); b=a; c=b; d=Util.createRandomAddress("D"); e=Util.createRandomAddress("E"); f=Util.createRandomAddress("F"); g=Util.createRandomAddress("G"); h=Util.createRandomAddress("H"); i=Util.createRandomAddress("I"); ViewId id=new ViewId(a, 34); members=Arrays.asList(a, b, d, e, f, g, h); view=new View(id, members); } public void testContainsMember() { assert view.containsMember(a) : "Member should be in view"; assert view.containsMember(b) : "Member should be in view"; assert view.containsMember(c) : "Member should be in view"; assert view.containsMember(d) : "Member should be in view"; assert view.containsMember(e) : "Member should be in view"; assert view.containsMember(f) : "Member should be in view"; assert !view.containsMember(i) : "Member should not be in view"; } public void testEqualsCreator() { assert a.equals(view.getCreator()) : "Creator should be a"; assert !view.getCreator().equals(d) : "Creator should not be d"; } public void testEquals() { assert view.equals(view); } public void testEquals2() { View v1=new View(new ViewId(a, 12345), new Vector
                (members)); View v2=new View(a, 12345, new Vector
                (members)); assert v1.equals(v2); View v3=new View(a, 12543, new Vector
                (members)); assert !v1.equals(v3); } public static void testEquals3() { View v1=new View(), v2=new View(); assert v1.equals(v2); } public void testCopy() { View view2=view.copy(); System.out.println("view = " + view); System.out.println("view2 = " + view2); assert view.equals(view2); } } libjgroups-java-2.12.2.Final.orig/tests/junit/0000755000175000017500000000000011647260573021052 5ustar moellermoellerlibjgroups-java-2.12.2.Final.orig/tests/junit/org/0000755000175000017500000000000011647260573021641 5ustar moellermoellerlibjgroups-java-2.12.2.Final.orig/tests/junit/org/jgroups/0000755000175000017500000000000011647260573023332 5ustar moellermoellerlibjgroups-java-2.12.2.Final.orig/tests/junit/org/jgroups/blocks/0000755000175000017500000000000011647260573024607 5ustar moellermoellerlibjgroups-java-2.12.2.Final.orig/tests/junit/org/jgroups/blocks/DistributedLockManagerTest.java0000644000175000017500000001100411647260573032674 0ustar moellermoellerpackage org.jgroups.blocks; import org.jgroups.Global; import org.jgroups.JChannel; import org.jgroups.tests.ChannelTestBase; import org.jgroups.util.Util; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.util.Map; /** * Testcase for the DistributedLockManager * * @author Robert Schaffar-Taurok (robert@fusion.at) */ @Test(groups=Global.STACK_DEPENDENT,sequential=true) public class DistributedLockManagerTest extends ChannelTestBase { private JChannel channel1; private JChannel channel2; protected VotingAdapter adapter1; protected VotingAdapter adapter2; protected LockManager lockManager1; protected LockManager lockManager2; @BeforeClass void setUp() throws Exception { channel1=createChannel(true); adapter1=new VotingAdapter(channel1); channel1.connect("DistributedLockManagerTest"); lockManager1=new DistributedLockManager(adapter1, "1"); Util.sleep(1000); // give some time for the channel to become a coordinator channel2=createChannel(channel1); adapter2=new VotingAdapter(channel2); lockManager2=new DistributedLockManager(adapter2, "2"); channel2.connect("DistributedLockManagerTest"); Util.sleep(1000); } @AfterClass void tearDown() throws Exception { channel2.close(); channel1.close(); } public void test() throws Exception { lockManager1.lock("obj1", "owner1", 10000); try { lockManager1.lock("obj1", "owner2", 10000); throw new IllegalStateException("obj1 should not be locked"); } catch (LockNotGrantedException ex) { System.out.println("got a lock not granted exception - expected"); } lockManager2.lock("obj2", "owner2", 1000); lockManager1.unlock("obj1", "owner1"); try { lockManager1.unlock("obj2", "owner1"); throw new IllegalStateException("obj2 should not be released"); } catch (LockNotReleasedException ex) { System.out.println("got a lock not released exception, as expected"); } lockManager1.unlock("obj2", "owner2"); } public void testMultiLock() throws Exception { lockManager1.lock("obj1", "owner1", 10000); // Override private members and simulate the errorcase which is, when two lockManagers have locked the same object // This can happen after a merge Class acquireLockDecreeClass = Class.forName("org.jgroups.blocks.DistributedLockManager$AcquireLockDecree"); Constructor acquireLockDecreeConstructor = acquireLockDecreeClass.getDeclaredConstructor(new Class[] {Object.class, Object.class, Object.class}); acquireLockDecreeConstructor.setAccessible(true); Object acquireLockDecree = acquireLockDecreeConstructor.newInstance("obj1", "owner2", "2"); Field heldLocksField = lockManager2.getClass().getDeclaredField("heldLocks"); heldLocksField.setAccessible(true); Map heldLocks = (Map)heldLocksField.get(lockManager2); heldLocks.put("obj1", acquireLockDecree); // Both lockManagers hold a lock on obj1 now try { lockManager1.unlock("obj1", "owner1", true); throw new IllegalStateException("obj1 should throw a lockMultiLockedException upon release"); } catch (LockMultiLockedException e) { // everything is ok } try { lockManager1.lock("obj1", "owner1", 10000); throw new IllegalStateException("obj1 should throw a LockNotGrantedException because it is still locked by lockManager2"); } catch (LockNotGrantedException e) { // everything is ok } try { lockManager2.unlock("obj1", "owner2", true); throw new IllegalStateException("obj1 should throw a lockMultiLockedException upon release"); } catch (LockMultiLockedException e) { // everything is ok } // Everything should be unlocked now try { lockManager1.lock("obj1", "owner1", 10000); } catch (LockNotGrantedException e) { throw new IllegalStateException("obj1 should be unlocked"); } lockManager1.unlock("obj1", "owner1", true); } } libjgroups-java-2.12.2.Final.orig/tests/junit/org/jgroups/blocks/ExecutingServiceTest.java0000644000175000017500000005336611647260573031603 0ustar moellermoellerpackage org.jgroups.blocks; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.lang.reflect.Constructor; import java.util.ArrayList; import java.util.Collection; import java.util.Queue; import java.util.Random; import java.util.concurrent.BlockingQueue; import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.Callable; import java.util.concurrent.CyclicBarrier; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.locks.Lock; import org.jgroups.Address; import org.jgroups.Global; import org.jgroups.JChannel; import org.jgroups.blocks.executor.ExecutionCompletionService; import org.jgroups.blocks.executor.ExecutionRunner; import org.jgroups.blocks.executor.ExecutionService; import org.jgroups.blocks.executor.ExecutionService.DistributedFuture; import org.jgroups.blocks.executor.Executions; import org.jgroups.conf.ClassConfigurator; import org.jgroups.logging.Log; import org.jgroups.logging.LogFactory; import org.jgroups.protocols.CENTRAL_EXECUTOR; import org.jgroups.protocols.Executing.Owner; import org.jgroups.stack.Protocol; import org.jgroups.stack.ProtocolStack; import org.jgroups.tests.ChannelTestBase; import org.jgroups.util.NotifyingFuture; import org.jgroups.util.Streamable; import org.jgroups.util.Util; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; /** Tests {@link org.jgroups.blocks.executor.ExecutionService} * @author wburns */ @Test(groups=Global.STACK_DEPENDENT,sequential=true) public class ExecutingServiceTest extends ChannelTestBase { protected static Log log = LogFactory.getLog(ExecutingServiceTest.class); protected static AtomicReference requestBlocker = new AtomicReference(); protected JChannel c1, c2, c3; protected ExecutionService e1, e2, e3; protected ExecutionRunner er1, er2, er3; @BeforeClass protected void init() throws Exception { c1=createChannel(true, 3, "A"); addExecutingProtocol(c1); er1=new ExecutionRunner(c1); c1.connect("ExecutionServiceTest"); c2=createChannel(c1, "B"); er2=new ExecutionRunner(c2); c2.connect("ExecutionServiceTest"); c3=createChannel(c1, "C"); er3=new ExecutionRunner(c3); c3.connect("ExecutionServiceTest"); LogFactory.getLog(ExecutionRunner.class).setLevel("trace"); } @AfterClass protected void cleanup() { Util.close(c3,c2,c1); } @BeforeMethod protected void createExecutors() { e1=new ExecutionService(c1); e2=new ExecutionService(c2); e3=new ExecutionService(c3); // Clear out the queue, in case if test doesn't clear it SleepingStreamableCallable.canceledThreads.clear(); // Reset the barrier in case test failed on the barrier SleepingStreamableCallable.barrier.reset(); } public static class ExposedExecutingProtocol extends CENTRAL_EXECUTOR { public ExposedExecutingProtocol() { // We use the same id as the CENTRAL_EXECUTOR id=ClassConfigurator.getProtocolId(CENTRAL_EXECUTOR.class); } // @see org.jgroups.protocols.Executing#sendRequest(org.jgroups.Address, org.jgroups.protocols.Executing.Type, long, java.lang.Object) @Override protected void sendRequest(Address dest, Type type, long requestId, Object object) { CyclicBarrier barrier = requestBlocker.get(); if (barrier != null) { try { barrier.await(); } catch (InterruptedException e) { assert false : "Exception while waiting: " + e.toString(); } catch (BrokenBarrierException e) { assert false : "Exception while waiting: " + e.toString(); } } super.sendRequest(dest, type, requestId, object); } public Queue getAwaitingConsumerQueue() { return _awaitingConsumer; } public Queue getRequestsFromCoordinator() { return _runRequests; } public Lock getLock() { return _consumerLock; } } /** * This class is to be used to test to make sure that when a non callable * is to be serialized that it works correctly. *

                * This class provides a few constructors that shouldn't be used. They * are just present to possibly poke holes in the constructor array offset. * @param The type that the value can be returned as * @author wburns */ protected static class SimpleCallable implements Callable { final V _object; // This constructor shouldn't be used public SimpleCallable(String noUse) { throw new UnsupportedOperationException(); } // This constructor shouldn't be used public SimpleCallable(Integer noUse) { throw new UnsupportedOperationException(); } public SimpleCallable(V object) { _object = object; } // This constructor shouldn't be used public SimpleCallable() { throw new UnsupportedOperationException(); } @Override public V call() throws Exception { return _object; } } protected static class SleepingStreamableCallable implements Callable, Streamable { long millis; public static BlockingQueue canceledThreads = new LinkedBlockingQueue(); public static CyclicBarrier barrier = new CyclicBarrier(2); public SleepingStreamableCallable() { } public SleepingStreamableCallable(long millis) { this.millis=millis; } @Override public void writeTo(DataOutputStream out) throws IOException { out.writeLong(millis); } @Override public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { millis = in.readLong(); } @Override public Void call() throws Exception { barrier.await(); try { Thread.sleep(millis); } catch (InterruptedException e) { Thread interruptedThread = Thread.currentThread(); if (log.isTraceEnabled()) log.trace("Submitted cancelled thread - " + interruptedThread); canceledThreads.offer(interruptedThread); } return null; } // @see java.lang.Object#toString() @Override public String toString() { return "SleepingStreamableCallable [timeout=" + millis + "]"; } } protected static class SimpleStreamableCallable implements Callable, Streamable { V _object; public SimpleStreamableCallable() { } public SimpleStreamableCallable(V object) { _object = object; } @Override public V call() throws Exception { return _object; } // @see java.lang.Object#toString() @Override public String toString() { return "SimpleSerializableCallable [value=" + _object + "]"; } @Override public void writeTo(DataOutputStream out) throws IOException { try { Util.writeObject(_object, out); } catch (IOException e) { throw e; } catch (Exception e) { throw new IOException(e); } } @SuppressWarnings("unchecked") @Override public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { try { _object = (V)Util.readObject(in); } catch (IOException e) { throw e; } catch (Exception e) { throw new IOException(e); } } } @Test public void testSimpleSerializableCallableSubmit() throws InterruptedException, ExecutionException, TimeoutException { Long value = Long.valueOf(100); Callable callable = new SimpleStreamableCallable(value); Thread consumer = new Thread(er2); consumer.start(); NotifyingFuture future = e1.submit(callable); Long returnValue = future.get(10L, TimeUnit.SECONDS); // We try to stop the thread. consumer.interrupt(); assert value == returnValue : "The value returned doesn't match"; consumer.join(2000); assert !consumer.isAlive() : "Consumer did not stop correctly"; } @SuppressWarnings({ "rawtypes", "unchecked" }) @Test public void testSimpleSerializableCallableConcurrently() throws InterruptedException, ExecutionException, TimeoutException { Thread[] consumers = {new Thread(er1), new Thread(er2), new Thread(er3)}; for (Thread thread : consumers) { thread.start(); } Random random = new Random(); int count = 100; Future[] futures1 = new Future[count]; Future[] futures2 = new Future[count]; Future[] futures3 = new Future[count]; StringBuilder builder = new StringBuilder("base"); for (int i = 0; i < count; i++) { builder.append(random.nextInt(10)); String value = builder.toString(); futures1[i] = e1.submit(new SimpleStreamableCallable(value)); futures2[i] = e2.submit(new SimpleStreamableCallable(value)); futures3[i] = e3.submit(new SimpleStreamableCallable(value)); } for (int i = 0; i < count; i++) { // All 3 of the futures should have returned the same value Object value = futures1[i].get(10L, TimeUnit.SECONDS); assert value.equals(futures2[i].get(10L, TimeUnit.SECONDS)); assert value.equals(futures3[i].get(10L, TimeUnit.SECONDS)); // Make sure that same value is what it should be CharSequence seq = builder.subSequence(0, 5+i); assert value.equals(seq); } for (Thread consumer : consumers) { // We try to stop the thread. consumer.interrupt(); consumer.join(2000); assert !consumer.isAlive() : "Consumer did not stop correctly"; } } /** * Interrupts can have a lot of timing issues, so we run it a lot to make * sure we find all the issues. * @throws InterruptedException * @throws BrokenBarrierException * @throws TimeoutException */ @Test public void testInterruptWhileRunningAlot() throws InterruptedException, BrokenBarrierException, TimeoutException { for (int i = 0; i < 500; ++i) testInterruptTaskRequestWhileRunning(); } protected void testInterruptTaskRequestWhileRunning() throws InterruptedException, BrokenBarrierException, TimeoutException { Callable callable = new SleepingStreamableCallable(10000); Thread consumer = new Thread(er2); consumer.start(); NotifyingFuture future = e1.submit(callable); // We wait until it is ready SleepingStreamableCallable.barrier.await(5, TimeUnit.SECONDS); if (log.isTraceEnabled()) log.trace("Cancelling future by interrupting"); future.cancel(true); Thread cancelled = SleepingStreamableCallable.canceledThreads.poll(2, TimeUnit.SECONDS); if (log.isTraceEnabled()) log.trace("Cancelling task by interrupting"); // We try to stop the thread now which should now stop the runner consumer.interrupt(); assert cancelled != null : "There was no cancelled thread"; consumer.join(2000); assert !consumer.isAlive() : "Consumer did not stop correctly"; } @Test public void testInterruptTaskRequestBeforeRunning() throws InterruptedException, TimeoutException { Callable callable = new SleepingStreamableCallable(10000); NotifyingFuture future = e1.submit(callable); // Now we make sure that ExposedExecutingProtocol protocol = (ExposedExecutingProtocol)c1.getProtocolStack().findProtocol( ExposedExecutingProtocol.class); Queue queue = protocol.getAwaitingConsumerQueue(); Lock lock = protocol.getLock(); lock.lock(); try { assert queue.peek() != null : "The object in queue doesn't match"; } finally { lock.unlock(); } // This should remove the task before it starts, since the consumer is // not yet running future.cancel(false); lock.lock(); try { assert queue.peek() == null : "There should be no more objects in the queue"; } finally { lock.unlock(); } } @Test public void testExecutorAwaitTerminationNoInterrupt() throws InterruptedException, BrokenBarrierException, TimeoutException { testExecutorAwaitTermination(false); } @Test public void testExecutorAwaitTerminationInterrupt() throws InterruptedException, BrokenBarrierException, TimeoutException { testExecutorAwaitTermination(true); } protected void testExecutorAwaitTermination(boolean interrupt) throws InterruptedException, BrokenBarrierException, TimeoutException { Thread consumer = new Thread(er2); consumer.start(); // We send a task that waits for 101 milliseconds and then finishes Callable callable = new SleepingStreamableCallable(101); e1.submit(callable); // We wait for the thread to start SleepingStreamableCallable.barrier.await(2, TimeUnit.SECONDS); if (interrupt) { if (log.isTraceEnabled()) log.trace("Cancelling futures by interrupting"); e1.shutdownNow(); // We wait for the task to be interrupted. assert SleepingStreamableCallable.canceledThreads.poll(2, TimeUnit.SECONDS) != null : "Thread wasn't interrupted due to our request"; } else { e1.shutdown(); } assert e1.awaitTermination(2, TimeUnit.SECONDS) : "Executor didn't terminate fast enough"; try { e1.submit(callable); assert false : "Task was submitted, where as it should have been rejected"; } catch (RejectedExecutionException e) { // We should have received this exception } if (log.isTraceEnabled()) log.trace("Cancelling task by interrupting"); // We try to stop the thread. consumer.interrupt(); consumer.join(2000); assert !consumer.isAlive() : "Consumer did not stop correctly"; } @Test public void testNonSerializableCallable() throws SecurityException, NoSuchMethodException, InterruptedException, ExecutionException, TimeoutException { Thread consumer = new Thread(er2); consumer.start(); Long value = Long.valueOf(100); @SuppressWarnings("rawtypes") Constructor constructor = SimpleCallable.class.getConstructor(Object.class); constructor.getGenericParameterTypes(); @SuppressWarnings("unchecked") Callable callable = (Callable)Executions.serializableCallable( constructor, value); NotifyingFuture future = e1.submit(callable); Long returnValue = future.get(10L, TimeUnit.SECONDS); // We try to stop the thread. consumer.interrupt(); assert value == returnValue : "The value returned doesn't match"; consumer.join(2000); assert !consumer.isAlive() : "Consumer did not stop correctly"; } @Test public void testExecutionCompletionService() throws InterruptedException { Thread consumer1 = new Thread(er2); consumer1.start(); Thread consumer2 = new Thread(er3); consumer2.start(); ExecutionCompletionService service = new ExecutionCompletionService(e1); // The sleeps will not occur until both threads get there due to barrier // This should result in future2 always ending first since the sleep // is 3 times smaller Future future1 = service.submit(new SleepingStreamableCallable(300)); Future future2 = service.submit(new SleepingStreamableCallable(100)); assert service.poll(2, TimeUnit.SECONDS) == future2 : "The task either didn't come back or was in wrong order"; assert service.poll(2, TimeUnit.SECONDS) == future1 : "The task either didn't come back or was in wrong order"; // We try to stop the threads. consumer1.interrupt(); consumer2.interrupt(); consumer1.join(2000); assert !consumer1.isAlive() : "Consumer did not stop correctly"; consumer2.join(2000); assert !consumer2.isAlive() : "Consumer did not stop correctly"; } @Test public void testCoordinatorWentDownWhileSendingMessage() throws Exception { // It is 3 calls. // The first is the original message sending to the coordinator // The second is the new message to send the request to the new coordinator // The last is our main method below waiting for others final CyclicBarrier barrier = new CyclicBarrier(3); requestBlocker.set(barrier); final Callable callable = new SimpleStreamableCallable(23); ExecutorService service = Executors.newCachedThreadPool(); service.submit(new Runnable() { @Override public void run() { e2.submit(callable); } }); service.submit(new Runnable() { @Override public void run() { // We close the coordinator Util.close(c1); } }); barrier.await(2, TimeUnit.SECONDS); requestBlocker.getAndSet(null).reset(); // We need to reconnect the channel now c1=createChannel(c2, "A"); addExecutingProtocol(c1); er1=new ExecutionRunner(c1); c1.connect("ExecutionServiceTest"); service.shutdown(); service.awaitTermination(2, TimeUnit.SECONDS); // Now we make sure that the new coordinator has the requests ExposedExecutingProtocol protocol = (ExposedExecutingProtocol)c2.getProtocolStack().findProtocol( ExposedExecutingProtocol.class); Queue runnables = protocol.getAwaitingConsumerQueue(); assert runnables.size() == 1 : "There is no runnable in the queue"; Runnable task = runnables.iterator().next(); assert task instanceof DistributedFuture : "The task wasn't a distributed future like we thought"; assert callable == ((DistributedFuture)task).getCallable() : "The inner callable wasn't the same"; Queue requests = protocol.getRequestsFromCoordinator(); assert requests.size() == 1 : "There is no request in the coordinator queue - " + requests.size(); Owner owner = requests.iterator().next(); assert owner.getAddress().equals(c2.getAddress()) : "The request Address doesn't match"; assert owner.getRequestId() == 0 : "We only had 1 request so it should be zero still"; } @Test public void testInvokeAnyCalls() throws InterruptedException, ExecutionException { Thread consumer1 = new Thread(er2); consumer1.start(); Thread consumer2 = new Thread(er3); consumer2.start(); Collection> callables = new ArrayList>(); callables.add(new SimpleStreamableCallable((long)10)); callables.add(new SimpleStreamableCallable((long)100)); Long value = e1.invokeAny(callables); assert value == 10 || value == 100 : "The task didn't return the right value"; // We try to stop the threads. consumer1.interrupt(); consumer2.interrupt(); consumer1.join(2000); assert !consumer1.isAlive() : "Consumer did not stop correctly"; consumer2.join(2000); assert !consumer2.isAlive() : "Consumer did not stop correctly"; } protected void addExecutingProtocol(JChannel ch) { ProtocolStack stack=ch.getProtocolStack(); Protocol protocol = new ExposedExecutingProtocol(); protocol.setLevel("trace"); stack.insertProtocolAtTop(protocol); } } libjgroups-java-2.12.2.Final.orig/tests/junit/org/jgroups/blocks/LockServiceTest.java0000644000175000017500000002645511647260573030537 0ustar moellermoellerpackage org.jgroups.blocks; import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import org.jgroups.Global; import org.jgroups.JChannel; import org.jgroups.blocks.locking.LockService; import org.jgroups.protocols.CENTRAL_LOCK; import org.jgroups.stack.Protocol; import org.jgroups.stack.ProtocolStack; import org.jgroups.tests.ChannelTestBase; import org.jgroups.util.Util; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; /** Tests {@link org.jgroups.blocks.locking.LockService} * @author Bela Ban */ @Test(groups=Global.STACK_DEPENDENT,sequential=true) public class LockServiceTest extends ChannelTestBase { protected JChannel c1, c2, c3; protected LockService s1, s2, s3; protected Lock lock; protected static final String LOCK="sample-lock"; @BeforeClass protected void init() throws Exception { c1=createChannel(true, 3, "A"); addLockingProtocol(c1); s1=new LockService(c1); c1.connect("LockServiceTest"); c2=createChannel(c1, "B"); s2=new LockService(c2); c2.connect("LockServiceTest"); c3=createChannel(c1, "C"); s3=new LockService(c3); c3.connect("LockServiceTest"); lock=s1.getLock(LOCK); } @AfterClass protected void cleanup() { Util.close(c3,c2,c1); } @BeforeMethod protected void unlockAll() { s3.unlockAll(); s2.unlockAll(); s1.unlockAll(); } public void testSimpleLock() { lock(lock, LOCK); unlock(lock, LOCK); } public void testLockingOfAlreadyAcquiredLock() { lock(lock, LOCK); lock(lock, LOCK); unlock(lock, LOCK); } public void testUnsuccessfulTryLock() { System.out.println("s1:\n" + s1.printLocks() + "\ns2:\n" + s2.printLocks() + "\ns3:\n" + s3.printLocks()); Lock lock2=s2.getLock(LOCK); lock(lock2, LOCK); try { boolean rc=tryLock(lock, LOCK); assert !rc; unlock(lock, LOCK); } finally { unlock(lock2, LOCK); } } public void testUnsuccessfulTryLockTimeout() throws InterruptedException { Lock lock2=s2.getLock(LOCK); lock(lock2, LOCK); try { boolean rc=tryLock(lock, 1000, LOCK); assert !rc; } finally { unlock(lock2, LOCK); } } public void testLockInterrupt() { // Interrupt ourselves before trying to acquire lock Thread.currentThread().interrupt(); lock.lock(); try { System.out.println("Locks we have: " + s1.printLocks()); if(Thread.interrupted()) { System.out.println("We still have interrupt flag set, as it should be"); } else { assert false : "Interrupt status was lost - we don't want this!"; } } finally { lock.unlock(); } } public void testTryLockInterrupt() { // Interrupt ourselves before trying to acquire lock Thread.currentThread().interrupt(); lock.tryLock(); try { System.out.println("Locks we have: " + s1.printLocks()); if(Thread.interrupted()) { System.out.println("We still have interrupt flag set, as it should be"); } else { assert false : "Interrupt status was lost - we don't want this!"; } } finally { lock.unlock(); } } @Test(expectedExceptions=InterruptedException.class) public void testLockInterruptibly() throws InterruptedException { // Interrupt ourselves before trying to acquire lock Thread.currentThread().interrupt(); lock.lockInterruptibly(); try { System.out.println("Locks we have: " + s1.printLocks()); if(Thread.interrupted()) { System.out.println("We still have interrupt flag set, as it should be"); } else { assert false : "Interrupt status was lost - we don't want this!"; } } finally { lock.unlock(); } } public void testSuccessfulSignalAllTimeout() throws InterruptedException, BrokenBarrierException { Lock lock2=s2.getLock(LOCK); Thread locker=new Signaller(true); boolean rc=tryLock(lock2, 5000, LOCK); assert rc; locker.start(); assert awaitNanos(lock2.newCondition(), TimeUnit.SECONDS.toNanos(5), LOCK) > 0 : "Condition was not signalled"; unlock(lock2, LOCK); } public void testSuccessfulTryLockTimeout() throws InterruptedException, BrokenBarrierException { final CyclicBarrier barrier=new CyclicBarrier(2); Thread locker=new Locker(barrier); locker.start(); barrier.await(); boolean rc=tryLock(lock, 10000, LOCK); assert rc; unlock(lock, LOCK); } public void testConcurrentLockRequests() throws Exception { int NUM=10; final CyclicBarrier barrier=new CyclicBarrier(NUM +1); TryLocker[] lockers=new TryLocker[NUM]; for(int i=0; i < lockers.length; i++) { lockers[i]=new TryLocker(lock, barrier, 500); lockers[i].start(); } barrier.await(); for(TryLocker locker: lockers) locker.join(); int num_acquired=0; for(TryLocker locker: lockers) { if(locker.acquired) { num_acquired++; } } assert num_acquired == 1; } public void testConcurrentLockRequestsFromDifferentMembers() throws Exception { int NUM=10; final CyclicBarrier barrier=new CyclicBarrier(NUM +1); TryLocker[] lockers=new TryLocker[NUM]; LockService[] services=new LockService[]{s1, s2, s3}; for(int i=0; i < lockers.length; i++) { Lock mylock=services[i % services.length].getLock(LOCK); lockers[i]=new TryLocker(mylock, barrier, 500); lockers[i].start(); } barrier.await(); for(TryLocker locker: lockers) locker.join(); int num_acquired=0; for(TryLocker locker: lockers) { if(locker.acquired) { num_acquired++; } } assert num_acquired == 1; } protected class Locker extends Thread { protected final CyclicBarrier barrier; public Locker(CyclicBarrier barrier) { this.barrier=barrier; } public void run() { lock(lock, LOCK); try { barrier.await(); Util.sleep(500); } catch(Exception e) { } finally { unlock(lock, LOCK); } } } protected class Signaller extends Thread { protected final boolean all; public Signaller(boolean all) { this.all=all; } public void run() { lock(lock, LOCK); try { Util.sleep(500); if (all) { signallingAll(lock.newCondition(), LOCK); } else { signalling(lock.newCondition(), LOCK); } } catch(Exception e) { e.printStackTrace(); } finally { unlock(lock, LOCK); } } } protected static class TryLocker extends Thread { protected final Lock mylock; protected final CyclicBarrier barrier; protected final long timeout; protected boolean acquired; public TryLocker(Lock mylock, CyclicBarrier barrier, long timeout) { this.mylock=mylock; this.barrier=barrier; this.timeout=timeout; } public boolean isAcquired() { return acquired; } public void run() { try { barrier.await(); } catch(Exception e) { e.printStackTrace(); } try { acquired=tryLock(mylock, timeout, LOCK); Util.sleep(timeout * 2); } catch(InterruptedException e) { e.printStackTrace(); } finally { unlock(mylock, LOCK); } } } protected static void lock(Lock lock, String name) { System.out.println("[" + Thread.currentThread().getId() + "] locking " + name); lock.lock(); System.out.println("[" + Thread.currentThread().getId() + "] locked " + name); } protected static boolean tryLock(Lock lock, String name) { System.out.println("[" + Thread.currentThread().getId() + "] tryLocking " + name); boolean rc=lock.tryLock(); System.out.println("[" + Thread.currentThread().getId() + "] " + (rc? "locked " : "failed locking") + name); return rc; } protected static boolean tryLock(Lock lock, long timeout, String name) throws InterruptedException { System.out.println("[" + Thread.currentThread().getId() + "] tryLocking " + name); boolean rc=lock.tryLock(timeout, TimeUnit.MILLISECONDS); System.out.println("[" + Thread.currentThread().getId() + "] " + (rc? "locked " : "failed locking ") + name); return rc; } protected static void unlock(Lock lock, String name) { if(lock == null) return; System.out.println("[" + Thread.currentThread().getId() + "] releasing " + name); lock.unlock(); System.out.println("[" + Thread.currentThread().getId() + "] released " + name); } protected static long awaitNanos(Condition condition, long nanoSeconds, String name) throws InterruptedException { System.out.println("[" + Thread.currentThread().getId() + "] waiting for signal - released lock " + name); long value = condition.awaitNanos(nanoSeconds); System.out.println("[" + Thread.currentThread().getId() + "] waited for signal - obtained lock " + name); return value; } protected static void signalling(Condition condition, String name) { System.out.println("[" + Thread.currentThread().getId() + "] signalling " + name); condition.signal(); System.out.println("[" + Thread.currentThread().getId() + "] signalled " + name); } protected static void signallingAll(Condition condition, String name) { System.out.println("[" + Thread.currentThread().getId() + "] signalling all " + name); condition.signalAll(); System.out.println("[" + Thread.currentThread().getId() + "] signalled " + name); } protected void addLockingProtocol(JChannel ch) { ProtocolStack stack=ch.getProtocolStack(); Protocol lockprot = new CENTRAL_LOCK(); lockprot.setLevel("trace"); stack.insertProtocolAtTop(lockprot); } } libjgroups-java-2.12.2.Final.orig/tests/junit/org/jgroups/blocks/MuxMessageDispatcherTest.java0000644000175000017500000001722311647260573032404 0ustar moellermoellerpackage org.jgroups.blocks; import java.util.Map; import org.jgroups.Address; import org.jgroups.Channel; import org.jgroups.Global; import org.jgroups.JChannel; import org.jgroups.Message; import org.jgroups.blocks.mux.MuxMessageDispatcher; import org.jgroups.blocks.mux.MuxUpHandler; import org.jgroups.tests.ChannelTestBase; import org.jgroups.util.Rsp; import org.jgroups.util.Util; import org.testng.Assert; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; /** * @author Paul Ferraro */ @Test(groups=Global.STACK_DEPENDENT) public class MuxMessageDispatcherTest extends ChannelTestBase { private JChannel[] channels = new JChannel[2]; private MessageDispatcher[] dispatchers = new MessageDispatcher[2]; private MessageDispatcher[][] muxDispatchers = new MessageDispatcher[2][2]; private MethodCall method = new MethodCall("getName", new Object[0], new Class[0]); @BeforeClass void setUp() throws Exception { channels[0] = createChannel(true); channels[1] = createChannel(channels[0]); for (int i = 0; i < dispatchers.length; i++) { dispatchers[i] = new MessageDispatcher(channels[i], null, null, new MuxRequestListener("dispatcher[" + i + "]")); channels[i].setUpHandler(new MuxUpHandler(dispatchers[i].getProtocolAdapter())); for (int j = 0; j < muxDispatchers[i].length; j++) { muxDispatchers[i][j] = new MuxMessageDispatcher((short) j, channels[i], null, null, new MuxRequestListener("muxDispatcher[" + i + "][" + j + "]")); } channels[i].connect("MuxMessageDispatcherTest"); Util.sleep(1000); } } @AfterClass void tearDown() throws Exception { for (int i = 0; i < dispatchers.length; ++i) { channels[i].disconnect(); channels[i].close(); dispatchers[i].stop(); for (int j = 0; j < muxDispatchers[i].length; ++j) { muxDispatchers[i][j].stop(); } } } public void testCastMessage() throws Exception { Message message = new Message(); // Validate normal dispatchers Map responses = dispatchers[0].castMessage(null, message, RequestOptions.SYNC()); Assert.assertEquals(responses.size(), 2); for (int i = 0; i < dispatchers.length; ++i) { verifyResponse(responses, channels[i], "dispatcher[" + i + "]"); } // Validate muxed dispatchers for (int j = 0; j < muxDispatchers[0].length; ++j) { responses = muxDispatchers[0][j].castMessage(null, message, RequestOptions.SYNC()); Assert.assertEquals(responses.size(), 2); for (int i = 0; i < dispatchers.length; ++i) { verifyResponse(responses, channels[i], "muxDispatcher[" + i + "][" + j + "]"); } } final Address address = channels[0].getAddress(); RspFilter filter = new RspFilter() { public boolean isAcceptable(Object response, Address sender) { return !sender.equals(address); } public boolean needMoreResponses() { return true; } }; // Validate muxed rpc dispatchers w/filter responses = muxDispatchers[0][0].castMessage(null, message, RequestOptions.SYNC().setRspFilter(filter)); Assert.assertEquals(responses.size(), 2); verifyResponse(responses, channels[0], null); verifyResponse(responses, channels[1], "muxDispatcher[1][0]"); muxDispatchers[1][0].stop(); // Validate stopped mux dispatcher response is auto-filtered responses = muxDispatchers[0][0].castMessage(null, message, RequestOptions.SYNC().setRspFilter(null)); Assert.assertEquals(responses.size(), 2); verifyResponse(responses, channels[0], "muxDispatcher[0][0]"); verifyResponse(responses, channels[1], null); // Validate stopped mux dispatcher response is auto-filtered and custom filter is applied responses = muxDispatchers[0][0].castMessage(null, message, RequestOptions.SYNC().setRspFilter(filter)); Assert.assertEquals(responses.size(), 2); verifyResponse(responses, channels[0], null); verifyResponse(responses, channels[1], null); muxDispatchers[1][0].start(); // Validate restarted mux dispatcher functions normally responses = muxDispatchers[0][0].castMessage(null, message, RequestOptions.SYNC().setRspFilter(null)); Assert.assertEquals(responses.size(), 2); verifyResponse(responses, channels[0], "muxDispatcher[0][0]"); verifyResponse(responses, channels[1], "muxDispatcher[1][0]"); } public void testSendMessage() throws Throwable { final Address address = channels[1].getAddress(); Message message = new Message(address); // Validate normal dispatchers Object response = dispatchers[0].sendMessage(message, RequestOptions.SYNC()); Assert.assertEquals(response, "dispatcher[1]"); // Validate muxed dispatchers for (int j = 0; j < muxDispatchers[0].length; ++j) { response = muxDispatchers[0][j].sendMessage(message, RequestOptions.SYNC()); Assert.assertEquals(response, "muxDispatcher[1][" + j + "]"); } // Filter testing is disabled for now pending filter improvements in JGroups 3 // // Validate muxed rpc dispatchers w/filter // // RspFilter filter = new RspFilter() { // // @Override // public boolean isAcceptable(Object response, Address sender) { // return !sender.equals(address); // } // // @Override // public boolean needMoreResponses() { // return true; // } // }; // // response = muxDispatchers[0][0].callRemoteMethod(address, method, RequestOptions.SYNC.setRspFilter(filter)); // // Assert.assertNull(address); // // // Validate stopped mux dispatcher response is auto-filtered // muxDispatchers[1][0].stop(); // // response = muxDispatchers[0][0].callRemoteMethod(address, method, RequestOptions.SYNC.setRspFilter(null)); // // Assert.assertNull(address); // // // Validate restarted mux dispatcher functions normally // muxDispatchers[1][0].start(); // // response = muxDispatchers[0][0].callRemoteMethod(address, method, RequestOptions.SYNC.setRspFilter(null)); // // Assert.assertEquals(response, "muxDispatcher[1][0]"); } private static void verifyResponse(Map responses, Channel channel, Object expected) { Rsp response = responses.get(channel.getAddress()); String address = channel.getAddress().toString(); Assert.assertNotNull(response, address); Assert.assertFalse(response.wasSuspected(), address); if (expected != null) { Assert.assertTrue(response.wasReceived(), address); Assert.assertEquals(response.getValue(), expected, address); } else { Assert.assertFalse(response.wasReceived(), address); } } public static class MuxRequestListener implements RequestHandler { private final String name; public MuxRequestListener(String name) { this.name = name; } public Object handle(Message msg) { return this.name; } } }libjgroups-java-2.12.2.Final.orig/tests/junit/org/jgroups/blocks/MuxRpcDispatcherTest.java0000644000175000017500000001706111647260573031544 0ustar moellermoellerpackage org.jgroups.blocks; import org.jgroups.Address; import org.jgroups.Channel; import org.jgroups.Global; import org.jgroups.JChannel; import org.jgroups.blocks.mux.MuxRpcDispatcher; import org.jgroups.blocks.mux.MuxUpHandler; import org.jgroups.tests.ChannelTestBase; import org.jgroups.util.Rsp; import org.jgroups.util.Util; import org.testng.Assert; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; import java.util.Map; /** * @author Paul Ferraro */ @Test(groups=Global.STACK_DEPENDENT) public class MuxRpcDispatcherTest extends ChannelTestBase { private JChannel[] channels = new JChannel[2]; private RpcDispatcher[] dispatchers = new RpcDispatcher[2]; private RpcDispatcher[][] muxDispatchers = new RpcDispatcher[2][2]; @BeforeClass void setUp() throws Exception { channels[0] = createChannel(true); channels[1] = createChannel(channels[0]); for (int i = 0; i < dispatchers.length; i++) { dispatchers[i] = new RpcDispatcher(channels[i], null, null, new Server("dispatcher[" + i + "]")); channels[i].setUpHandler(new MuxUpHandler(dispatchers[i].getProtocolAdapter())); for (int j = 0; j < muxDispatchers[i].length; j++) { muxDispatchers[i][j] = new MuxRpcDispatcher((short) j, channels[i], null, null, new Server("muxDispatcher[" + i + "][" + j + "]")); } channels[i].connect("MuxRpcDispatcherTest"); Util.sleep(1000); } } @AfterClass void tearDown() throws Exception { for (int i = 0; i < dispatchers.length; ++i) { channels[i].disconnect(); channels[i].close(); dispatchers[i].stop(); for (int j = 0; j < muxDispatchers[i].length; ++j) { muxDispatchers[i][j].stop(); } } } public void testMulticastRPCs() throws Exception { MethodCall method = new MethodCall("getName", new Object[0], new Class[0]); // Validate normal dispatchers Map responses = dispatchers[0].callRemoteMethods(null, method, RequestOptions.SYNC()); Assert.assertEquals(responses.size(), 2); for (int i = 0; i < dispatchers.length; ++i) { verifyResponse(responses, channels[i], "dispatcher[" + i + "]"); } // Validate muxed dispatchers for (int j = 0; j < muxDispatchers[0].length; ++j) { responses = muxDispatchers[0][j].callRemoteMethods(null, method, RequestOptions.SYNC()); Assert.assertEquals(responses.size(), 2); for (int i = 0; i < dispatchers.length; ++i) { verifyResponse(responses, channels[i], "muxDispatcher[" + i + "][" + j + "]"); } } // Validate muxed rpc dispatchers w/filter final Address address = channels[0].getAddress(); RspFilter filter = new RspFilter() { public boolean isAcceptable(Object response, Address sender) { return !sender.equals(address); } public boolean needMoreResponses() { return true; } }; responses = muxDispatchers[0][0].callRemoteMethods(null, method, RequestOptions.SYNC().setRspFilter(filter)); Assert.assertEquals(responses.size(), 2); verifyResponse(responses, channels[0], null); verifyResponse(responses, channels[1], "muxDispatcher[1][0]"); // Validate stopped mux dispatcher response is auto-filtered muxDispatchers[1][0].stop(); responses = muxDispatchers[0][0].callRemoteMethods(null, method, RequestOptions.SYNC().setRspFilter(null)); Assert.assertEquals(responses.size(), 2); verifyResponse(responses, channels[0], "muxDispatcher[0][0]"); verifyResponse(responses, channels[1], null); // Validate stopped mux dispatcher response is auto-filtered and custom filter is applied responses = muxDispatchers[0][0].callRemoteMethods(null, method, RequestOptions.SYNC().setRspFilter(filter)); Assert.assertEquals(responses.size(), 2); verifyResponse(responses, channels[0], null); verifyResponse(responses, channels[1], null); // Validate restarted mux dispatcher functions normally muxDispatchers[1][0].start(); responses = muxDispatchers[0][0].callRemoteMethods(null, method, RequestOptions.SYNC().setRspFilter(null)); Assert.assertEquals(responses.size(), 2); verifyResponse(responses, channels[0], "muxDispatcher[0][0]"); verifyResponse(responses, channels[1], "muxDispatcher[1][0]"); } public void testUnicastRPCs() throws Throwable { MethodCall method = new MethodCall("getName", new Object[0], new Class[0]); final Address address = channels[1].getAddress(); // Validate normal dispatchers Object response = dispatchers[0].callRemoteMethod(address, method, RequestOptions.SYNC()); Assert.assertEquals(response, "dispatcher[1]"); // Validate muxed dispatchers for (int j = 0; j < muxDispatchers[0].length; ++j) { response = muxDispatchers[0][j].callRemoteMethod(address, method, RequestOptions.SYNC()); Assert.assertEquals(response, "muxDispatcher[1][" + j + "]"); } // Filter testing is disabled for now pending filter improvements in JGroups 3 // // Validate muxed rpc dispatchers w/filter // // RspFilter filter = new RspFilter() { // // @Override // public boolean isAcceptable(Object response, Address sender) { // return !sender.equals(address); // } // // @Override // public boolean needMoreResponses() { // return true; // } // }; // // response = muxDispatchers[0][0].callRemoteMethod(address, method, RequestOptions.SYNC.setRspFilter(filter)); // // Assert.assertNull(address); // // // Validate stopped mux dispatcher response is auto-filtered // muxDispatchers[1][0].stop(); // // response = muxDispatchers[0][0].callRemoteMethod(address, method, RequestOptions.SYNC.setRspFilter(null)); // // Assert.assertNull(address); // // // Validate restarted mux dispatcher functions normally // muxDispatchers[1][0].start(); // // response = muxDispatchers[0][0].callRemoteMethod(address, method, RequestOptions.SYNC.setRspFilter(null)); // // Assert.assertEquals(response, "muxDispatcher[1][0]"); } private static void verifyResponse(Map responses, Channel channel, Object expected) { Rsp response = responses.get(channel.getAddress()); String address = channel.getAddress().toString(); Assert.assertNotNull(response, address); Assert.assertFalse(response.wasSuspected(), address); if (expected != null) { Assert.assertTrue(response.wasReceived(), address); Assert.assertEquals(response.getValue(), expected, address); } else { Assert.assertFalse(response.wasReceived(), address); } } public static class Server { private final String name; public Server(String name) { this.name = name; } public String getName() { return this.name; } } } ././@LongLink0000000000000000000000000000015400000000000011565 Lustar rootrootlibjgroups-java-2.12.2.Final.orig/tests/junit/org/jgroups/blocks/RpcDispatcherAnycastMultipleCallsTest.javalibjgroups-java-2.12.2.Final.orig/tests/junit/org/jgroups/blocks/RpcDispatcherAnycastMultipleCallsTe0000644000175000017500000001046511647260573033602 0ustar moellermoellerpackage org.jgroups.blocks; import org.jgroups.Global; import org.jgroups.JChannel; import org.jgroups.tests.ChannelTestBase; import org.testng.annotations.AfterClass; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; @Test(groups=Global.STACK_DEPENDENT, sequential=true) public class RpcDispatcherAnycastMultipleCallsTest extends ChannelTestBase { private RpcDispatcherAnycastServerObject[] targets=null; static final int NUM=3; @BeforeClass public void init() throws Exception { targets=new RpcDispatcherAnycastServerObject[NUM]; final String GROUP="RpcDispatcherAnycastMultipleCallsTest"; JChannel first_channel=null; for(int i=0; i < NUM; i++) { JChannel c=first_channel == null? createChannel(true,NUM) : createChannel(first_channel); if(first_channel == null){ first_channel=c; } targets[i]=new RpcDispatcherAnycastServerObject(c); c.connect(GROUP); } } @AfterMethod protected void tearDown() throws Exception { if(targets != null) { for(int i=0; i < targets.length; i++) { if(targets[i] != null) targets[i].i=0; } } } @AfterClass protected void shutdown() throws Exception { if(targets != null) { for(int i=0; i < targets.length; i++) { if(targets[i] != null) targets[i].shutdown(); targets[i]=null; } targets=null; } } public void test2InstancesAnycastIncludeSelf() throws Exception { performTest(true, 2, false); } public void test3InstancesAnycastIncludeSelf() throws Exception { performTest(true, 3, false); } public void test2InstancesMcastIncludeSelf() throws Exception { performTest(false, 2, false); } public void test3InstancesMcastIncludeSelf() throws Exception { performTest(false, 3, false); } public void test2InstancesAnycastExcludeSelf() throws Exception { performTest(true, 2, true); } public void test3InstancesAnycastExcludeSelf() throws Exception { performTest(true, 3, true); } /** * Simple test that creates n instances of {@link org.jgroups.blocks.RpcDispatcherAnycastServerObject}, each one creates a Channel * and registers an RpcDispatcher. *

                * This test then calls {@link org.jgroups.blocks.RpcDispatcherAnycastServerObject#callRemote(boolean, boolean)} once on each of the n instances * and then tests that the method calls have in fact been executed on each of the n instances. * @param useAnycast if true, anycast is used for remote calls. * @param n number of instances * @param excludeSelf whether or not to exclude self in rpc calls */ private void performTest(boolean useAnycast, int n, boolean excludeSelf) throws Exception { // test that the target method has been invoked 0 times on each instance. for(int i=0; i < n; i++) assertEquals(0, targets[i].i); // if we don't exclude self, the state of all instances should be identical. int value=0; // if we are excluding self, we need an array of expected values. int[] expectedValues=null; if(excludeSelf) { expectedValues=new int[n]; for(int i=0; i < n; i++) expectedValues[i]=0; } for(int instances=0; instances < n; instances++) { targets[instances].callRemote(useAnycast, excludeSelf); // the assertions and how we measure test success is different depending on whether we exclude self or not. if(excludeSelf) { for(int i=0; i < n; i++) { if(i != instances) expectedValues[i]++; } for(int i=0; i < n; i++) assertEquals("Failure when invoking call on instance " + instances + ". Did not reach instance " + i + ".", expectedValues[i], targets[i].i); } else { value++; for(int i=0; i < n; i++) assertEquals("Failure when invoking call on instance " + instances + ". Did not reach instance " + i + ".", value, targets[i].i); } } } } ././@LongLink0000000000000000000000000000014700000000000011567 Lustar rootrootlibjgroups-java-2.12.2.Final.orig/tests/junit/org/jgroups/blocks/RpcDispatcherAnycastServerObject.javalibjgroups-java-2.12.2.Final.orig/tests/junit/org/jgroups/blocks/RpcDispatcherAnycastServerObject.ja0000644000175000017500000000327611647260573033527 0ustar moellermoellerpackage org.jgroups.blocks; import org.jgroups.Address; import org.jgroups.Channel; import org.jgroups.ChannelException; import org.jgroups.ReceiverAdapter; import org.jgroups.util.Rsp; import org.jgroups.util.RspList; import java.util.Iterator; import java.util.Map; import java.util.Vector; public class RpcDispatcherAnycastServerObject extends ReceiverAdapter { int i=0; private final Channel c; private final RpcDispatcher d; public RpcDispatcherAnycastServerObject(Channel channel) throws ChannelException { c=channel; d=new RpcDispatcher(c, this, this, this); } public void doSomething() { // System.out.println("doSomething invoked on " + c.getLocalAddress() + ". i = " + i); i++; // System.out.println("Now i = " + i); } public void callRemote(boolean useAnycast, boolean excludeSelf) { // we need to copy the vector, otherwise the modification below will throw an exception because the underlying // vector is unmodifiable Vector

                v=new Vector
                (c.getView().getMembers()); if(excludeSelf) v.remove(c.getAddress()); RspList rsps=d.callRemoteMethods(v, "doSomething", new Object[]{}, new Class[]{}, GroupRequest.GET_ALL, 10000, useAnycast); Map.Entry entry; for(Iterator it=rsps.entrySet().iterator(); it.hasNext();) { entry=(Map.Entry)it.next(); Address member=(Address)entry.getKey(); Rsp rsp=(Rsp)entry.getValue(); if(!rsp.wasReceived()) throw new RuntimeException("response from " + member + " was not received, rsp=" + rsp); } } public void shutdown() { c.close(); } }libjgroups-java-2.12.2.Final.orig/tests/junit/org/jgroups/blocks/RpcDispatcherAnycastTest.java0000644000175000017500000000503411647260573032372 0ustar moellermoellerpackage org.jgroups.blocks; import org.jgroups.Address; import org.jgroups.Global; import org.jgroups.JChannel; import org.jgroups.tests.ChannelTestBase; import org.jgroups.util.RspList; import org.jgroups.util.Util; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.Vector; /** * @author Bela Ban */ @Test(groups=Global.STACK_DEPENDENT) public class RpcDispatcherAnycastTest extends ChannelTestBase { RpcDispatcher disp, disp2, disp3; JChannel ch, ch2, ch3; @BeforeMethod void setUp() throws Exception { ch=createChannel(true,3); ServerObject obj=new ServerObject(null); disp=new RpcDispatcher(ch, null, null, obj); ch.connect("RpcDispatcherAnycastTest"); obj.setAddress(ch.getAddress()); ch2=createChannel(ch); ServerObject obj2=new ServerObject(null); disp2=new RpcDispatcher(ch2, null, null, obj2); ch2.connect("RpcDispatcherAnycastTest"); obj2.setAddress(ch2.getAddress()); ch3=createChannel(ch); ServerObject obj3=new ServerObject(null); disp3=new RpcDispatcher(ch3, null, null, obj3); ch3.connect("RpcDispatcherAnycastTest"); obj3.setAddress(ch3.getAddress()); } @AfterMethod void tearDown() throws Exception { ch3.close(); disp3.stop(); ch2.close(); disp2.stop(); ch.close(); disp.stop(); } public void testUnserializableValue() { Vector
                members=ch.getView().getMembers(); System.out.println("members: " + members); assert members.size() > 1: "we should have more than 1 member"; Vector
                subset=Util.pickSubset(members, 0.2); System.out.println("subset: " + subset); Util.sleep(1000); RspList rsps=disp.callRemoteMethods(subset, "foo", null, (Class[])null, GroupRequest.GET_ALL, 0, false); System.out.println("rsps (no anycast): " + rsps); rsps=disp.callRemoteMethods(subset, "foo", null, (Class[])null, GroupRequest.GET_ALL, 0, true); System.out.println("rsps (with anycast): " + rsps); } static class ServerObject { Address addr; public ServerObject(Address addr) { this.addr=addr; } public Address foo() { // System.out.println("foo() - returning " + addr); return addr; } public void setAddress(Address localAddress) { addr=localAddress; } } } libjgroups-java-2.12.2.Final.orig/tests/junit/org/jgroups/blocks/RpcDispatcherExceptionTest.java0000644000175000017500000000353311647260573032730 0ustar moellermoellerpackage org.jgroups.blocks; import org.jgroups.Channel; import org.jgroups.Global; import org.jgroups.tests.ChannelTestBase; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; import java.io.NotSerializableException; /** * @author Bela Ban */ @Test(groups=Global.STACK_DEPENDENT,sequential=true) public class RpcDispatcherExceptionTest extends ChannelTestBase { RpcDispatcher disp; Channel channel; private final Target target=new Target(); @BeforeClass public void setUp() throws Exception { channel=createChannel(true); disp=new RpcDispatcher(channel, null, null, target); channel.connect("RpcDispatcherExceptionTest"); } @AfterClass public void tearDown() throws Exception { disp.stop(); channel.close(); } public void testUnserializableValue() { try { disp.callRemoteMethods(null, "foo", new Object[]{new Pojo()}, new Class[]{Pojo.class}, GroupRequest.GET_ALL, 5000); throw new IllegalStateException("this should have thrown an exception"); } catch(Throwable t) { System.out.println("received an exception as expected: " + t); assert t.getCause() instanceof NotSerializableException; } } @Test(expectedExceptions=NotSerializableException.class) public void testUnserializableValue2() throws Throwable { disp.callRemoteMethod(channel.getAddress(), "foo", new Object[]{new Pojo()}, new Class[]{Pojo.class}, GroupRequest.GET_ALL, 5000); } private static class Pojo { // doesn't implement Serializable ! int age; String name; } private static class Target { public static void foo(Pojo p) { System.out.println(p.toString()); } } } libjgroups-java-2.12.2.Final.orig/tests/junit/org/jgroups/blocks/RpcDispatcherInterruptTest.java0000644000175000017500000000635211647260573032770 0ustar moellermoellerpackage org.jgroups.blocks; import org.jgroups.Address; import org.jgroups.Global; import org.jgroups.JChannel; import org.jgroups.protocols.pbcast.GMS; import org.jgroups.stack.ProtocolStack; import org.jgroups.tests.ChannelTestBase; import org.jgroups.util.Rsp; import org.jgroups.util.RspList; import org.jgroups.util.Util; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.Iterator; import java.util.Map; /** * Tests interruption of a blocked call with the timeout and a thread pool * @author Bela Ban */ @Test(groups=Global.STACK_DEPENDENT) public class RpcDispatcherInterruptTest extends ChannelTestBase { private RpcDispatcher disp, disp2; private JChannel ch, ch2; @BeforeMethod void setUp() throws Exception { ch=createChannel(true); modifyStack(ch); ServerObject obj=new ServerObject(); disp=new RpcDispatcher(ch, null, null, obj); ch.connect("RpcDispatcherInterruptTest"); ch2=createChannel(ch); ServerObject obj2=new ServerObject(); disp2=new RpcDispatcher(ch2, null, null, obj2); ch2.connect("RpcDispatcherInterruptTest"); } @AfterMethod void tearDown() throws Exception { ch2.close(); disp2.stop(); ch.close(); disp.stop(); } public void testMethodCallWithTimeoutNoInterrupt() { long timeout, block_time; RspList rsps; timeout=0; block_time=0; rsps=call(timeout, block_time); checkResults(rsps, 2, true); timeout=0; block_time=1000L; rsps=call(timeout, block_time); checkResults(rsps, 2, true); timeout=1000; block_time=0L; rsps=call(timeout, block_time); checkResults(rsps, 2, true); timeout=1000; block_time=10000L; rsps=call(timeout, block_time); checkResults(rsps, 2, false); } private static void modifyStack(JChannel ch) { ProtocolStack stack=ch.getProtocolStack(); GMS gms=(GMS)stack.findProtocol(GMS.class); if(gms != null) gms.setLogCollectMessages(false); } private RspList call(long timeout, long block_time) { long start, stop, diff; System.out.println("calling with timeout=" + timeout + ", block_time=" + block_time); start=System.currentTimeMillis(); RspList retval=disp.callRemoteMethods(null, "foo", new Object[]{block_time}, new Class[]{long.class}, GroupRequest.GET_ALL, timeout); stop=System.currentTimeMillis(); diff=stop-start; System.out.println("rsps (in " + diff + "ms:)\n" + retval); return retval; } private static void checkResults(RspList rsps, int num, boolean received) { assertEquals("responses: " + rsps, num, rsps.size()); for(Iterator> it=rsps.entrySet().iterator(); it.hasNext();) { Map.Entry entry=it.next(); Rsp rsp=entry.getValue(); assertEquals("rsp: " + rsp, rsp.wasReceived(), received); } } static class ServerObject { public static void foo(long timeout) { Util.sleep(timeout); } } }libjgroups-java-2.12.2.Final.orig/tests/junit/org/jgroups/blocks/RpcDispatcherSerializationTest.java0000644000175000017500000001564011647260573033611 0ustar moellermoellerpackage org.jgroups.blocks; import org.jgroups.Address; import org.jgroups.Channel; import org.jgroups.Global; import org.jgroups.JChannel; import org.jgroups.tests.ChannelTestBase; import org.jgroups.util.Rsp; import org.jgroups.util.RspList; import org.jgroups.util.Util; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; import java.io.*; import java.util.Iterator; import java.util.Vector; @Test(groups=Global.STACK_DEPENDENT,sequential=true) public class RpcDispatcherSerializationTest extends ChannelTestBase { private JChannel channel, channel2; private RpcDispatcher disp, disp2; private final Target target=new Target(); @BeforeClass protected void setUp() throws Exception { channel=createChannel(true); disp=new RpcDispatcher(channel, null, null, target); channel.connect("RpcDispatcherSerializationTest"); channel2=createChannel(channel); disp2=new RpcDispatcher(channel2, null, null, target); channel2.connect("RpcDispatcherSerializationTest"); } @AfterClass protected void tearDown() throws Exception { channel2.close(); disp2.stop(); disp.stop(); channel.close(); } public void testNonSerializableArgument() throws Throwable { try { disp.callRemoteMethods(null, "foo", new Object[]{new NonSerializable()}, new Class[]{NonSerializable.class}, GroupRequest.GET_ALL, 5000); throw new IllegalStateException("should throw NotSerializableException"); } catch(Throwable t) { Throwable cause=t.getCause(); if(cause != null && cause instanceof NotSerializableException) { // this needs to be changed once we change the signature System.out.println("received RuntimeException with NotSerializableException as cause - this is expected"); } else throw t; } } public void testTargetMethodNotFound() { Vector
                members=channel.getView().getMembers(); System.out.println("members are: " + members); RspList rsps=disp.callRemoteMethods(members, "foo", null, new Class[]{String.class, String.class}, GroupRequest.GET_ALL, 8000); System.out.println("responses:\n" + rsps + ", channel.view: " + channel.getView() + ", channel2.view: " + channel2.getView()); assert members.size() == rsps.size() : "expected " + members.size() + " responses, but got " + rsps + " (" + rsps.size() + ")"; for(Rsp rsp: rsps.values()) { assert rsp.getValue() instanceof NoSuchMethodException : "response value is " + rsp.getValue(); } } public void testMarshaller() { RpcDispatcher.Marshaller m=new MyMarshaller(); disp.setRequestMarshaller(m); disp.setResponseMarshaller(m); disp2.setRequestMarshaller(m); disp2.setResponseMarshaller(m); RspList rsps; rsps=disp.callRemoteMethods(null, "methodA", new Object[]{Boolean.TRUE, new Long(322649)}, new Class[]{boolean.class, long.class}, GroupRequest.GET_ALL, 0); assert rsps.size() == 2; for(Iterator it=rsps.values().iterator(); it.hasNext();) { Rsp rsp=it.next(); assert rsp.getValue() == null; assertTrue(rsp.wasReceived()); assertFalse(rsp.wasSuspected()); } rsps=disp.callRemoteMethods(null, "methodB", null, (Class[])null, GroupRequest.GET_ALL, 0); assertEquals(2, rsps.size()); for(Iterator it=rsps.values().iterator(); it.hasNext();) { Rsp rsp=it.next(); assertNotNull(rsp.getValue()); assertEquals(Boolean.TRUE, rsp.getValue()); assertTrue(rsp.wasReceived()); assertFalse(rsp.wasSuspected()); } rsps=disp.callRemoteMethods(null, "methodC", null, (Class[])null, GroupRequest.GET_ALL, 0); assertEquals(2, rsps.size()); for(Iterator it=rsps.values().iterator(); it.hasNext();) { Rsp rsp=it.next(); assertNotNull(rsp.getValue()); assertTrue(rsp.getValue() instanceof Throwable); assertTrue(rsp.wasReceived()); assertFalse(rsp.wasSuspected()); } disp.setRequestMarshaller(null); disp.setResponseMarshaller(null); disp2.setRequestMarshaller(null); disp2.setResponseMarshaller(null); } static class MyMarshaller implements RpcDispatcher.Marshaller { static final byte NULL = 0; static final byte BOOL = 1; static final byte LONG = 2; static final byte OBJ = 3; public byte[] objectToByteBuffer(Object obj) throws Exception { ByteArrayOutputStream out=new ByteArrayOutputStream(24); ObjectOutputStream oos=new ObjectOutputStream(out); try { if(obj == null) { oos.writeByte(NULL); } else if(obj instanceof Boolean) { oos.writeByte(BOOL); oos.writeBoolean(((Boolean)obj).booleanValue()); } else if(obj instanceof Long) { oos.writeByte(LONG); oos.writeLong(((Long)obj).longValue()); } else { oos.writeByte(OBJ); oos.writeObject(obj); } oos.flush(); return out.toByteArray(); } finally { Util.close(oos); } } public Object objectFromByteBuffer(byte[] buf) throws Exception { ByteArrayInputStream inp=new ByteArrayInputStream(buf); ObjectInputStream in=new ObjectInputStream(inp); try { int type=in.readByte(); switch(type) { case NULL: return null; case BOOL: return Boolean.valueOf(in.readBoolean()); case LONG: return new Long(in.readLong()); case OBJ: return in.readObject(); default: throw new IllegalArgumentException("incorrect type " + type); } } finally { Util.close(in); } } } static class Target { public static void methodA(boolean b, long l) { ; } public static boolean methodB() { return true; } public static void methodC() { throw new IllegalArgumentException("dummy exception - for testing only"); } } static class NonSerializable { int i; } } libjgroups-java-2.12.2.Final.orig/tests/junit/org/jgroups/blocks/RpcDispatcherTest.java0000644000175000017500000005422011647260573031050 0ustar moellermoellerpackage org.jgroups.blocks; import org.jgroups.Address; import org.jgroups.Global; import org.jgroups.JChannel; import org.jgroups.View; import org.jgroups.protocols.FRAG; import org.jgroups.protocols.FRAG2; import org.jgroups.protocols.TP; import org.jgroups.stack.Protocol; import org.jgroups.tests.ChannelTestBase; import org.jgroups.util.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.*; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; /** * A collection of tests to test the RpcDispatcher. * * NOTE on processing return values: * * The method RspDispatcher.callRemoteMethods(...) returns an RspList, containing one Rsp * object for each group member receiving the RPC call. Rsp.getValue() returns the * value returned by the RPC call from the corresponding member. Rsp.getValue() may * contain several classes of values, depending on what happened during the call: * * (i) a value of the expected return data type, if the RPC call completed successfully * (ii) null, if the RPC call timed out before the value could be returned * (iii) an object of type java.lang.Throwable, if an exception (e.g. lava.lang.OutOfMemoryException) * was raised during the processing of the call * * It is wise to check for such cases when processing RpcDispatcher calls. * * This also applies to the return value of callRemoteMethod(...). * * @author Bela Ban */ @Test(groups=Global.STACK_DEPENDENT,sequential=true) public class RpcDispatcherTest extends ChannelTestBase { RpcDispatcher disp1, disp2, disp3; JChannel c1, c2, c3; // specify return values sizes which should work correctly with 64Mb heap final static int[] SIZES={10000, 20000, 40000, 80000, 100000, 200000, 400000, 800000, 1000000, 2000000, 5000000}; // specify return value sizes which may generate timeouts or OOMEs with 64Mb heap final static int[] HUGESIZES={10000000, 20000000}; @BeforeMethod protected void setUp() throws Exception { c1=createChannel(true, 3); c1.setName("A"); final String GROUP="RpcDispatcherTest"; disp1=new RpcDispatcher(c1, null, null, new ServerObject(1)); c1.connect(GROUP); c2=createChannel(c1); c2.setName("B"); disp2=new RpcDispatcher(c2, null, null, new ServerObject(2)); c2.connect(GROUP); c3=createChannel(c1); c3.setName("C"); disp3=new RpcDispatcher(c3, null, null, new ServerObject(3)); c3.connect(GROUP); System.out.println("c1.view=" + c1.getView() + "\nc2.view=" + c2.getView() + "\nc3.view=" + c3.getView()); View view=c3.getView(); assert view.size() == 3 : "view=" + view; } @AfterMethod protected void tearDown() throws Exception { disp3.stop(); disp2.stop(); disp1.stop(); Util.close(c3, c2, c1); } public void testEmptyConstructor() throws Exception { RpcDispatcher d1=new RpcDispatcher(), d2=new RpcDispatcher(); JChannel channel1=null, channel2=null; final String GROUP=getUniqueClusterName("RpcDispatcherTest"); try { channel1=createChannel(true, 2); channel2=createChannel(channel1); d1.setChannel(channel1); d2.setChannel(channel2); d1.setServerObject(new ServerObject(1)); d2.setServerObject(new ServerObject(2)); d1.start(); d2.start(); channel1.connect(GROUP); channel2.connect(GROUP); Util.sleep(500); View view=channel2.getView(); System.out.println("view channel 2= " + view); view=channel1.getView(); System.out.println("view channel 1= " + view); assert view.size() == 2; RspList rsps=d1.callRemoteMethods(null, "foo", null, null, new RequestOptions(Request.GET_ALL, 5000)); System.out.println("rsps:\n" + rsps); assert rsps.size() == 2; for(Rsp rsp: rsps.values()) { assert rsp.wasReceived(); assert !rsp.wasSuspected(); assert rsp.getValue() != null; } Object server_object=new Object() { public long foobar() { return System.currentTimeMillis(); } }; d1.setServerObject(server_object); d2.setServerObject(server_object); rsps=d2.callRemoteMethods(null, "foobar", null, null, new RequestOptions(Request.GET_ALL, 5000)); System.out.println("rsps:\n" + rsps); assert rsps.size() == 2; for(Rsp rsp: rsps.values()) { assert rsp.wasReceived(); assert !rsp.wasSuspected(); assert rsp.getValue() != null; } } finally { d2.stop(); d1.stop(); Util.close(channel2, channel1); } } /** * Test the response filter mechanism which can be used to filter responses received with * a call to RpcDispatcher. * * The test filters requests based on the id of the server object they were received * from, and only accept responses from servers with id > 1. * * The expected behaviour is that the response from server 1 is rejected, but the responses * from servers 2 and 3 are accepted. * */ public void testResponseFilter() { final long timeout = 10 * 1000 ; RequestOptions options=new RequestOptions(Request.GET_ALL, timeout, false, new RspFilter() { int num=0; public boolean isAcceptable(Object response, Address sender) { boolean retval=((Integer)response).intValue() > 1; if(retval) num++; return retval; } public boolean needMoreResponses() { return num < 2; } }); RspList rsps=disp1.callRemoteMethods(null, "foo", null, null, options); System.out.println("responses are:\n" + rsps); assertEquals("there should be three response values", 3, rsps.size()); assertEquals("number of responses received should be 2", 2, rsps.numReceived()); } public void testFuture() throws Exception { MethodCall sleep=new MethodCall("sleep", new Object[]{1000L}, new Class[]{long.class}); Future future; future=disp1.callRemoteMethodsWithFuture(null, sleep, new RequestOptions(Request.GET_ALL, 5000L, false, null)); assert !future.isDone(); assert !future.isCancelled(); try { future.get(300, TimeUnit.MILLISECONDS); assert false : "we should not get here, get(300) should have thrown a TimeoutException"; } catch(TimeoutException e) { System.out.println("got TimeoutException - as expected"); } assert !future.isDone(); RspList result=future.get(6000L, TimeUnit.MILLISECONDS); System.out.println("result:\n" + result); assert result != null; assert result.size() == 3; assert future.isDone(); } public void testNotifyingFuture() throws Exception { MethodCall sleep=new MethodCall("sleep", new Object[]{1000L}, new Class[]{long.class}); NotifyingFuture future; MyFutureListener listener=new MyFutureListener(); future=disp1.callRemoteMethodsWithFuture(null, sleep, new RequestOptions(Request.GET_ALL, 5000L, false, null)); future.setListener(listener); assert !future.isDone(); assert !future.isCancelled(); assert !listener.isDone(); Util.sleep(2000); assert listener.isDone(); RspList result=future.get(1L, TimeUnit.MILLISECONDS); System.out.println("result:\n" + result); assert result != null; assert result.size() == 3; assert future.isDone(); } public void testNotifyingFutureWithDelayedListener() throws Exception { MethodCall sleep=new MethodCall("sleep", new Object[]{1000L}, new Class[]{long.class}); NotifyingFuture future; MyFutureListener listener=new MyFutureListener(); future=disp1.callRemoteMethodsWithFuture(null, sleep, new RequestOptions(Request.GET_ALL, 5000L, false, null)); assert !future.isDone(); assert !future.isCancelled(); Util.sleep(2000); future.setListener(listener); assert listener.isDone(); RspList result=future.get(1L, TimeUnit.MILLISECONDS); System.out.println("result:\n" + result); assert result != null; assert result.size() == 3; assert future.isDone(); } public void testMultipleFutures() throws Exception { MethodCall sleep=new MethodCall("sleep", new Object[]{100L}, new Class[]{long.class}); List> futures=new ArrayList>(); long target=System.currentTimeMillis() + 30000L; Future future; RequestOptions options=new RequestOptions(Request.GET_ALL, 30000L, false, null); for(int i=0; i < 10; i++) { future=disp1.callRemoteMethodsWithFuture(null, sleep, options); futures.add(future); } List> rsps=new ArrayList>(); while(!futures.isEmpty() && System.currentTimeMillis() < target) { for(Iterator> it=futures.iterator(); it.hasNext();) { future=it.next(); if(future.isDone()) { it.remove(); rsps.add(future); } } System.out.println("pending responses: " + futures.size()); Util.sleep(200); } System.out.println("\n" + rsps.size() + " responses:\n"); for(Future tmp: rsps) { System.out.println(tmp); } } public void testMultipleNotifyingFutures() throws Exception { MethodCall sleep=new MethodCall("sleep", new Object[]{100L}, new Class[]{long.class}); List listeners=new ArrayList(); RequestOptions options=new RequestOptions(Request.GET_ALL, 30000L, false, null); for(int i=0; i < 10; i++) { MyFutureListener listener=new MyFutureListener(); listeners.add(listener); disp1.callRemoteMethodsWithFuture(null, sleep, options).setListener(listener); } Util.sleep(1000); for(int i=0; i < 10; i++) { boolean all_done=true; for(MyFutureListener listener: listeners) { boolean done=listener.isDone(); System.out.print(done? "+ " : "- "); if(!listener.isDone()) all_done=false; } if(all_done) break; Util.sleep(500); System.out.println(""); } for(MyFutureListener listener: listeners) { assert listener.isDone(); } } public void testFutureCancel() throws Exception { MethodCall sleep=new MethodCall("sleep", new Object[]{1000L}, new Class[]{long.class}); Future future; future=disp1.callRemoteMethodsWithFuture(null, sleep, new RequestOptions(Request.GET_ALL, 5000L)); assert !future.isDone(); assert !future.isCancelled(); future.cancel(true); assert future.isDone(); assert future.isCancelled(); future=disp1.callRemoteMethodsWithFuture(null, sleep, new RequestOptions(Request.GET_ALL, 0)); assert !future.isDone(); assert !future.isCancelled(); future.cancel(true); assert future.isDone(); assert future.isCancelled(); } /** * Test the ability of RpcDispatcher to handle large argument and return values * with multicast RPC calls. * * The test sends requests for return values (byte arrays) having increasing sizes, * which increase the processing time for requests as well as the amount of memory * required to process requests. * * The expected behaviour is that all RPC requests complete successfully. * */ public void testLargeReturnValue() { setProps(c1, c2, c3); for(int i=0; i < SIZES.length; i++) { _testLargeValue(SIZES[i]); } } /** * Test the ability of RpcDispatcher to handle huge argument and return values * with multicast RPC calls. * * The test sends requests for return values (byte arrays) having increasing sizes, * which increase the processing time for requests as well as the amount of memory * required to process requests. * * The expected behaviour is that RPC requests either timeout or trigger out of * memory exceptions. Huge return values extend the processing time required; but * the length of time depends upon the speed of the machine the test runs on. * */ /*@Test(groups="first") public void testHugeReturnValue() { setProps(c1, c2, c3); for(int i=0; i < HUGESIZES.length; i++) { _testHugeValue(HUGESIZES[i]); } }*/ /** * Tests a method call to {A,B,C} where C left *before* the call. http://jira.jboss.com/jira/browse/JGRP-620 */ public void testMethodInvocationToNonExistingMembers() { final int timeout = 5 * 1000 ; // get the current membership, as seen by C View view=c3.getView(); Vector
                members=view.getMembers(); System.out.println("list is " + members); // cause C to leave the group and close its channel System.out.println("closing c3"); c3.close(); Util.sleep(1000); // make an RPC call using C's now outdated view of membership System.out.println("calling method foo() in " + members + " (view=" + c2.getView() + ")"); RspList rsps=disp1.callRemoteMethods(members, "foo", null, null, new RequestOptions(Request.GET_ALL, timeout)); // all responses System.out.println("responses:\n" + rsps); for(Map.Entry entry: rsps.entrySet()) { Rsp rsp=entry.getValue(); assertTrue("response from " + entry.getKey() + " was not received", rsp.wasReceived()); assertFalse(rsp.wasSuspected()); } } /** * Test the ability of RpcDispatcher to handle large argument and return values * with unicast RPC calls. * * The test sends requests for return values (byte arrays) having increasing sizes, * which increase the processing time for requests as well as the amount of memory * required to process requests. * * The expected behaviour is that all RPC requests complete successfully. * */ public void testLargeReturnValueUnicastCall() throws Throwable { setProps(c1, c2, c3); for(int i=0; i < SIZES.length; i++) { _testLargeValueUnicastCall(c1.getAddress(), SIZES[i]); } } private static void setProps(JChannel... channels) { for(JChannel ch: channels) { Protocol prot=ch.getProtocolStack().findProtocol("FRAG2"); if(prot != null) { ((FRAG2)prot).setFragSize(12000); } prot=ch.getProtocolStack().findProtocol("FRAG"); if(prot != null) { ((FRAG)prot).setFragSize(12000); } prot=ch.getProtocolStack().getTransport(); if(prot != null) ((TP)prot).setMaxBundleSize(14000); } } /** * Helper method to perform a RPC call on server method "returnValue(int size)" for * all group members. * * The method checks that each returned value is non-null and has the correct size. * */ void _testLargeValue(int size) { // 20 second timeout final long timeout = 20 * 1000 ; System.out.println("\ntesting with " + size + " bytes"); RspList rsps=disp1.callRemoteMethods(null, "largeReturnValue", new Object[]{size}, new Class[]{int.class}, new RequestOptions(Request.GET_ALL, timeout)); System.out.println("rsps:"); assert rsps.size() == 3 : "there should be three responses to the RPC call but only " + rsps.size() + " were received: " + rsps; for(Map.Entry entry: rsps.entrySet()) { // its possible that an exception was raised in processing Object obj = entry.getValue().getValue() ; // this should not happen assert !(obj instanceof Throwable) : "exception was raised in processing reasonably sized argument"; byte[] val=(byte[]) obj; assert val != null; System.out.println(val.length + " bytes from " + entry.getValue().getSender()); assert val.length == size : "return value does not match required size"; } } /** * Helper method to perform a RPC call on server method "returnValue(int size)" for * all group members. * * This method need to take into account that RPC calls can timeout with huge values, * and they can also trigger OOMEs. But if we are lucky, they can also return * reasonable values. * */ void _testHugeValue(int size) { // 20 second timeout final long timeout = 20 * 1000 ; System.out.println("\ntesting with " + size + " bytes"); RspList rsps=disp1.callRemoteMethods(null, "largeReturnValue", new Object[]{size}, new Class[]{int.class}, new RequestOptions(Request.GET_ALL, timeout)); System.out.println("rsps:"); assert rsps != null; assert rsps.size() == 3 : "there should be three responses to the RPC call but only " + rsps.size() + " were received: " + rsps; // in checking the return values, we need to take account of timeouts (i.e. when // a null value is returned) and exceptions for(Map.Entry entry: rsps.entrySet()) { Object obj = entry.getValue().getValue() ; // its possible that an exception was raised if (obj instanceof java.lang.Throwable) { Throwable t = (Throwable) obj ; System.out.println(t.toString() + " exception was raised processing argument from " + entry.getValue().getSender() + " -this is expected") ; continue ; } // its possible that the request timed out before the serve could reply if (obj == null) { System.out.println("request timed out processing argument from " + entry.getValue().getSender() + " - this is expected") ; continue ; } // if we reach here, we sould have a reasobable value byte[] val=(byte[]) obj; System.out.println(val.length + " bytes from " + entry.getValue().getSender()); assert val.length == size : "return value does not match required size"; } } /** * Helper method to perform a RPC call on server method "returnValue(int size)" for * an individual group member. * * The method checks that the returned value is non-null and has the correct size. * * @param dst the group member * @param size the size of the byte array to be returned * @throws Throwable */ void _testLargeValueUnicastCall(Address dst, int size) throws Throwable { // 20 second timeout final long timeout = 20 * 1000 ; System.out.println("\ntesting unicast call with " + size + " bytes"); assertNotNull(dst); Object retval=disp1.callRemoteMethod(dst, "largeReturnValue", new Object[]{size}, new Class[]{int.class}, new RequestOptions(Request.GET_ALL, timeout)); // it's possible that an exception was raised if (retval instanceof java.lang.Throwable) { throw (Throwable)retval; } byte[] val=(byte[])retval; // check value is not null, otherwise fail the test assertNotNull("return value should be non-null", val); System.out.println("rsp: " + val.length + " bytes"); // returned value should have requested size assertEquals("return value does not match requested size", size, val.length); } /** * This class serves as a server obect to turn requests into replies. * It is initialised with an integer id value. * * It implements two functions: * function foo() returns the id of the server * function largeReturnValue(int size) returns a byte array of size 'size' * */ private static class ServerObject { int i; public ServerObject(int i) { this.i=i; } public int foo() {return i;} public static long sleep(long timeout) { // System.out.println("sleep()"); long start=System.currentTimeMillis(); Util.sleep(timeout); return System.currentTimeMillis() - start; } public static byte[] largeReturnValue(int size) { return new byte[size]; } } private static class MyFutureListener implements FutureListener { private boolean done; public void futureDone(Future future) { done=true; } public boolean isDone() {return done;} } }././@LongLink0000000000000000000000000000015600000000000011567 Lustar rootrootlibjgroups-java-2.12.2.Final.orig/tests/junit/org/jgroups/blocks/RpcDispatcherUnicastMethodExceptionTest.javalibjgroups-java-2.12.2.Final.orig/tests/junit/org/jgroups/blocks/RpcDispatcherUnicastMethodException0000644000175000017500000000565611647260573033650 0ustar moellermoellerpackage org.jgroups.blocks; import org.jgroups.Channel; import org.jgroups.Global; import org.jgroups.TimeoutException; import org.jgroups.tests.ChannelTestBase; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; /** * @author Bela Ban */ @Test(groups=Global.STACK_DEPENDENT,sequential=true) public class RpcDispatcherUnicastMethodExceptionTest extends ChannelTestBase { RpcDispatcher disp; Channel channel; @BeforeClass protected void setUp() throws Exception { channel=createChannel(true); disp=new RpcDispatcher(channel, null, null, this); channel.connect(getUniqueClusterName("RpcDispatcherUnicastMethodExceptionTest")); } @AfterClass protected void tearDown() throws Exception { disp.stop(); channel.close(); } @Test(enabled=false) public static Object foo() { return "foo(): OK"; } @Test(enabled=false) public static Object bar() throws Exception { throw new TimeoutException("this is an exception"); } @Test(enabled=false) public static Object foobar() { throw new IllegalArgumentException("bla bla bla from foobar"); } @Test(enabled=false) public static Object foofoobar() { throw new AssertionError("bla bla bla from foofoobar"); } @Test(enabled=false) public static void fooWithThrowable() throws Throwable { throw new Throwable("this is an exception"); } public void testMethodWithoutException() throws Throwable { Object retval=disp.callRemoteMethod(channel.getAddress(), "foo", null, (Class[])null, GroupRequest.GET_ALL, 5000); System.out.println("retval: " + retval); assertNotNull(retval); } @Test(expectedExceptions=TimeoutException.class) public void testMethodWithException() throws Throwable { Object retval=disp.callRemoteMethod(channel.getAddress(), "bar", null, (Class[])null, GroupRequest.GET_ALL, 5000); System.out.println("retval: " + retval); } @Test(expectedExceptions=IllegalArgumentException.class) public void testMethodWithException2() throws Throwable { Object retval=disp.callRemoteMethod(channel.getAddress(), "foobar", null, (Class[])null, GroupRequest.GET_ALL, 5000); System.out.println("retval: " + retval); } @Test(expectedExceptions=AssertionError.class) public void testMethodWithError() throws Throwable { Object retval=disp.callRemoteMethod(channel.getAddress(), "foofoobar", null, (Class[])null, GroupRequest.GET_ALL, 5000); System.out.println("retval: " + retval); } @Test(expectedExceptions=Throwable.class) public void testMethodWithThrowable() throws Throwable { Object retval=disp.callRemoteMethod(channel.getAddress(), "fooWithThrowable", null, (Class[])null, GroupRequest.GET_ALL, 5000); System.out.println("retval: " + retval); } } libjgroups-java-2.12.2.Final.orig/tests/junit/org/jgroups/blocks/RpcDispatcherUnitTest.java0000644000175000017500000001004211647260573031702 0ustar moellermoellerpackage org.jgroups.blocks; import org.jgroups.Global; import org.jgroups.JChannel; import org.jgroups.View; import org.jgroups.Address; import org.jgroups.tests.ChannelTestBase; import org.jgroups.util.*; import org.testng.annotations.*; import java.util.List; import java.util.Arrays; /** * @author Bela Ban */ @Test(groups=Global.STACK_DEPENDENT,sequential=true) public class RpcDispatcherUnitTest extends ChannelTestBase { private RpcDispatcher d1, d2, d3; private JChannel c1, c2, c3; private ServerObject o1, o2, o3; private Address a1, a2, a3; private List
                members; @BeforeClass protected void setUp() throws Exception { o1=new ServerObject(); o2=new ServerObject(); o3=new ServerObject(); c1=createChannel(true, 3); c1.setName("A"); final String GROUP="RpcDispatcherUnitTest"; d1=new RpcDispatcher(c1, null, null, o1); c1.connect(GROUP); c2=createChannel(c1); c2.setName("B"); d2=new RpcDispatcher(c2, null, null, o2); c2.connect(GROUP); c3=createChannel(c1); c3.setName("C"); d3=new RpcDispatcher(c3, null, null, o3); c3.connect(GROUP); System.out.println("c1.view=" + c1.getView() + "\nc2.view=" + c2.getView() + "\nc3.view=" + c3.getView()); View view=c3.getView(); assert view.size() == 3 : "view=" + view; a1=c1.getAddress(); a2=c2.getAddress(); a3=c3.getAddress(); members=Arrays.asList(a1, a2, a3); } @BeforeMethod protected void reset() { o1.reset(); o2.reset(); o3.reset(); } @AfterClass protected void tearDown() throws Exception { d3.stop(); d2.stop(); d1.stop(); Util.close(c3, c2, c1); } public void testInvocationOnEntireGroup() { RspList rsps=d1.callRemoteMethods(null, "foo", null, null, RequestOptions.SYNC()); System.out.println("rsps:\n" + rsps); assert rsps.size() == 3; assert o1.wasCalled() && o2.wasCalled() && o3.wasCalled(); } public void testInvocationOnEntireGroupWithTargetList() { RspList rsps=d1.callRemoteMethods(members, "foo", null, null, RequestOptions.SYNC()); System.out.println("rsps:\n" + rsps); assert rsps.size() == 3; assert o1.wasCalled() && o2.wasCalled() && o3.wasCalled(); } /** Invoke a method on all but myself */ public void testInvocationWithExclusionOfSelf() { RequestOptions options=new RequestOptions(Request.GET_ALL, 5000).setExclusionList(a1); RspList rsps=d1.callRemoteMethods(null, "foo", null, null, options); Util.sleep(500); System.out.println("rsps:\n" + rsps); assert rsps.size() == 2; assert rsps.containsKey(a2) && rsps.containsKey(a3); assert !o1.wasCalled() && o2.wasCalled() && o3.wasCalled(); } public void testInvocationWithExclusionOfTwo() { RequestOptions options=new RequestOptions(Request.GET_ALL, 5000).setExclusionList(a2, a3); RspList rsps=d1.callRemoteMethods(null, "foo", null, null, options); Util.sleep(500); System.out.println("rsps:\n" + rsps); assert rsps.size() == 1; assert rsps.containsKey(a1); assert o1.wasCalled() && !o2.wasCalled() && !o3.wasCalled(); } public void testInvocationOnEmptyTargetSet() { RequestOptions options=new RequestOptions(Request.GET_ALL, 5000).setExclusionList(a1, a2, a3); RspList rsps=d1.callRemoteMethods(null, "foo", null, null, options); Util.sleep(500); System.out.println("rsps:\n" + rsps); assert rsps.isEmpty(); assert !o1.wasCalled() && !o2.wasCalled() && !o3.wasCalled(); } private static class ServerObject { boolean called=false; public boolean wasCalled() { return called; } public void reset() { called=false; } public boolean foo() { called=true; return called; } } }libjgroups-java-2.12.2.Final.orig/tests/junit/org/jgroups/blocks/VotingAdapterTest.java0000644000175000017500000000441711647260573031067 0ustar moellermoellerpackage org.jgroups.blocks; import org.jgroups.Global; import org.jgroups.JChannel; import org.jgroups.util.Util; import org.jgroups.tests.ChannelTestBase; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; @Test(groups=Global.STACK_DEPENDENT) public class VotingAdapterTest extends ChannelTestBase { private JChannel channel1; private JChannel channel2; protected VotingAdapter adapter1; protected VotingAdapter adapter2; protected TestVoteChannelListener listener1; protected TestVoteChannelListener listener2; protected TestVoteChannelListener listener3; protected TestVoteChannelListener listener4; protected static boolean logConfigured=false; @BeforeMethod void setUp() throws Exception { listener1=new TestVoteChannelListener(true); listener2=new TestVoteChannelListener(true); listener3=new TestVoteChannelListener(false); listener4=new TestVoteChannelListener(false); channel1=createChannel(true); adapter1=new VotingAdapter(channel1); channel1.connect("VotingAdapterTest"); channel2=createChannel(channel1); adapter2=new VotingAdapter(channel2); channel2.connect("VotingAdapterTest"); } @AfterMethod void tearDown() throws Exception { Util.close(channel2, channel1); } public void testVoteAll() throws Exception { adapter1.addVoteListener(listener1); adapter2.addVoteListener(listener2); boolean voting1 = adapter1.vote("object1", VotingAdapter.VOTE_ALL, 1000); assert voting1 : "Result of voting1 should be 'true'"; adapter1.addVoteListener(listener3); boolean voting2 = adapter1.vote("object2", VotingAdapter.VOTE_ALL, 1000); assert !voting2 : "Result of voting2 should be 'false'"; } /** * This class always vote according to the parameter passed on the * object creation. */ public static class TestVoteChannelListener implements VotingListener { private boolean vote; public TestVoteChannelListener(boolean vote) { this.vote = vote; } public boolean vote(Object decree) { return vote; } } } libjgroups-java-2.12.2.Final.orig/tests/junit/org/jgroups/protocols/0000755000175000017500000000000011647260573025356 5ustar moellermoellerlibjgroups-java-2.12.2.Final.orig/tests/junit/org/jgroups/protocols/FRAG2_Test.java0000644000175000017500000001376711647260573030037 0ustar moellermoellerpackage org.jgroups.protocols; import org.jgroups.Address; import org.jgroups.Event; import org.jgroups.Global; import org.jgroups.Message; import org.jgroups.View; import org.jgroups.util.Util; import org.jgroups.debug.Simulator; import org.jgroups.stack.IpAddress; import org.jgroups.stack.Protocol; import org.jgroups.tests.ChannelTestBase; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.nio.ByteBuffer; import java.util.Vector; import java.util.concurrent.atomic.AtomicInteger; /** * Tests the fragmentation (FRAG2) protocol for http://jira.jboss.com/jira/browse/JGRP-215 * @author Bela Ban */ @Test(groups={Global.STACK_INDEPENDENT}) public class FRAG2_Test extends ChannelTestBase { private IpAddress a1; private Vector
                members; private View v; private Simulator s=null; private AtomicInteger num_done=new AtomicInteger(0); private Sender[] senders=null; public static final int SIZE=10000; // bytes public static final int NUM_MSGS=10; public static final int NUM_THREADS=100; @BeforeMethod void setUp() throws Exception { 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); FRAG2 frag=new FRAG2(); frag.setFragSize(512); Protocol[] stack=new Protocol[]{frag}; s.setProtocolStack(stack); s.start(); } @AfterMethod void tearDown() throws Exception { s.stop(); } public void testFragmentation() throws InterruptedException { FRAG2_Test.Receiver r=new FRAG2_Test.Receiver(); s.setReceiver(r); senders=new Sender[NUM_THREADS]; for(int i=0; i < senders.length; i++) { senders[i]=new Sender(i); } for(int i=0; i < senders.length; i++) { Sender sender=senders[i]; sender.start(); } for(int i=0; i < senders.length; i++) { Sender sender=senders[i]; sender.join(5000); if(sender.isAlive()) { System.err.println("sender #" + i + " could not be joined (still alive): sender is " + sender); System.out.println("stack trace:\n" + Util.dumpThreads()); } } int sent=0, received=0, corrupted=0; for(int i=0; i < senders.length; i++) { Sender sender=senders[i]; received+=sender.getNumReceived(); sent+=sender.getNumSent(); corrupted+=sender.getNumCorrupted(); } System.out.println("sent: " + sent + ", received: " + received + ", corrupted: " + corrupted); assert sent == received : "sent and received should be the same (sent=" + sent + ", received=" + received + ")"; assert corrupted == 0 : "we should have 0 corrupted messages"; } class Sender extends Thread { int id=-1; int num_sent=0; int num_received=0; int num_corrupted=0; volatile boolean done=false; public int getIdent() { return id; } public int getNumReceived() { return num_received; } public int getNumSent() { return num_sent; } public int getNumCorrupted() { return num_corrupted; } public Sender(int id) { super("sender #" + id); this.id=id; } public void run() { byte[] buf=createBuffer(id); Message msg; Event evt; for(int i=0; i < NUM_MSGS; i++) { msg=new Message(null, null, buf); evt=new Event(Event.MSG, msg); s.send(evt); num_sent++; } synchronized(this) { try { while(!done) this.wait(500); num_done.incrementAndGet(); System.out.println("thread #" + id + " is done (" + num_done.get() + ")"); } catch(InterruptedException e) { } } } private byte[] createBuffer(int id) { ByteBuffer buf=ByteBuffer.allocate(SIZE); int elements=SIZE / Global.INT_SIZE; for(int i=0; i < elements; i++) { buf.putInt(id); } return buf.array(); } /** 1 int has already been read by the Receiver */ public void verify(ByteBuffer buf) { boolean corrupted=false; int num_elements=(SIZE / Global.INT_SIZE) -1; int tmp; for(int i=0; i < num_elements; i++) { tmp=buf.getInt(); if(tmp != id) { corrupted=true; break; } } if(corrupted) num_corrupted++; else num_received++; if(num_corrupted + num_received >= NUM_MSGS) { synchronized(this) { done=true; this.notify(); } } } public String toString() { return id + ": num_sent=" + num_sent + ", num_received=" + num_received + ", done=" + done; } } class Receiver implements Simulator.Receiver { int received=0; public void receive(Event evt) { if(evt.getType() == Event.MSG) { received++; //if(received % 1000 == 0) // System.out.println("<== " + received); Message msg=(Message)evt.getArg(); byte[] data=msg.getBuffer(); ByteBuffer buf=ByteBuffer.wrap(data); int id=buf.getInt(); Sender sender=senders[id]; sender.verify(buf); } } } } libjgroups-java-2.12.2.Final.orig/tests/junit/org/jgroups/protocols/GMS_MergeTest.java0000644000175000017500000007120711647260573030635 0ustar moellermoellerpackage org.jgroups.protocols; import org.jgroups.*; import org.jgroups.conf.ProtocolStackConfigurator; import org.jgroups.conf.ClassConfigurator; import org.jgroups.protocols.pbcast.GMS; import org.jgroups.protocols.pbcast.NAKACK; import org.jgroups.protocols.pbcast.STABLE; import org.jgroups.stack.ProtocolStack; import org.jgroups.tests.ChannelTestBase; import org.jgroups.util.Digest; import org.jgroups.util.MergeId; import org.jgroups.util.Util; import org.testng.annotations.Test; import org.w3c.dom.Element; import java.io.File; import java.net.URL; import java.util.*; import java.util.concurrent.atomic.AtomicInteger; /** * Tests the GMS protocol for merging functionality * @author Bela Ban */ @Test(groups={Global.STACK_INDEPENDENT}, sequential=true) public class GMS_MergeTest extends ChannelTestBase { static final String simple_props="SHARED_LOOPBACK:PING(timeout=1000):" + "pbcast.NAKACK(use_mcast_xmit=false;gc_lag=0;log_discard_msgs=false;log_not_found_msgs=false)" + ":UNICAST:pbcast.STABLE(stability_delay=200):pbcast.GMS:FC:FRAG2"; static final String flush_props=simple_props + ":pbcast.FLUSH"; static final short GMS_ID=ClassConfigurator.getProtocolId(GMS.class); public static void testMergeRequestTimeout() throws Exception { _testMergeRequestTimeout(simple_props, "testMergeRequestTimeout"); } public static void testMergeRequestTimeoutWithFlush() throws Exception { _testMergeRequestTimeout(flush_props, "testMergeRequestTimeoutWithFlush"); } public static void testSimpleMerge() throws Exception { _testSimpleMerge(simple_props, "testSimpleMerge"); } public static void testSimpleMergeWithFlush() throws Exception { _testSimpleMerge(flush_props, "testSimpleMergeWithFlush"); } public static void testConcurrentMergeTwoPartitions() throws Exception { _testConcurrentMergeTwoPartitions(simple_props, "testConcurrentMergeTwoPartitions"); } public static void testConcurrentMergeTwoPartitionsWithFlush() throws Exception { _testConcurrentMergeTwoPartitions(flush_props, "testConcurrentMergeTwoPartitionsWithFlush"); } public static void testConcurrentMergeMultiplePartitions() throws Exception { _testConcurrentMergeMultiplePartitions(simple_props, "testConcurrentMergeMultiplePartitions"); } public static void testConcurrentMergeMultiplePartitionsWithFlush() throws Exception { _testConcurrentMergeMultiplePartitions(flush_props, "testConcurrentMergeMultiplePartitionsWithFlush"); } public static void testMergeAsymmetricPartitions() throws Exception { _testMergeAsymmetricPartitions(simple_props, "testMergeAsymmetricPartitions"); } public static void testMergeAsymmetricPartitionsWithFlush() throws Exception { _testMergeAsymmetricPartitions(flush_props, "testMergeAsymmetricPartitionsWithFlush"); } public static void testMergeAsymmetricPartitions2() throws Exception { _testMergeAsymmetricPartitions2(simple_props, "testMergeAsymmetricPartitions2"); } public static void testMergeAsymmetricPartitionsWithFlush2() throws Exception { _testMergeAsymmetricPartitions2(flush_props, "testMergeAsymmetricPartitionsWithFlush2"); } /** * Simulates the death of a merge leader after having sent a MERG_REQ. Because there is no MergeView or CANCEL_MERGE * message, the MergeCanceller has to null merge_id after a timeout */ static void _testMergeRequestTimeout(String props, String cluster_name) throws Exception { JChannel c1=new JChannel(props); try { c1.connect(cluster_name); Message merge_request=new Message(); GMS.GmsHeader hdr=new GMS.GmsHeader(GMS.GmsHeader.MERGE_REQ); MergeId new_merge_id=MergeId.create(c1.getAddress()); hdr.setMergeId(new_merge_id); merge_request.putHeader(GMS_ID, hdr); GMS gms=(GMS)c1.getProtocolStack().findProtocol(GMS.class); gms.setMergeTimeout(2000); MergeId merge_id=gms.getMergeId(); assert merge_id == null; System.out.println("starting merge"); gms.up(new Event(Event.MSG, merge_request)); merge_id=gms.getMergeId(); System.out.println("merge_id = " + merge_id); assert new_merge_id.equals(merge_id); long timeout=gms.getMergeTimeout() * 2; System.out.println("sleeping for " + timeout + " ms, then fetching merge_id: should be null (cancelled by the MergeCanceller)"); long target_time=System.currentTimeMillis() + timeout; while(System.currentTimeMillis() < target_time) { merge_id=gms.getMergeId(); if(merge_id == null) break; Util.sleep(500); } merge_id=gms.getMergeId(); System.out.println("merge_id = " + merge_id); assert merge_id == null : "MergeCanceller didn't kick in"; } finally { Util.close(c1); } } static void _testSimpleMerge(String props, String cluster_name) throws Exception { JChannel[] channels=null; try { channels=create(props, true, cluster_name, "A", "B", "C", "D"); print(channels); View view=channels[channels.length -1].getView(); assert view.size() == channels.length : "view is " + view; System.out.println("\ncreating partitions: "); String[][] partitions=generate(new String[]{"A", "B"}, new String[]{"C", "D"}); createPartitions(channels, partitions); print(channels); checkViews(channels, "A", "A", "B"); checkViews(channels, "B", "A", "B"); checkViews(channels, "C", "C", "D"); checkViews(channels, "C", "C", "D"); System.out.println("\ndigests:"); printDigests(channels); Address leader=determineLeader(channels, "A", "C"); long end_time=System.currentTimeMillis() + 30000; do { System.out.println("\n==== injecting merge events into " + leader + " ===="); injectMergeEvent(channels, leader, "A", "C"); Util.sleep(1000); if(allChannelsHaveViewOf(channels, channels.length)) break; } while(end_time > System.currentTimeMillis()); System.out.println("\n"); print(channels); assertAllChannelsHaveViewOf(channels, channels.length); System.out.println("\ndigests:"); printDigests(channels); } finally { System.out.println("closing channels"); close(channels); System.out.println("done"); } } static void _testConcurrentMergeTwoPartitions(String props, String cluster_name) throws Exception { JChannel[] channels=null; try { channels=create(props, true, cluster_name, "A", "B", "C", "D"); print(channels); View view=channels[channels.length -1].getView(); assert view.size() == channels.length : "view is " + view; System.out.println("\ncreating partitions: "); String[][] partitions=generate(new String[]{"A", "B"}, new String[]{"C", "D"}); createPartitions(channels, partitions); print(channels); checkViews(channels, "A", "A", "B"); checkViews(channels, "B", "A", "B"); checkViews(channels, "C", "C", "D"); checkViews(channels, "D", "C", "D"); long end_time=System.currentTimeMillis() + 30000; do { System.out.println("\n==== injecting merge events into A and C concurrently ===="); injectMergeEvent(channels, "C", "A", "C"); injectMergeEvent(channels, "A", "A", "C"); Util.sleep(1000); if(allChannelsHaveViewOf(channels, 4)) break; } while(end_time > System.currentTimeMillis()); System.out.println("\n"); print(channels); assertAllChannelsHaveViewOf(channels, 4); } finally { close(channels); } } static void _testConcurrentMergeMultiplePartitions(String props, String cluster_name) throws Exception { JChannel[] channels=null; try { channels=create(props, true, cluster_name, "A", "B", "C", "D", "E", "F", "G", "H"); print(channels); View view=channels[channels.length -1].getView(); assert view.size() == channels.length : "view is " + view; assertAllChannelsHaveViewOf(channels, 8); System.out.println("\ncreating partitions: "); String[][] partitions=generate(new String[]{"A", "B"}, new String[]{"C", "D"}, new String[]{"E", "F"}, new String[]{"G", "H"}); createPartitions(channels, partitions); print(channels); checkViews(channels, "A", "A", "B"); checkViews(channels, "B", "A", "B"); checkViews(channels, "C", "C", "D"); checkViews(channels, "D", "C", "D"); checkViews(channels, "E", "E", "F"); checkViews(channels, "F", "E", "F"); checkViews(channels, "G", "G", "H"); checkViews(channels, "H", "G", "H"); long end_time=System.currentTimeMillis() + 30000; do { System.out.println("\n==== injecting merge event into A, C, E and G concurrently ===="); injectMergeEvent(channels, "G", "A", "C", "E", "G"); injectMergeEvent(channels, "E", "A", "C", "E", "G"); injectMergeEvent(channels, "A", "A", "C", "E", "G"); injectMergeEvent(channels, "C", "A", "C", "E", "G"); Util.sleep(1000); if(allChannelsHaveViewOf(channels, 8)) break; } while(end_time > System.currentTimeMillis()); print(channels); assertAllChannelsHaveViewOf(channels, 8); } finally { close(channels); } } /** * Tests the merge of the following partitions: *
                  *
                • A: {B, A, C} *
                • B: {B, C} *
                • C: {B, C} * * JIRA: https://jira.jboss.org/jira/browse/JGRP-1031 * @throws Exception */ static void _testMergeAsymmetricPartitions(String props, String cluster_name) throws Exception { JChannel[] channels=null; MyReceiver[] receivers; final int NUM=10; try { // use simple IDs for UUIDs, so sorting on merge will NOT change the view order channels=create(props, true, cluster_name, "B", "A", "C"); receivers=new MyReceiver[channels.length]; for(int i=0; i < channels.length; i++) { receivers[i]=new MyReceiver(channels[i].getName()); channels[i].setReceiver(receivers[i]); } JChannel a=findChannel("A", channels), b=findChannel("B", channels), c=findChannel("C", channels); print(channels); View view=channels[channels.length -1].getView(); assert view.size() == channels.length : "view is " + view; System.out.println("sending " + NUM + " msgs:"); for(int i=0; i < NUM; i++) for(JChannel ch: channels) ch.send(null, null, "Number #" + i + " from " + ch.getAddress()); waitForNumMessages(NUM * channels.length, 10000, 1000, receivers); checkMessages(NUM * channels.length, receivers); System.out.println("\ncreating partitions: "); applyView(channels, "A", "B", "A", "C"); applyView(channels, "B", "B", "C"); applyView(channels, "C", "B", "C"); print(channels); checkViews(channels, "A", "B", "A", "C"); checkViews(channels, "B", "B", "C"); checkViews(channels, "C", "B", "C"); for(MyReceiver receiver: receivers) receiver.clear(); DISCARD discard=new DISCARD(); discard.addIgnoreMember(b.getAddress()); discard.addIgnoreMember(c.getAddress()); // A should drop all traffic from B or C a.getProtocolStack().insertProtocol(discard, ProtocolStack.ABOVE, SHARED_LOOPBACK.class); System.out.println("B and C exchange " + NUM + " messages, A discards them"); for(int i=0; i < NUM; i++) b.send(null, null, "message #" + i +" from B"); for(int i=0; i < NUM; i++) c.send(null, null, "message #" + i +" from C"); waitForNumMessages(NUM * 2, 10000, 500, receivers[0], receivers[2]); // A *does* receiver B's and C's messages ! checkMessages(NUM * 2, receivers[0], receivers[2]); checkMessages(0, receivers[1]); Digest da=((NAKACK)a.getProtocolStack().findProtocol(NAKACK.class)).getDigest(), db=((NAKACK)b.getProtocolStack().findProtocol(NAKACK.class)).getDigest(), dc=((NAKACK)c.getProtocolStack().findProtocol(NAKACK.class)).getDigest(); System.out.println("Digest A: " + da + "\nDigest B: " + db + "\nDigest C: " + dc); System.out.println("Running stability protocol on B and C now"); // a.getProtocolStack().findProtocol(STABLE.class).setLevel("trace"); // b.getProtocolStack().findProtocol(STABLE.class).setLevel("trace"); // c.getProtocolStack().findProtocol(STABLE.class).setLevel("trace"); for(int i=0; i < 3; i++) { ((STABLE)b.getProtocolStack().findProtocol(STABLE.class)).runMessageGarbageCollection(); ((STABLE)c.getProtocolStack().findProtocol(STABLE.class)).runMessageGarbageCollection(); Util.sleep(300); } db=((NAKACK)a.getProtocolStack().findProtocol(NAKACK.class)).getDigest(); db=((NAKACK)b.getProtocolStack().findProtocol(NAKACK.class)).getDigest(); dc=((NAKACK)c.getProtocolStack().findProtocol(NAKACK.class)).getDigest(); System.out.println("(after purging)\nDigest A: " + da + "\nDigest B: " + db + "\nDigest C: " + dc); // now enable traffic reception of B and C on A: discard.removeIgnoredMember(b.getAddress()); discard.removeIgnoredMember(c.getAddress()); Address leader=b.getAddress(); long end_time=System.currentTimeMillis() + 12000; do { System.out.println("\n==== injecting merge event into " + leader + " ===="); injectMergeEvent(channels, leader, "B", "A", "C"); Util.sleep(3000); if(allChannelsHaveView(channels, b.getView())) break; } while(end_time > System.currentTimeMillis()); System.out.println("\n"); print(channels); assertAllChannelsHaveView(channels, b.getView()); } finally { close(channels); } } /** * Tests the merge of the following partitions: *
                    *
                  • A: {A,B} *
                  • B: {B} * * JIRA: https://jira.jboss.org/jira/browse/JGRP-1031 * @throws Exception */ static void _testMergeAsymmetricPartitions2(String props, String cluster_name) throws Exception { JChannel[] channels=null; MyReceiver[] receivers; final int NUM=10; try { // use simple IDs for UUIDs, so sorting on merge will NOT change the view order channels=create(props, true, cluster_name, "A", "B"); receivers=new MyReceiver[channels.length]; for(int i=0; i < channels.length; i++) { receivers[i]=new MyReceiver(channels[i].getName()); channels[i].setReceiver(receivers[i]); } JChannel a=findChannel("A", channels), b=findChannel("B", channels); print(channels); View view=channels[channels.length -1].getView(); assert view.size() == channels.length : "view is " + view; System.out.println("sending " + NUM + " msgs:"); for(int i=0; i < NUM; i++) for(JChannel ch: channels) ch.send(null, null, "Number #" + i + " from " + ch.getAddress()); waitForNumMessages(NUM * channels.length, 10000, 1000, receivers); checkMessages(NUM * channels.length, receivers); System.out.println("\ncreating partitions: "); applyView(channels, "B", "B"); // B has view {B} print(channels); checkViews(channels, "A", "A", "B"); // A: {A,B} checkViews(channels, "B", "B"); // B: {B} for(MyReceiver receiver: receivers) receiver.clear(); Digest da=((NAKACK)a.getProtocolStack().findProtocol(NAKACK.class)).getDigest(); Digest db=((NAKACK)b.getProtocolStack().findProtocol(NAKACK.class)).getDigest(); System.out.println("(after purging)\nDigest A: " + da + "\nDigest B: " + db); long end_time=System.currentTimeMillis() + 12000; do { System.out.println("\n==== injecting merge event ===="); injectMergeEvent(channels, a.getAddress(), "A", "B"); injectMergeEvent(channels, b.getAddress(), "A", "B"); Util.sleep(3000); if(allChannelsHaveView(channels, a.getView())) break; } while(end_time > System.currentTimeMillis()); /* long end_time=System.currentTimeMillis() + 12000; do { if(allChannelsHaveView(channels, a.getView())) break; Util.sleep(3000); } while(end_time > System.currentTimeMillis());*/ System.out.println("\n"); print(channels); assertAllChannelsHaveView(channels, a.getView()); } finally { close(channels); } } /** * First name is the channel name, the rest is the view to be applied * @param members */ private static void applyView(JChannel[] channels, String member, String ... members) throws Exception { JChannel ch=findChannel(member, channels); View view=createView(members, channels); GMS gms=(GMS)ch.getProtocolStack().findProtocol(GMS.class); gms.installView(view); } private static boolean allChannelsHaveViewOf(JChannel[] channels, int count) { for(JChannel ch: channels) { if(ch.getView().size() != count) return false; } return true; } private static boolean allChannelsHaveView(JChannel[] channels, View view) { for(JChannel ch: channels) { if(!ch.getView().equals(view)) return false; } return true; } private static void assertAllChannelsHaveView(JChannel[] channels, View view) { for(JChannel ch: channels) { View v=ch.getView(); assert v.equals(view) : "expected view " + view + " but got " + v + " for channel " + ch.getName(); } } private static void assertAllChannelsHaveViewOf(JChannel[] channels, int count) { for(JChannel ch: channels) assert ch.getView().size() == count : ch.getName() + " has view " + ch.getView() + " (should have " + count + " mbrs)"; } private static void close(JChannel[] channels) { if(channels == null) return; for(int i=channels.length -1; i >= 0; i--) { JChannel ch=channels[i]; Util.close(ch); } } /*private static JChannel[] create(String cluster_name, String ... names) throws Exception { return create(false, cluster_name, names); }*/ private static JChannel[] create(String props, boolean simple_ids, String cluster_name, String ... names) throws Exception { JChannel[] retval=new JChannel[names.length]; for(int i=0; i < retval.length; i++) { JChannel ch; if(simple_ids) { ch=new MyChannel(props); ((MyChannel)ch).setId(i+1); } else ch=new JChannel(props); ch.setName(names[i]); retval[i]=ch; ch.connect(cluster_name); if(i == 0) Util.sleep(3000); } return retval; } private static void createPartitions(JChannel[] channels, String[]... partitions) throws Exception { checkUniqueness(partitions); List views=new ArrayList(partitions.length); for(String[] partition: partitions) { View view=createView(partition, channels); views.add(view); } applyViews(views, channels); } private static void injectMergeEvent(JChannel[] channels, String leader, String ... coordinators) { Address leader_addr=leader != null? findAddress(leader, channels) : determineLeader(channels); injectMergeEvent(channels, leader_addr, coordinators); } private static void injectMergeEvent(JChannel[] channels, Address leader_addr, String ... coordinators) { Map views=new HashMap(); for(String tmp: coordinators) { Address coord=findAddress(tmp, channels); views.put(coord, findView(tmp, channels)); } JChannel coord=findChannel(leader_addr, channels); GMS gms=(GMS)coord.getProtocolStack().findProtocol(GMS.class); gms.setLevel("trace"); gms.up(new Event(Event.MERGE, views)); } private static Address determineLeader(JChannel[] channels, String ... coords) { Membership membership=new Membership(); for(String coord: coords) membership.add(findAddress(coord, channels)); membership.sort(); return membership.elementAt(0); } private static String[][] generate(String[] ... partitions) { String[][] retval=new String[partitions.length][]; System.arraycopy(partitions, 0, retval, 0, partitions.length); return retval; } private static void checkUniqueness(String[] ... partitions) throws Exception { Set set=new HashSet(); for(String[] partition: partitions) { for(String tmp: partition) { if(!set.add(tmp)) throw new Exception("partitions are overlapping: element " + tmp + " is in multiple partitions"); } } } private static View createView(String[] partition, JChannel[] channels) throws Exception { Vector
                    members=new Vector
                    (partition.length); for(String tmp: partition) { Address addr=findAddress(tmp, channels); if(addr == null) throw new Exception(tmp + " not associated with a channel"); members.add(addr); } return new View(members.firstElement(), 10, members); } private static void checkViews(JChannel[] channels, String channel_name, String ... members) { JChannel ch=findChannel(channel_name, channels); View view=ch.getView(); assert view.size() == members.length : "view is " + view + ", members: " + Arrays.toString(members); for(String member: members) { Address addr=findAddress(member, channels); assert view.getMembers().contains(addr) : "view " + view + " does not contain " + addr; } } private static JChannel findChannel(String tmp, JChannel[] channels) { for(JChannel ch: channels) { if(ch.getName().equals(tmp)) return ch; } return null; } private static JChannel findChannel(Address addr, JChannel[] channels) { for(JChannel ch: channels) { if(ch.getAddress().equals(addr)) return ch; } return null; } private static Address findAddress(String tmp, JChannel[] channels) { for(JChannel ch: channels) { if(ch.getName().equals(tmp)) return ch.getAddress(); } return null; } private static View findView(String tmp, JChannel[] channels) { for(JChannel ch: channels) { if(ch.getName().equals(tmp)) return ch.getView(); } return null; } private static void applyViews(List views, JChannel[] channels) { for(View view: views) { Collection
                    members=view.getMembers(); for(JChannel ch: channels) { Address addr=ch.getAddress(); if(members.contains(addr)) { GMS gms=(GMS)ch.getProtocolStack().findProtocol(GMS.class); gms.installView(view); } } } } private static void print(JChannel[] channels) { for(JChannel ch: channels) { System.out.println(ch.getName() + ": " + ch.getView()); } } private static void printDigests(JChannel[] channels) { for(JChannel ch: channels) { NAKACK nak=(NAKACK)ch.getProtocolStack().findProtocol(NAKACK.class); Digest digest=nak.getDigest(); System.out.println(ch.getName() + ": " + digest.toStringSorted()); } } static void waitForNumMessages(int num_msgs, long timeout, long interval, MyReceiver ... receivers) { long target_time=System.currentTimeMillis() + timeout; while(System.currentTimeMillis() < target_time) { boolean all_received=true; for(MyReceiver receiver: receivers) { if(receiver.getNumMsgs() < num_msgs) { all_received=false; break; } } if(all_received) break; Util.sleep(interval); } } static void checkMessages(int expected, MyReceiver ... receivers) { for(MyReceiver receiver: receivers) System.out.println(receiver.name + ": " + receiver.getNumMsgs()); for(MyReceiver receiver: receivers) assert receiver.getNumMsgs() == expected : "[" + receiver.name + "] expected " + expected + " msgs, but received " + receiver.getNumMsgs(); } private static class MyChannel extends JChannel { protected int id=0; public MyChannel() throws ChannelException { super(); } public MyChannel(File properties) throws ChannelException { super(properties); } public MyChannel(Element properties) throws ChannelException { super(properties); } public MyChannel(URL properties) throws ChannelException { super(properties); } public MyChannel(String properties) throws ChannelException { super(properties); } public MyChannel(ProtocolStackConfigurator configurator) throws ChannelException { super(configurator); } public MyChannel(JChannel ch) throws ChannelException { super(ch); } public void setId(int id) { this.id=id; } protected void setAddress() { Address old_addr=local_addr; local_addr=new org.jgroups.util.UUID(id, id); if(old_addr != null) down(new Event(Event.REMOVE_ADDRESS, old_addr)); if(name == null || name.length() == 0) // generate a logical name if not set name=Util.generateLocalName(); if(name != null && name.length() > 0) org.jgroups.util.UUID.add(local_addr, name); Event evt=new Event(Event.SET_LOCAL_ADDRESS, local_addr); down(evt); if(up_handler != null) up_handler.up(evt); } } private static class MyReceiver extends ReceiverAdapter { private final String name; private AtomicInteger num_msgs=new AtomicInteger(0); public MyReceiver(String name) { this.name=name; } public int getNumMsgs() {return num_msgs.get();} public void clear() {num_msgs.set(0);} public void receive(Message msg) { num_msgs.incrementAndGet(); } public void viewAccepted(View new_view) { System.out.println("[" + name + "] view=" + new_view); } } }libjgroups-java-2.12.2.Final.orig/tests/junit/org/jgroups/protocols/S3_PINGTest.java0000644000175000017500000001357211647260573030173 0ustar moellermoellerpackage org.jgroups.protocols; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import org.jgroups.Global; import org.jgroups.protocols.S3_PING.Utils; import org.testng.Assert; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; @Test(groups={Global.STACK_INDEPENDENT}) public class S3_PINGTest { private S3_PING ping; @BeforeMethod public void setUp() { ping = new S3_PING(); } @Test(expectedExceptions = IllegalArgumentException.class) public void testValidatePropertiesWithPreSignedPutSet() { ping.pre_signed_put_url = "http://s3.amazonaws.com/test-bucket/node1"; ping.validateProperties(); } @Test(expectedExceptions = IllegalArgumentException.class) public void testValidatePropertiesWithPreSignedDeleteSet() { ping.pre_signed_delete_url = "http://s3.amazonaws.com/test-bucket/node1"; ping.validateProperties(); } @Test(expectedExceptions = IllegalArgumentException.class) public void testValidatePropertiesWithBothPreSignedSetButNoBucket() { ping.pre_signed_put_url = "http://s3.amazonaws.com/"; ping.pre_signed_delete_url = "http://s3.amazonaws.com/"; ping.validateProperties(); } @Test(expectedExceptions = IllegalArgumentException.class) public void testValidatePropertiesWithBothPreSignedSetButNoFile() { ping.pre_signed_put_url = "http://s3.amazonaws.com/test-bucket"; ping.pre_signed_delete_url = "http://s3.amazonaws.com/test-bucket"; ping.validateProperties(); } @Test(expectedExceptions = IllegalArgumentException.class) public void testValidatePropertiesWithBothPreSignedSetButTooManySubdirectories() { ping.pre_signed_put_url = "http://s3.amazonaws.com/test-bucket/subdir/DemoCluster/node1"; ping.pre_signed_delete_url = "http://s3.amazonaws.com/test-bucket/subdir/DemoCluster/node1"; ping.validateProperties(); } @Test public void testValidatePropertiesWithBothPreSignedSetToValid() { ping.pre_signed_put_url = "http://s3.amazonaws.com/test-bucket/node1"; ping.pre_signed_delete_url = "http://s3.amazonaws.com/test-bucket/node1"; ping.validateProperties(); } @Test public void testValidatePropertiesWithBothPreSignedSetToValidSubdirectory() { ping.pre_signed_put_url = "http://s3.amazonaws.com/test-bucket/DemoCluster/node1"; ping.pre_signed_delete_url = "http://s3.amazonaws.com/test-bucket/DemoCluster/node1"; ping.validateProperties(); } @Test public void testUsingPreSignedUrlWhenNotSet() { Assert.assertFalse(ping.usingPreSignedUrls()); } @Test public void testUsingPreSignedUrlWhenSet() { ping.pre_signed_put_url = "http://s3.amazonaws.com/test-bucket/node1"; Assert.assertTrue(ping.usingPreSignedUrls()); } @Test public void testGenerateQueryStringAuthenticationWithBasicGet() { String expectedUrl = "http://s3.amazonaws.com/test-bucket/node1?AWSAccessKeyId=abcd&Expires=1234567890&Signature=Khyk4bU1A3vaed9woyp%2B5qepazQ%3D"; String encodedUrl = Utils.generateQueryStringAuthentication("abcd", "efgh", "get", "test-bucket", "node1", new HashMap(), new HashMap(), 1234567890); Assert.assertEquals(encodedUrl, expectedUrl); } @Test public void testGenerateQueryStringAuthenticationWithBasicPost() { String expectedUrl = "http://s3.amazonaws.com/test-bucket/node1?AWSAccessKeyId=abcd&Expires=1234567890&Signature=%2BsCW1Fc20UUvIqPjeGXkyN960sk%3D"; String encodedUrl = Utils.generateQueryStringAuthentication("abcd", "efgh", "POST", "test-bucket", "node1", new HashMap(), new HashMap(), 1234567890); Assert.assertEquals(encodedUrl, expectedUrl); } @Test public void testGenerateQueryStringAuthenticationWithBasicPutAndHeaders() { Map headers = new HashMap(); headers.put("x-amz-acl", Arrays.asList("public-read")); String expectedUrl = "http://s3.amazonaws.com/test-bucket/subdir/node1?AWSAccessKeyId=abcd&Expires=1234567890&Signature=GWu2Mm5MysW83YDgS2R0Jakthes%3D"; String encodedUrl = Utils.generateQueryStringAuthentication("abcd", "efgh", "put", "test-bucket", "subdir/node1", new HashMap(), headers, 1234567890); Assert.assertEquals(encodedUrl, expectedUrl); } @Test public void testGeneratePreSignedUrlForPut() { String expectedUrl = "http://s3.amazonaws.com/test-bucket/subdir/node1?AWSAccessKeyId=abcd&Expires=1234567890&Signature=GWu2Mm5MysW83YDgS2R0Jakthes%3D"; String preSignedUrl = S3_PING.generatePreSignedUrl("abcd", "efgh", "put", "test-bucket", "subdir/node1", 1234567890); Assert.assertEquals(preSignedUrl, expectedUrl); } @Test public void testGeneratePreSignedUrlForDelete() { String expectedUrl = "http://s3.amazonaws.com/test-bucket/subdir/node1?AWSAccessKeyId=abcd&Expires=1234567890&Signature=qbEMukqq0KIpZVjXaDi0VxepSVo%3D"; String preSignedUrl = S3_PING.generatePreSignedUrl("abcd", "efgh", "delete", "test-bucket", "subdir/node1", 1234567890); Assert.assertEquals(preSignedUrl, expectedUrl); } }libjgroups-java-2.12.2.Final.orig/tests/junit/org/jgroups/protocols/STATE_TRANSFER_Test.java0000644000175000017500000001325011647260573031405 0ustar moellermoellerpackage org.jgroups.protocols; import static java.util.concurrent.TimeUnit.SECONDS; import org.jgroups.*; import org.jgroups.tests.ChannelTestBase; import org.jgroups.util.Util; import org.testng.annotations.Test; import org.testng.annotations.BeforeMethod; import org.testng.annotations.AfterMethod; /** * It's an attemp to setup Junit test case template for Protocol regression.

                    * Two "processes" are started, and the coord. keeps sending msg of a counter. The 2nd * process joins the grp and get the state from the coordinator. The subsequent msgs * after the setState will be validated to ensure the total ordering of msg delivery.

                    * This should cover the fix introduced by rev. 1.12 * @author Wenbo Zhu */ @Test(groups={Global.STACK_DEPENDENT,"known-failures"}) public class STATE_TRANSFER_Test extends ChannelTestBase { public static final String GROUP_NAME="STATE_TRANSFER_Test"; private Coordinator coord; @BeforeMethod protected void setUp() throws Exception { coord=new Coordinator(); coord.recvLoop(); coord.sendLoop(); } @AfterMethod protected void tearDown() throws Exception { coord.stop(); coord=null; } class Coordinator extends ChannelListenerAdapter { private JChannel channel=null; private int cnt=0; // the state private volatile boolean closed=false; String getProps() { return channel.getProperties(); } public JChannel getChannel() { return channel; } protected Coordinator() throws Exception { channel=createChannel(true); channel.setOpt(Channel.LOCAL, Boolean.FALSE); channel.addChannelListener(this); channel.connect(GROUP_NAME); } public void recvLoop() throws Exception { Thread task=new Thread(new Runnable() { public void run() { Object tmp; while(!closed) { try { tmp=channel.receive(0); if(tmp instanceof GetStateEvent) { synchronized(Coordinator.this) { // System.err.println("-- GetStateEvent, cnt=" + cnt); channel.returnState(Util.objectToByteBuffer(new Integer(cnt))); } } } catch(ChannelNotConnectedException not) { break; } catch(ChannelClosedException closed) { break; } catch(Exception e) { System.err.println(e); } } } }); task.start(); } public void sendLoop() throws Exception { Thread task=new Thread(new Runnable() { public void run() { while(!closed) { try { synchronized(Coordinator.this) { channel.send(null, null, new Integer(++cnt)); // System.err.println("send cnt=" + cnt); } Thread.sleep(1000); } catch(ChannelNotConnectedException not) { break; } catch(ChannelClosedException closed) { break; } catch(Exception e) { System.err.println(e); } } } }); task.start(); } public void stop() { closed=true; channel.close(); } } public void testBasicStateSync() throws Exception { Channel channel= null; int timeout=60; //seconds int counter=0; try { channel = createChannel(coord.getChannel()); channel.setOpt(Channel.LOCAL, Boolean.FALSE); channel.connect(GROUP_NAME); Thread.sleep(1000); boolean join=false; join=channel.getState(null, 100000l); assertTrue(join); Object tmp; int cnt=-1; for(;counter < timeout;SECONDS.sleep(1),counter++) { try { tmp=channel.receive(0); if(tmp instanceof SetStateEvent) { cnt=((Integer)Util.objectFromByteBuffer(((SetStateEvent)tmp).getArg())).intValue(); // System.err.println("-- SetStateEvent, cnt=" + cnt); continue; } if(tmp instanceof Message) { if(cnt != -1) { int msg=((Integer)((Message)tmp).getObject()).intValue(); assertEquals(cnt, msg - 1); break; // done } } } catch(ChannelNotConnectedException not) { break; } catch(ChannelClosedException closed) { break; } catch(Exception e) { System.err.println(e); } } } finally { channel.close(); assertTrue("Timeout reached", counter < timeout); } } } libjgroups-java-2.12.2.Final.orig/tests/junit/org/jgroups/protocols/UNICAST_ContentionTest.java0000644000175000017500000001514511647260573032375 0ustar moellermoellerpackage org.jgroups.protocols; import org.jgroups.*; import org.jgroups.stack.Protocol; import org.jgroups.util.Util; import org.testng.annotations.Test; import org.testng.annotations.AfterMethod; import org.testng.annotations.DataProvider; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicInteger; /** * Tests for contention on UNICAST, measured by the number of retransmissions in UNICAST * @author Bela Ban */ @Test(groups=Global.STACK_INDEPENDENT, sequential=true) public class UNICAST_ContentionTest { JChannel c1, c2; static final String unicast_props="SHARED_LOOPBACK(thread_pool.queue_max_size=5000;" + "thread_pool.rejection_policy=discard;thread_pool.min_threads=20;thread_pool.max_threads=20;" + "oob_thread_pool.rejection_policy=discard;enable_bundling=true)"+ ":UNICAST(timeout=300,600,1200)"; static final String unicast2_props=unicast_props.replace("UNICAST", "UNICAST2"); static final int NUM_THREADS=200; static final int NUM_MSGS=100; static final int SIZE=1000; // default size of a message in bytes @AfterMethod protected void tearDown() throws Exception { Util.close(c2, c1); } @Test(dataProvider="provider") public void testSimpleMessageReception(String props) throws Exception { c1=new JChannel(props); c2=new JChannel(props); MyReceiver r1=new MyReceiver("c1"), r2=new MyReceiver("c2"); c1.setReceiver(r1); c2.setReceiver(r2); c1.connect("testSimpleMessageReception"); c2.connect("testSimpleMessageReception"); int NUM=100; Address c1_addr=c1.getLocalAddress(), c2_addr=c2.getLocalAddress(); for(int i=1; i <= NUM; i++) { c1.send(c1_addr, null, "bla"); c1.send(c2_addr, null, "bla"); c2.send(c2_addr, null, "bla"); c2.send(c1_addr, null, "bla"); } for(int i=0; i < 10; i++) { if(r1.getNum() == NUM * 2 && r2.getNum() == NUM * 2) break; Util.sleep(500); } System.out.println("c1 received " + r1.getNum() + " msgs, " + getNumberOfRetransmissions(c1) + " retransmissions"); System.out.println("c2 received " + r2.getNum() + " msgs, " + getNumberOfRetransmissions(c2) + " retransmissions"); assert r1.getNum() == NUM * 2: "expected " + NUM *2 + ", but got " + r1.getNum(); assert r2.getNum() == NUM * 2: "expected " + NUM *2 + ", but got " + r2.getNum(); } /** * Multiple threads (NUM_THREADS) send messages (NUM_MSGS) * @throws Exception */ @Test(dataProvider="provider") public void testMessageReceptionUnderHighLoad(String props) throws Exception { CountDownLatch latch=new CountDownLatch(1); c1=new JChannel(props); c2=new JChannel(props); MyReceiver r1=new MyReceiver("c1"), r2=new MyReceiver("c2"); c1.setReceiver(r1); c2.setReceiver(r2); c1.connect("testSimpleMessageReception"); c2.connect("testSimpleMessageReception"); Address c1_addr=c1.getLocalAddress(), c2_addr=c2.getLocalAddress(); MySender[] c1_senders=new MySender[NUM_THREADS]; for(int i=0; i < c1_senders.length; i++) { c1_senders[i]=new MySender(c1, c2_addr, latch); c1_senders[i].start(); } MySender[] c2_senders=new MySender[NUM_THREADS]; for(int i=0; i < c2_senders.length; i++) { c2_senders[i]=new MySender(c2, c1_addr, latch); c2_senders[i].start(); } Util.sleep(500); latch.countDown(); // starts all threads long NUM_EXPECTED_MSGS=NUM_THREADS * NUM_MSGS; for(int i=0; i < 100; i++) { if(r1.getNum() == NUM_EXPECTED_MSGS && r2.getNum() == NUM_EXPECTED_MSGS) break; Util.sleep(500); UNICAST2 unicast2=(UNICAST2)c1.getProtocolStack().findProtocol(UNICAST2.class); if(unicast2 != null) unicast2.sendStableMessages(); unicast2=(UNICAST2)c2.getProtocolStack().findProtocol(UNICAST2.class); if(unicast2 != null) unicast2.sendStableMessages(); } System.out.println("c1 received " + r1.getNum() + " msgs, " + getNumberOfRetransmissions(c1) + " retransmissions"); System.out.println("c2 received " + r2.getNum() + " msgs, " + getNumberOfRetransmissions(c2) + " retransmissions"); assert r1.getNum() == NUM_EXPECTED_MSGS : "expected " + NUM_EXPECTED_MSGS + ", but got " + r1.getNum(); assert r2.getNum() == NUM_EXPECTED_MSGS : "expected " + NUM_EXPECTED_MSGS + ", but got " + r2.getNum(); } @DataProvider public static Object[][] provider() { return new Object[][] { {unicast_props}, {unicast2_props} }; } private static long getNumberOfRetransmissions(JChannel ch) { Protocol prot=ch.getProtocolStack().findProtocol(UNICAST.class, UNICAST2.class); if(prot instanceof UNICAST) return ((UNICAST)prot).getNumberOfRetransmissions(); if(prot instanceof UNICAST2) return ((UNICAST2)prot).getNumberOfRetransmissions(); return -1; } private static class MySender extends Thread { private final JChannel ch; private final Address dest; private final CountDownLatch latch; private final byte[] buf=new byte[SIZE]; public MySender(JChannel ch, Address dest, CountDownLatch latch) { this.ch=ch; this.dest=dest; this.latch=latch; } public void run() { try { latch.await(); } catch(InterruptedException e) { e.printStackTrace(); } for(int i=0; i < NUM_MSGS; i++) { try { Message msg=new Message(dest, null, buf); ch.send(msg); } catch(Exception e) { e.printStackTrace(); } } } } private static class MyReceiver extends ReceiverAdapter { final String name; final AtomicInteger num=new AtomicInteger(0); static final long MOD=NUM_MSGS * NUM_THREADS / 10; public MyReceiver(String name) { this.name=name; } public void receive(Message msg) { if(num.incrementAndGet() % MOD == 0) { System.out.println("[" + name + "] received " + getNum() + " msgs"); } } public int getNum() { return num.get(); } } }libjgroups-java-2.12.2.Final.orig/tests/junit/org/jgroups/tests/0000755000175000017500000000000011647260573024474 5ustar moellermoellerlibjgroups-java-2.12.2.Final.orig/tests/junit/org/jgroups/tests/AddDataTest.java0000644000175000017500000000664211647260573027471 0ustar moellermoellerpackage org.jgroups.tests; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import org.jgroups.Address; import org.jgroups.Channel; import org.jgroups.Event; import org.jgroups.Global; import org.jgroups.JChannel; import org.jgroups.Message; import org.jgroups.ReceiverAdapter; import org.jgroups.stack.IpAddress; import org.jgroups.util.Util; import org.jgroups.util.UUID; import org.testng.annotations.Test; /** * * @author Bela Ban */ @Test(groups={Global.STACK_DEPENDENT},sequential=false) public class AddDataTest extends ChannelTestBase { @Test public void testAdditionalData() throws Exception { for(int i=1;i <= 2;i++) { System.out.println("-- attempt # " + i + "/2"); Channel c=createChannel(true); try { Map m=new HashMap(); m.put("additional_data", new byte[] { 'b', 'e', 'l', 'a' }); c.down(new Event(Event.CONFIG, m)); c.connect("AddDataTest.testadditionalData()"); UUID addr=(UUID)c.getAddress(); System.out.println("address is " + addr); assert addr.getAdditionalData() != null; assert addr.getAdditionalData()[0] == 'b'; } finally { c.close(); } } } @Test public void testBetweenTwoChannelsMcast() throws Exception { _testWithProps(true, "AddDataTest.testBetweenTwoChannelsMcast"); } @Test public void testBetweenTwoChannelsUnicast() throws Exception { _testWithProps(false, "AddDataTest.testBetweenTwoChannelsUnicast"); } private void _testWithProps(boolean mcast, String cluster_name) throws Exception { Map m=new HashMap(); m.put("additional_data", new byte[] { 'b', 'e', 'l', 'a' }); byte[] buf=new byte[1000]; JChannel ch1=null, ch2=null; try { ch1=createChannel(true, 2); ch1.down(new Event(Event.CONFIG, m)); ch2=createChannel(ch1); // same props as ch1 above ch2.down(new Event(Event.CONFIG, m)); MyReceiver receiver=new MyReceiver(); ch2.setReceiver(receiver); ch1.connect(cluster_name); ch2.connect(cluster_name); if(mcast) ch1.send(new Message(null, null, buf)); else { Address dest=ch2.getAddress(); ch1.send(new Message(dest, null, buf)); } Util.sleep(500); // msgs are sent asynchronously, give ch2 some time to receive them List list=receiver.getMsgs(); assert !list.isEmpty(); Message msg=list.get(0); UUID src=(UUID)msg.getSrc(); assert src != null; assert src.getAdditionalData() != null; assert src.getAdditionalData().length == 4; } finally { if(ch2 != null) ch2.close(); if(ch1 != null) ch1.close(); } } private static class MyReceiver extends ReceiverAdapter { final List msgs=new LinkedList(); public List getMsgs() {return msgs;} public void clear() {msgs.clear();} public void receive(Message msg) { System.out.println("received " + msg); msgs.add(msg); } } } libjgroups-java-2.12.2.Final.orig/tests/junit/org/jgroups/tests/ChannelConcurrencyTest.java0000644000175000017500000001676111647260573031775 0ustar moellermoellerpackage org.jgroups.tests; import static java.util.concurrent.TimeUnit.SECONDS; import java.net.InetAddress; import java.util.Properties; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import org.jgroups.Channel; import org.jgroups.JChannel; import org.jgroups.Message; import org.jgroups.blocks.GroupRequest; import org.jgroups.blocks.MessageDispatcher; import org.jgroups.blocks.RequestHandler; import org.jgroups.protocols.MERGE2; import org.jgroups.protocols.MPING; import org.jgroups.protocols.pbcast.GMS; import org.jgroups.stack.Protocol; import org.jgroups.stack.ProtocolStack; import org.jgroups.util.RspList; import org.jgroups.util.Util; import org.testng.annotations.Test; /** * Tests concurrent startup * @author Brian Goose */ @Test(groups="broken",sequential=true) public class ChannelConcurrencyTest extends ChannelTestBase{ public void testPlainChannel () throws Throwable{ testhelper(false); } public void testwithDispatcher () throws Throwable{ testhelper(true); } protected void testhelper(boolean useDispatcher) throws Throwable { final int count=8; final ExecutorService executor=Executors.newFixedThreadPool(count); final CountDownLatch latch=new CountDownLatch(count); final JChannel[] channels=new JChannel[count]; final Task[] tasks=new Task[count]; final long start=System.currentTimeMillis(); for(int i=0;i < count;i++) { if(i == 0) channels[i]=createChannel(true, count); else channels[i]=createChannel(channels[0]); tasks[i]=new Task(latch, channels[i],useDispatcher); changeMergeInterval(channels[i]); changeViewBundling(channels[i]); replaceDiscoveryProtocol(channels[i]); } for(final Task t:tasks) { executor.execute(t); } int timeoutToConverge=120; boolean successConnecting = false; try { // Wait for all channels to finish connecting successConnecting=latch.await(timeoutToConverge, TimeUnit.SECONDS); if(successConnecting) { log.info("All connected. Converging..."); for(Task t:tasks) { Throwable ex=t.getException(); if(ex != null) throw ex; } // Wait for all channels to have the correct number of members in their // current view boolean converged=false; for(int counter=0;counter < timeoutToConverge && !converged;SECONDS.sleep(1),counter++) { for(final JChannel channel:channels) { converged=channel.getView() != null && channel.getView().size() == count; if(!converged) break; } } final long duration=System.currentTimeMillis() - start; log.info("Converged to a single group after " + duration + " ms; group is:\n"); for(int i=0;i < channels.length;i++) { log.info("#" + (i + 1) + ": " + channels[i].getAddress() + ": " + channels[i].getView()); } } for(final JChannel channel:channels) { assertEquals("View ok for channel " + channel.getAddress(), count, channel.getView().size()); } assertTrue("All channels were succefully connected",successConnecting); } finally { Util.sleep(2500); executor.shutdownNow(); log.info("closing channels: "); for(JChannel ch:channels) { ch.close(); //there are sometimes big delays until entire cluster shuts down //use sleep to make a smoother shutdown so we avoid false positives Util.sleep(500); } } } private static void changeViewBundling(JChannel channel) { ProtocolStack stack=channel.getProtocolStack(); GMS gms=(GMS)stack.findProtocol(GMS.class); if(gms != null) { gms.setViewBundling(true); gms.setMaxBundlingTime(500); } } private static void changeMergeInterval(JChannel channel) { ProtocolStack stack=channel.getProtocolStack(); MERGE2 merge=(MERGE2)stack.findProtocol(MERGE2.class); if(merge != null) { merge.setMinInterval(5000); merge.setMaxInterval(10000); } } private static void replaceDiscoveryProtocol(JChannel ch) throws Exception { ProtocolStack stack=ch.getProtocolStack(); Protocol discovery=stack.removeProtocol("TCPPING"); if(discovery != null){ Protocol transport = stack.getTransport(); MPING mping=new MPING(); InetAddress bindAddress=Util.getBindAddress(new Properties()); mping.setBindAddr(bindAddress); mping.setMulticastAddress("230.1.2.3"); mping.setMcastPort(8888); stack.insertProtocol(mping, ProtocolStack.ABOVE, transport.getName()); mping.setProtocolStack(ch.getProtocolStack()); mping.init(); mping.start(); System.out.println("Replaced TCPPING with MPING. See http://wiki.jboss.org/wiki/Wiki.jsp?page=JGroupsMERGE2"); } } private static class Task implements Runnable { private final Channel c; private final CountDownLatch latch; private Throwable exception=null; private boolean useDispatcher = false; public Task(CountDownLatch latch, Channel c,boolean useDispatcher) { this.latch=latch; this.c=c; this.useDispatcher = useDispatcher; } public Throwable getException() { return exception; } public void run() { try { c.connect("ChannelConcurrencyTest"); if(useDispatcher && c.isConnected()) { final MessageDispatcher md=new MessageDispatcher(c, null, null, new MyHandler()); for(int i=0;i < 10;i++) { final RspList rsp=md.castMessage(null, new Message(null, null, i), GroupRequest.GET_ALL, 2500); for(Object o:rsp.getResults()) { assertEquals("Wrong result received at " + c.getAddress(), i, o); } } } } catch(final Exception e) { exception=e; e.printStackTrace(); } finally { latch.countDown(); } } } private static class MyHandler implements RequestHandler { public Object handle(Message msg) { return msg.getObject(); } } }libjgroups-java-2.12.2.Final.orig/tests/junit/org/jgroups/tests/ChannelTest.java0000644000175000017500000002331311647260573027551 0ustar moellermoeller package org.jgroups.tests; import org.jgroups.*; import org.jgroups.util.Util; import org.testng.annotations.Test; import java.util.LinkedList; import java.util.List; /** * Tests various methods in JChannel * @author Bela Ban */ @Test(groups=Global.STACK_DEPENDENT,sequential=false) public class ChannelTest extends ChannelTestBase { @Test public void testBasicOperations() throws Exception { JChannel c1 = createChannel(true,1); JChannel c2=null; try { c1.connect("testBasicOperations"); assert c1.isOpen(); assert c1.isConnected(); assert c1.getAddress() != null; assert c1.getView() != null; assert c1.getView().getMembers().contains(c1.getAddress()); c1.connect("testBasicOperations"); c1.disconnect(); assert c1.isConnected() == false; assert c1.isOpen(); assert c1.getAddress() == null; assert c1.getView() == null; assert c1.getClusterName() == null; c1.connect("testBasicOperations"); c1.close(); try { c1.connect("testBasicOperations"); throw new IllegalStateException("Should generated exception, and it has NOT"); } catch (Exception e) { assert e instanceof ChannelClosedException; } assert c1.isConnected() == false; assert c1.isOpen() == false; assert c1.getAddress() == null; assert c1.getView() == null; assert c1.getClusterName() == null; c1 = createChannel(true,2); c1.connect("testBasicOperations"); c2 = createChannel(c1); c2.connect("testBasicOperations"); Util.sleep(1000); assert c1.isOpen(); assert c1.isConnected(); assert c1.getAddress() != null; assert c1.getView() != null; assert c1.getView().getMembers().contains(c1.getAddress()); assert c1.getView().getMembers().contains(c2.getAddress()); assert c2.isOpen(); assert c2.isConnected(); assert c2.getAddress() != null; assert c2.getView() != null; assert c2.getView().getMembers().contains(c2.getAddress()); assert c2.getView().getMembers().contains(c1.getAddress()); c2.close(); Util.sleep(1000); assert c2.isOpen() == false; assert c2.isConnected() == false; assert c2.getAddress() == null; assert c2.getView() == null; assert c1.isOpen(); assert c1.isConnected(); assert c1.getAddress() != null; assert c1.getView() != null; assert c1.getView().getMembers().contains(c1.getAddress()); assert c1.getView().getMembers().contains(c2.getAddress()) == false; } finally { Util.close(c1,c2); } } @Test public void testViewChange() throws Exception { JChannel ch1 = createChannel(true,2); ViewChecker checker=new ViewChecker(ch1); ch1.setReceiver(checker); ch1.connect("testViewChange"); Channel ch2=createChannel(ch1); try { ch2.connect("testViewChange"); assertTrue(checker.getReason(), checker.isSuccess()); ch2.close(); assertTrue(checker.getReason(), checker.isSuccess()); } finally { Util.close(ch1,ch2); } } @Test public void testIsConnectedOnFirstViewChange() throws Exception { JChannel ch1 = createChannel(true,2); Channel ch2=createChannel(ch1); ConnectedChecker tmp=new ConnectedChecker(ch2); ch2.setReceiver(tmp); try { ch1.connect("testIsConnectedOnFirstViewChange"); ch2.connect("testIsConnectedOnFirstViewChange"); assert tmp.isConnected(); } finally { Util.close(ch1,ch2); } } @Test public void testNoViewIsReceivedAfterDisconnect() throws Exception { JChannel ch1 = createChannel(true,2); Channel ch2=createChannel(ch1); MyViewChecker ra = new MyViewChecker(ch2); ch2.setReceiver(ra); try { ch1.connect("testNoViewIsReceivedAferDisconnect"); ch2.connect("testNoViewIsReceivedAferDisconnect"); Util.sleep(500); ch2.disconnect(); Util.sleep(1000); assert !ra.receivedViewWhenDisconnected : "Received view where not member"; } finally { Util.close(ch1,ch2); } } @Test public void testNoViewIsReceivedAfterClose() throws Exception { JChannel ch1 = createChannel(true,2); Channel ch2=createChannel(ch1); MyViewChecker ra = new MyViewChecker(ch2); ch2.setReceiver(ra); try { ch1.connect("testNoViewIsReceivedAferClose"); ch2.connect("testNoViewIsReceivedAferClose"); Util.sleep(200); ch2.close(); Util.sleep(1000); assert !ra.receivedViewWhenDisconnected : "Received view where not member"; } finally { Util.close(ch1,ch2); } } @Test(expectedExceptions=TimeoutException.class) public void testReceiveTimeout() throws Exception, TimeoutException { JChannel ch1 = createChannel(true,2); try{ ch1.connect("testReceiveTimeout"); ch1.receive(1000); // this one works, because we're expecting a View ch1.receive(2000);// .. but this one doesn't (no msg available) - needs to throw a TimeoutException } finally{ Util.close(ch1); } } @Test(expectedExceptions={NullPointerException.class}) public void testNullMessage() throws Exception { JChannel ch1 = createChannel(true,2); try{ ch1.connect("testNullMessage"); ch1.send(null); } finally{ Util.close(ch1); } } @Test public void testOrdering() throws Exception { final int NUM=100; JChannel ch=createChannel(true, 2); MyReceiver receiver=new MyReceiver(NUM); ch.setReceiver(receiver); try { ch.connect("testOrdering"); for(int i=1;i <= NUM;i++) { ch.send(new Message(null, null, new Integer(i))); // System.out.println("-- sent " + i); } receiver.waitForCompletion(); List nums=receiver.getNums(); checkMonotonicallyIncreasingNumbers(nums); } finally { Util.close(ch); } } private static void checkMonotonicallyIncreasingNumbers(List nums) { int current=-1; for(int num: nums) { if(current < 0) { current=num; } else { assert ++current == num : "list is " + nums; } } } private static class MyReceiver extends ReceiverAdapter { final List nums=new LinkedList(); final int expected; public MyReceiver(int expected) { this.expected=expected; } public List getNums() { return nums; } public void waitForCompletion() throws InterruptedException { synchronized(nums) { while(nums.size() < expected) { nums.wait(); } } } public void receive(Message msg) { Integer num=(Integer)msg.getObject(); synchronized(nums) { nums.add(num); if(nums.size() >= expected) { nums.notifyAll(); } } } } private static class ConnectedChecker extends ReceiverAdapter { boolean connected=false; public ConnectedChecker(Channel channel) { this.channel=channel; } final Channel channel; public boolean isConnected() { return connected; } public void viewAccepted(View new_view) { connected=channel.isConnected(); // System.out.println("ConnectedChecker: channel.isConnected()=" + connected + ", view=" + new_view); } } private static class ViewChecker extends ReceiverAdapter { final Channel channel; boolean success=true; String reason=""; public ViewChecker(Channel channel) { this.channel=channel; } public String getReason() { return reason; } public boolean isSuccess() { return success; } public void viewAccepted(View new_view) { View view=channel.getView(); String str="viewAccepted(): channel's view=" + view + "\nreceived view=" + new_view; // System.out.println(str); if(view!= null && !view.equals(new_view)) { success=false; reason+=str + "\n"; } } } private static class MyViewChecker extends ReceiverAdapter { private boolean receivedViewWhenDisconnected; private final Channel ch; public MyViewChecker(Channel ch) { this.ch=ch; } public void viewAccepted(View new_view) { receivedViewWhenDisconnected = !new_view.containsMember(ch.getAddress()); } } } libjgroups-java-2.12.2.Final.orig/tests/junit/org/jgroups/tests/ChannelTestBase.java0000644000175000017500000004505011647260573030346 0ustar moellermoellerpackage org.jgroups.tests; import org.jgroups.*; import org.jgroups.logging.Log; import org.jgroups.logging.LogFactory; import org.jgroups.protocols.BasicTCP; import org.jgroups.protocols.TCPPING; import org.jgroups.protocols.TP; import org.jgroups.protocols.UDP; import org.jgroups.protocols.pbcast.FLUSH; import org.jgroups.stack.Protocol; import org.jgroups.stack.ProtocolStack; import org.jgroups.stack.IpAddress; import org.jgroups.util.ResourceManager; import org.jgroups.util.Util; import org.jgroups.util.StackType; import org.testng.AssertJUnit; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeMethod; import org.testng.annotations.BeforeClass; import org.testng.annotations.Optional; import org.testng.annotations.Parameters; import org.testng.annotations.Test; import java.io.InputStream; import java.io.OutputStream; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.net.InetAddress; import java.util.*; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * @author Bela Ban * @author Vladimir Blagojevic * @author Brian Stansberry */ @Test(groups = "base", sequential = true) public class ChannelTestBase { protected String channel_conf = "udp.xml"; protected boolean use_blocking = false; protected boolean use_flush = false; private String bind_addr = null; protected final Log log = LogFactory.getLog(this.getClass()); @BeforeClass @Parameters(value = { "channel.conf", "use_blocking" }) protected void initializeBase(@Optional("udp.xml") String chconf, @Optional("false") String use_blocking) throws Exception { Test annotation = this.getClass().getAnnotation(Test.class); // this should never ever happen! if (annotation == null) throw new Exception("Test is not marked with @Test annotation"); StackType type=Util.getIpStackType(); bind_addr=type == StackType.IPv6 ? "::1" : "127.0.0.1"; List groups = Arrays.asList(annotation.groups()); boolean testRequiresFlush = groups.contains(Global.FLUSH); this.use_blocking = testRequiresFlush || Boolean.parseBoolean(use_blocking); this.use_flush = testRequiresFlush; this.channel_conf = chconf; boolean ignore_systemprops=Util.isBindAddressPropertyIgnored(); bind_addr = Util.getProperty(new String[]{Global.BIND_ADDR, Global.BIND_ADDR_OLD}, null, "bind_addr", ignore_systemprops, bind_addr); // bind_addr = Util.getBindAddress(null).getHostAddress(); } @BeforeMethod protected static void startTestHeader(java.lang.reflect.Method m) { System.out.println("\n================ Starting test " + m.getName() + " ================\n"); } @AfterClass(alwaysRun = true) protected void nullifyInstanceFields() { for (Class current = this.getClass(); current.getSuperclass() != null; current = current.getSuperclass()) { Field[] fields = current.getDeclaredFields(); for (Field f : fields) { try { if (!Modifier.isStatic(f.getModifiers()) && !f.getDeclaringClass().isPrimitive()) { f.setAccessible(true); f.set(this, null); } } catch (Exception e) { } } } } protected String getBindAddress() { return bind_addr; } protected boolean useBlocking() { return use_blocking; } protected void setUseBlocking(boolean flag) { use_blocking = flag; } protected boolean useFlush() { return use_flush; } protected final static void assertTrue(boolean condition) { Util.assertTrue(condition); } protected final static void assertTrue(String message, boolean condition) { Util.assertTrue(message, condition); } protected final static void assertFalse(boolean condition) { Util.assertFalse(condition); } protected final static void assertFalse(String message, boolean condition) { Util.assertFalse(message, condition); } protected final static void assertEquals(String message, Object val1, Object val2) { Util.assertEquals(message, val1, val2); } protected final static void assertEquals(Object val1, Object val2) { Util.assertEquals(null, val1, val2); } protected final static void assertNotNull(String message, Object val) { Util.assertNotNull(message, val); } protected final static void assertNotNull(Object val) { Util.assertNotNull(null, val); } protected final static void assertNull(String message, Object val) { Util.assertNull(message, val); } protected final static void assertNull(Object val) { Util.assertNotNull(null, val); } /** * Creates a channel and modifies the configuration such that no other channel will able to join * this one even if they have the same cluster name (if unique = true). This is done by * modifying mcast_addr and mcast_port with UDP, and by changing TCP.start_port, TCP.port_range * and TCPPING.initial_hosts with TCP. Mainly used to run TestNG tests concurrently. Note that * MuxChannels are not currently supported. * * @param num * The number of channels we will create. Only important (for port_range) with TCP, * ignored by UDP * @return * @throws Exception */ protected JChannel createChannel(boolean unique, int num) throws Exception { return (JChannel) new DefaultChannelTestFactory().createChannel(unique, num); } protected JChannel createChannel(boolean unique, int num, String name) throws Exception { JChannel ch=(JChannel)new DefaultChannelTestFactory().createChannel(unique, num); ch.setName(name); return ch; } protected JChannel createChannel() throws Exception { return new DefaultChannelTestFactory().createChannel(); } protected JChannel createChannel(boolean unique) throws Exception { return createChannel(unique, 2); } protected JChannel createChannel(JChannel ch) throws Exception { return (JChannel) new DefaultChannelTestFactory().createChannel(ch); } protected JChannel createChannel(JChannel ch, String name) throws Exception { JChannel retval=(JChannel) new DefaultChannelTestFactory().createChannel(ch); retval.setName(name); return retval; } protected static String getUniqueClusterName() { return getUniqueClusterName(null); } protected static String getUniqueClusterName(String base_name) { return ResourceManager.getUniqueClusterName(base_name); } /** * Default channel factory used in junit tests */ protected class DefaultChannelTestFactory { public JChannel createChannel() throws Exception { return createChannel(channel_conf); } public Channel createChannel(boolean unique, int num) throws Exception { JChannel c = createChannel(channel_conf); if(unique) makeUnique(c, num); return c; } public Channel createChannel(final JChannel ch) throws Exception { JChannel retval = new JChannel(ch); retval.setOpt(Channel.BLOCK, ch.getOpt(Channel.BLOCK)); if(useFlush()) Util.addFlush(retval, new FLUSH()); return retval; } private JChannel createChannel(String configFile) throws Exception { JChannel ch = new JChannel(configFile); ch.setOpt(Channel.BLOCK, useBlocking()); if(useFlush()) Util.addFlush(ch, new FLUSH()); return ch; } protected void makeUnique(Channel channel, int num) throws Exception { String str = Util.getProperty(new String[]{ Global.UDP_MCAST_ADDR, "jboss.partition.udpGroup" }, null, "mcast_addr", false, null); makeUnique(channel, num, str); } protected void makeUnique(Channel channel, int num, String mcast_address) throws Exception { ProtocolStack stack = channel.getProtocolStack(); Protocol transport = stack.getTransport(); if (transport instanceof UDP) { short mcast_port = ResourceManager.getNextMulticastPort(InetAddress.getByName(bind_addr)); ((UDP) transport).setMulticastPort(mcast_port); if (mcast_address != null) { ((UDP) transport).setMulticastAddress(InetAddress.getByName(mcast_address)); } else { String mcast_addr = ResourceManager.getNextMulticastAddress(); ((UDP) transport).setMulticastAddress(InetAddress.getByName(mcast_addr)); } } else if (transport instanceof BasicTCP) { List ports = ResourceManager.getNextTcpPorts(InetAddress.getByName(bind_addr), num); ((TP) transport).setBindPort(ports.get(0)); ((TP) transport).setPortRange(num); Protocol ping = stack.findProtocol(TCPPING.class); if (ping == null) throw new IllegalStateException("TCP stack must consist of TCP:TCPPING - other config are not supported"); List initial_hosts = new LinkedList(); for (short port : ports) { initial_hosts.add(bind_addr + "[" + port + "]"); } String tmp = Util.printListWithDelimiter(initial_hosts, ","); List init_hosts = Util.parseCommaDelimitedHosts(tmp, 0); ((TCPPING) ping).setInitialHosts(init_hosts); } else { throw new IllegalStateException("Only UDP and TCP are supported as transport protocols"); } } } interface EventSequence { List getEvents(); String getName(); } /** * Base class for all aplications using channel */ protected abstract class ChannelApplication extends ExtendedReceiverAdapter implements EventSequence, Runnable { protected Channel channel; protected Thread thread; protected Throwable exception; protected List events; public ChannelApplication(String name) throws Exception { channel = createChannel(true, 4); init(name); } public ChannelApplication(JChannel copySource, String name) throws Exception { channel = createChannel(copySource); init(name); } protected void init(String name) { events = Collections.synchronizedList(new LinkedList()); channel.setName(name); channel.setReceiver(this); } /** * Method allowing implementation of specific test application level logic * * @throws Exception */ protected abstract void useChannel() throws Exception; public void run() { try { useChannel(); } catch (Exception e) { log.error(channel.getName() + ": " + e.getLocalizedMessage(), e); exception = e; // Save it for the test to check } } public List
                    getMembers() { List
                    result = null; View v = channel.getView(); if (v != null) { result = v.getMembers(); } return result; } public Address getLocalAddress() { return channel.getAddress(); } public void start() { thread = new Thread(this, getName()); thread.start(); } public Channel getChannel() { return channel; } public String getName() { return channel != null ? channel.getName() : "n/a"; } public void cleanup() { if (thread != null && thread.isAlive()) thread.interrupt(); try { channel.close(); } catch (Throwable t) { log.warn("Exception while closing channel " + getName(), t); } } public List getEvents() { return events; } public void block() { events.add(new BlockEvent()); } public byte[] getState() { events.add(new GetStateEvent(null, null)); return null; } public void getState(OutputStream ostream) { events.add(new GetStateEvent(null, null)); } public byte[] getState(String state_id) { events.add(new GetStateEvent(null, state_id)); return null; } public void getState(String state_id, OutputStream ostream) { events.add(new GetStateEvent(null, state_id)); } public void setState(byte[] state) { events.add(new SetStateEvent(null, null)); } public void setState(InputStream istream) { events.add(new SetStateEvent(null, null)); } public void setState(String state_id, byte[] state) { events.add(new SetStateEvent(null, null)); } public void setState(String state_id, InputStream istream) { events.add(new SetStateEvent(null, null)); } public void unblock() { events.add(new UnblockEvent()); } public void viewAccepted(View new_view) { events.add(new_view); log.info(getLocalAddress() + ": view=" + new_view); } } /** * Channel with semaphore allows application to go through fine-grained synchronous step * control. *

                    * PushChannelApplicationWithSemaphore application will not proceed to useChannel() until it * acquires permit from semphore. After useChannel() completes the acquired permit will be * released. Test driver should control how semaphore tickets are given and acquired. */ protected abstract class PushChannelApplicationWithSemaphore extends ChannelApplication { protected Semaphore semaphore; public PushChannelApplicationWithSemaphore(String name, Semaphore semaphore) throws Exception { super(name); this.semaphore = semaphore; } public PushChannelApplicationWithSemaphore(JChannel copySource, String name, Semaphore semaphore) throws Exception { super(copySource, name); this.semaphore = semaphore; } public void run() { boolean acquired = false; try { acquired = semaphore.tryAcquire(60000L, TimeUnit.MILLISECONDS); if (!acquired) throw new Exception(channel.getAddress() + ": cannot acquire semaphore"); useChannel(); } catch (Exception e) { exception = e; // Save it for the test to check } finally { if (acquired) { semaphore.release(); } } } } protected static void checkEventStateTransferSequence(EventSequence receiver) { List events = receiver.getEvents(); assertNotNull(events); final String validSequence = "([b][vgs]*[u])+"; // translate the eventTrace to an eventString try { assertTrue("Invalid event sequence " + events, validateEventString( translateEventTrace(events), validSequence)); } catch (Exception e) { AssertJUnit.fail("Invalid event sequence " + events); } } /** * Method for translating event traces into event strings, where each event in the trace is * represented by a letter. */ protected static String translateEventTrace(List et) throws Exception { StringBuilder eventString = new StringBuilder(); for (Iterator it = et.iterator(); it.hasNext();) { Object obj = it.next(); if (obj instanceof BlockEvent) eventString.append("b"); else if (obj instanceof UnblockEvent) eventString.append("u"); else if (obj instanceof SetStateEvent) eventString.append("s"); else if (obj instanceof GetStateEvent) eventString.append("g"); else if (obj instanceof View) eventString.append("v"); else throw new Exception("Unrecognized event type in event trace"); } String s = eventString.toString(); // if it ends with block, strip it out because it will be regarded as error sequence while (s.endsWith("b")) { s = s.substring(0, s.length() - 1); } return s; } /** * Method for validating event strings against event string specifications, where a * specification is a regular expression involving event symbols. e.g. [b]([sgv])[u] */ protected static boolean validateEventString(String eventString, String spec) { Pattern pattern = null; Matcher matcher = null; // set up the regular expression specification pattern = Pattern.compile(spec); // set up the actual event string matcher = pattern.matcher(eventString); // check if the actual string satisfies the specification if (matcher.find()) { // a match has been found, but we need to check that the whole event string // matches, and not just a substring if (!(matcher.start() == 0 && matcher.end() == eventString.length())) { // match on full eventString not found System.err .println("event string invalid (proper substring matched): event string = " + eventString + ", specification = " + spec + "matcher.start() " + matcher.start() + " matcher.end() " + matcher.end()); return false; } } else { return false; } return true; } } libjgroups-java-2.12.2.Final.orig/tests/junit/org/jgroups/tests/CloseTest.java0000644000175000017500000002546711647260573027262 0ustar moellermoeller package org.jgroups.tests; import org.jgroups.*; import org.jgroups.util.Util; import org.testng.Assert; import org.testng.annotations.AfterMethod; import org.testng.annotations.Test; import java.util.Vector; import java.util.List; import java.util.ArrayList; /** * Demos the creation of a channel and subsequent connection and closing. Demo application should exit (no * more threads running) */ @Test(groups=Global.STACK_DEPENDENT, sequential=false) public class CloseTest extends ChannelTestBase { private final ThreadLocal ch=new ThreadLocal(), channel1=new ThreadLocal(), c1=new ThreadLocal(), c2=new ThreadLocal(), c3=new ThreadLocal(); @AfterMethod void tearDown() throws Exception { closeChannel(ch); closeChannel(channel1); closeChannel(c1); closeChannel(c2); closeChannel(c3); } protected boolean useBlocking() { return false; } private static void closeChannel(ThreadLocal local) { Channel c=local.get(); if(c != null && (c.isOpen() || c.isConnected())) { c.close(); } local.set(null); } @Test public void testDoubleClose() throws Exception { System.out.println("-- creating channel1 --"); channel1.set(createChannel(true)); System.out.println("-- connecting channel1 --"); channel1.get().connect(getUniqueClusterName("CloseTest.testDoubleClose")); assertTrue("channel open", channel1.get().isOpen()); assertTrue("channel connected", channel1.get().isConnected()); System.out.println("-- closing channel1 --"); channel1.get().close(); System.out.println("-- closing channel1 (again) --"); channel1.get().close(); assertFalse("channel not connected", channel1.get().isConnected()); } @Test public void testCreationAndClose() throws Exception { System.out.println("-- creating channel1 --"); ch.set(createChannel(true)); ch.get().connect(getUniqueClusterName("CloseTest.testCreationAndClose")); assertTrue("channel open", ch.get().isOpen()); assertTrue("channel connected", ch.get().isConnected()); ch.get().close(); assertFalse("channel not connected", ch.get().isConnected()); ch.get().close(); } @Test public void testViewChangeReceptionOnChannelCloseByParticipant() throws Exception { Address a1, a2; Vector members; MyReceiver r1=new MyReceiver(), r2=new MyReceiver(); c1.set(createChannel(true)); c1.get().setReceiver(r1); System.out.println("-- connecting c1"); final String GROUP="CloseTest.testViewChangeReceptionOnChannelCloseByParticipant"; c1.get().connect(GROUP); Util.sleep(500); // time to receive its own view System.out.println("c1: " + r1.getViews()); a1=c1.get().getAddress(); c2.set(createChannel(c1.get())); c2.get().setReceiver(r2); System.out.println("-- connecting c2"); r1.clearViews(); c2.get().connect(GROUP); Util.sleep(500); // time to receive its own view a2=c2.get().getAddress(); System.out.println("c2: " + r2.getViews()); System.out.println("-- closing c2"); c2.get().close(); Util.sleep(500); View v=r1.getViews().get(0); members=v.getMembers(); System.out.println("-- first view of c1: " + v); Assert.assertEquals(2, members.size()); assertTrue(members.contains(a1)); assertTrue(members.contains(a2)); v=r1.getViews().get(1); members=v.getMembers(); System.out.println("-- second view of c1: " + v); assert 1 == members.size(); assert members.contains(a1); assert !members.contains(a2); } @Test public void testViewChangeReceptionOnChannelCloseByCoordinator() throws Exception { Address a1, a2; Vector members; MyReceiver r1=new MyReceiver(), r2=new MyReceiver(); final String GROUP=getUniqueClusterName("CloseTest.testViewChangeReceptionOnChannelCloseByCoordinator"); c1.set(createChannel(true)); c1.get().setReceiver(r1); c1.get().connect(GROUP); Util.sleep(500); // time to receive its own view a1=c1.get().getAddress(); c2.set(createChannel(c1.get())); c2.get().setReceiver(r2); c2.get().connect(GROUP); Util.sleep(500); // time to receive its own view a2=c2.get().getAddress(); View v=r2.getViews().get(0); members=v.getMembers(); assert 2 == members.size(); assert members.contains(a2); r2.clearViews(); c1.get().close(); Util.sleep(1000); v=r2.getViews().get(0); members=v.getMembers(); assert 1 == members.size(); assert !members.contains(a1); assert members.contains(a2); assert c2.get().getNumMessages() == 0; } @Test public void testConnectDisconnectConnectCloseSequence() throws Exception { System.out.println("-- creating channel --"); ch.set(createChannel(true)); System.out.println("-- connecting channel to CloseTest--"); ch.get().connect("CloseTest.testConnectDisconnectConnectCloseSequence-CloseTest"); System.out.println("view is " + ch.get().getView()); System.out.println("-- disconnecting channel --"); ch.get().disconnect(); Util.sleep(500); System.out.println("-- connecting channel to OtherGroup --"); ch.get().connect("CloseTest.testConnectDisconnectConnectCloseSequence-OtherGroup"); System.out.println("view is " + ch.get().getView()); System.out.println("-- closing channel --"); ch.get().close(); } @Test public void testConnectCloseSequenceWith2Members() throws Exception { System.out.println("-- creating channel --"); ch.set(createChannel(true)); System.out.println("-- connecting channel --"); final String GROUP=getUniqueClusterName("CloseTest.testConnectCloseSequenceWith2Members"); ch.get().connect(GROUP); System.out.println("view is " + ch.get().getView()); System.out.println("-- creating channel1 --"); channel1.set(createChannel(ch.get())); System.out.println("-- connecting channel1 --"); channel1.get().connect(GROUP); System.out.println("view is " + channel1.get().getView()); System.out.println("-- closing channel1 --"); channel1.get().close(); Util.sleep(2000); System.out.println("-- closing channel --"); ch.get().close(); } @Test public void testCreationAndClose2() throws Exception { System.out.println("-- creating channel2 --"); ch.set(createChannel(true)); System.out.println("-- connecting channel2 --"); ch.get().connect(getUniqueClusterName("CloseTest.testCreationAndClose2")); System.out.println("-- closing channel --"); ch.get().close(); } @Test public void testChannelClosedException() throws Exception { System.out.println("-- creating channel --"); ch.set(createChannel(true)); System.out.println("-- connecting channel --"); ch.get().connect(getUniqueClusterName("CloseTest.testChannelClosedException")); System.out.println("-- closing channel --"); ch.get().close(); Util.sleep(2000); try { ch.get().connect(getUniqueClusterName("CloseTest.testChannelClosedException")); assert false; } catch(ChannelClosedException ex) { assertTrue(true); } } @Test public void testMultipleConnectsAndDisconnects() throws Exception { c1.set(createChannel(true)); assertTrue(c1.get().isOpen()); assertFalse(c1.get().isConnected()); final String GROUP=getUniqueClusterName("CloseTest.testMultipleConnectsAndDisconnects"); c1.get().connect(GROUP); System.out.println("view after c1.connect(): " + c1.get().getView()); assertTrue(c1.get().isOpen()); assertTrue(c1.get().isConnected()); assertServiceAndClusterView(c1.get(), 1); c2.set(createChannel(c1.get())); assertTrue(c2.get().isOpen()); assertFalse(c2.get().isConnected()); c2.get().connect(GROUP); System.out.println("view after c2.connect(): " + c2.get().getView()); assertTrue(c2.get().isOpen()); assertTrue(c2.get().isConnected()); assertServiceAndClusterView(c2.get(), 2); Util.sleep(500); assertServiceAndClusterView(c1.get(), 2); c2.get().disconnect(); System.out.println("view after c2.disconnect(): " + c2.get().getView()); assertTrue(c2.get().isOpen()); assertFalse(c2.get().isConnected()); Util.sleep(500); assertServiceAndClusterView(c1.get(), 1); c2.get().connect(GROUP); System.out.println("view after c2.connect(): " + c2.get().getView()); assertTrue(c2.get().isOpen()); assertTrue(c2.get().isConnected()); assertServiceAndClusterView(c2.get(), 2); Util.sleep(300); assertServiceAndClusterView(c1.get(), 2); // Now see what happens if we reconnect the first channel c3.set(createChannel(c1.get())); assertTrue(c3.get().isOpen()); assertFalse(c3.get().isConnected()); assertServiceAndClusterView(c1.get(), 2); assertServiceAndClusterView(c2.get(), 2); c1.get().disconnect(); Util.sleep(1000); assertTrue(c1.get().isOpen()); assertFalse(c1.get().isConnected()); assertServiceAndClusterView(c2.get(), 1); assertTrue(c3.get().isOpen()); assertFalse(c3.get().isConnected()); c1.get().connect(GROUP); System.out.println("view after c1.connect(): " + c1.get().getView()); assertTrue(c1.get().isOpen()); assertTrue(c1.get().isConnected()); assertServiceAndClusterView(c1.get(), 2); Util.sleep(500); assertServiceAndClusterView(c2.get(), 2); assertTrue(c3.get().isOpen()); assertFalse(c3.get().isConnected()); } private static void assertServiceAndClusterView(Channel ch, int num) { View view=ch.getView(); String msg="view=" + view; assertNotNull(view); Assert.assertEquals(view.size(), num, msg); } private static class MyReceiver extends ReceiverAdapter { final List views=new ArrayList(); public void viewAccepted(View new_view) { views.add(new_view); System.out.println("new_view = " + new_view); } public List getViews() {return views;} public void clearViews() {views.clear();} } } libjgroups-java-2.12.2.Final.orig/tests/junit/org/jgroups/tests/ConcurrentCloseTest.java0000644000175000017500000000476711647260573031325 0ustar moellermoellerpackage org.jgroups.tests; import org.jgroups.*; import org.jgroups.util.Util; import org.testng.annotations.AfterMethod; import org.testng.annotations.Test; import java.util.concurrent.CyclicBarrier; /** * @author Bela Ban */ @Test(groups=Global.STACK_DEPENDENT,sequential=true) public class ConcurrentCloseTest extends ChannelTestBase { JChannel c1, c2; @AfterMethod void tearDown() throws Exception { Util.close(c2, c1); } /** * 2 channels, both call Channel.close() at exactly the same time */ public void testConcurrentClose() throws Exception { System.setProperty("useBlocking", "true"); // enables reception of block() and unblock() callbacks c1=createChannel(true); c1.setReceiver(new MyReceiver("C1")); c2=createChannel(c1); c2.setReceiver(new MyReceiver("C2")); final String GROUP=getUniqueClusterName("ConcurrentCloseTest"); c1.connect(GROUP); c2.connect(GROUP); CyclicBarrier barrier=new CyclicBarrier(3); Closer one=new Closer(c1, barrier), two=new Closer(c2, barrier); one.start(); two.start(); Util.sleep(500); barrier.await(); // starts the closing of the 2 channels one.join(10000); two.join(10000); assertFalse(one.isAlive()); assertFalse(two.isAlive()); } private static class Closer extends Thread { private final Channel channel; final private CyclicBarrier barrier; public Closer(Channel channel, CyclicBarrier barrier) { this.channel=channel; this.barrier=barrier; } public void run() { try { barrier.await(); System.out.println("closing channel for " + channel.getAddress()); channel.close(); } catch(Exception e) { } } } private static class MyReceiver extends ExtendedReceiverAdapter { private final String name; public MyReceiver(String name) { this.name=name; } public void block() { System.out.println("[" + name + "] block()"); } public void unblock() { System.out.println("[" + name + "] unblock()"); } public void viewAccepted(View new_view) { System.out.println("[" + name + "] " + new_view); } public void receive(Message msg) { System.out.println("[" + name + "] " + msg); } } } libjgroups-java-2.12.2.Final.orig/tests/junit/org/jgroups/tests/ConcurrentFlushTest.java0000644000175000017500000002402611647260573031327 0ustar moellermoellerpackage org.jgroups.tests; import org.jgroups.*; import org.jgroups.util.Util; import org.testng.annotations.AfterMethod; import org.testng.annotations.Test; import java.util.Arrays; import java.util.List; import java.util.LinkedList; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; /** * Tests concurrent and partial flushes * @author Manik Surtani * @author Bela Ban */ @Test(groups=Global.FLUSH, sequential=true) public class ConcurrentFlushTest extends ChannelTestBase { JChannel c1, c2, c3; @AfterMethod public void tearDown() throws Exception { Util.close(c3, c2, c1); } public boolean useBlocking() { return true; } /** * Tests A.startFlush(), followed by another A.startFlush() */ @Test public void testTwoStartFlushesOnSameMemberWithTotalFlush() throws Exception { c1=createChannel(true, 3); c1.connect("testTwoStartFlushes"); c2=createChannel(c1); c2.connect("testTwoStartFlushes"); assertViewsReceived(c1, c2); boolean rc=startFlush(c1, true); assert rc; rc=startFlush(c1, false); assert rc; rc=startFlush(c1, 1, 500, false); assert !rc; stopFlush(c1); rc=startFlush(c1, true); assert rc; } /** * Tests A.startFlush(), followed by another A.startFlush() */ public void testTwoStartFlushesOnDifferentMembersWithTotalFlush() throws Exception { c1=createChannel(true, 3); c1.connect("testTwoStartFlushesOnDifferentMembersWithTotalFlush"); c2=createChannel(c1); c2.connect("testTwoStartFlushesOnDifferentMembersWithTotalFlush"); assertViewsReceived(c1, c2); boolean rc=startFlush(c1, false); assert rc; rc=startFlush(c2, 1, 500, false); assert !rc; stopFlush(c1); rc=startFlush(c2, false); assert rc; stopFlush(c2); rc=startFlush(c1, false); assert rc; stopFlush(c2); // c2 can actually stop a flush started by c1 rc=startFlush(c2, true); assert rc; } /** * Tests 2 channels calling FLUSH simultaneously */ @Test public void testConcurrentFlush() throws Exception { c1=createChannel(true, 2); c1.connect("testConcurrentFlush"); c2=createChannel(c1); c2.connect("testConcurrentFlush"); assertViewsReceived(c1, c2); final CountDownLatch startFlushLatch=new CountDownLatch(1); final CountDownLatch stopFlushLatch=new CountDownLatch(1); final CountDownLatch flushStartReceived=new CountDownLatch(2); final CountDownLatch flushStopReceived=new CountDownLatch(2); Thread t1=new Thread() { public void run() { try { startFlushLatch.await(); boolean rc=Util.startFlush(c1); System.out.println("t1: rc=" + rc); } catch(InterruptedException e) { interrupt(); } try { stopFlushLatch.await(); } catch(InterruptedException e) { interrupt(); } finally { c1.stopFlush(); } } }; Thread t2=new Thread() { public void run() { try { startFlushLatch.await(); boolean rc=Util.startFlush(c2); System.out.println("t2: rc=" + rc); } catch(InterruptedException e) { interrupt(); } try { stopFlushLatch.await(); } catch(InterruptedException e) { interrupt(); } finally { c2.stopFlush(); } } }; Listener l1=new Listener("c1", c1, flushStartReceived, flushStopReceived); Listener l2=new Listener("c2", c2, flushStartReceived, flushStopReceived); t1.start(); t2.start(); startFlushLatch.countDown(); assertTrue(flushStartReceived.await(60, TimeUnit.SECONDS)); // at this stage both channels should have started a flush stopFlushLatch.countDown(); t1.join(); t2.join(); assertTrue(flushStopReceived.await(60, TimeUnit.SECONDS)); assert l1.blockReceived; assert l1.unblockReceived; assert l2.blockReceived; assert l2.unblockReceived; } /** * Tests 2 channels calling partial FLUSHes and one calling FLUSH simultaneously */ @Test public void testConcurrentFlushAndPartialFlush() throws Exception { c1=createChannel(true, 3); c1.connect("testConcurrentFlushAndPartialFlush"); c2=createChannel(c1); c2.connect("testConcurrentFlushAndPartialFlush"); c3=createChannel(c1); c3.connect("testConcurrentFlushAndPartialFlush"); assertViewsReceived(c1, c2, c3); final CountDownLatch startFlushLatch=new CountDownLatch(1); final CountDownLatch stopFlushLatch=new CountDownLatch(1); // 2 because either total or partial has to finish first final CountDownLatch flushStartReceived=new CountDownLatch(2); // 5 because we have total and partial flush final CountDownLatch flushStopReceived=new CountDownLatch(5); Thread t1=new Thread() { public void run() { try { startFlushLatch.await(); boolean rc=Util.startFlush(c1); System.out.println("t1: rc=" + rc); } catch(InterruptedException e) { interrupt(); } try { stopFlushLatch.await(); } catch(InterruptedException e) { interrupt(); } finally { c1.stopFlush(); } } }; Thread t2=new Thread() { public void run() { try { startFlushLatch.await(); // partial, only between c2 and c3 boolean rc=Util.startFlush(c2, (Arrays.asList(c2.getLocalAddress(), c3.getLocalAddress()))); System.out.println("t2: partial flush rc=" + rc); } catch(InterruptedException e) { interrupt(); } try { stopFlushLatch.await(); } catch(InterruptedException e) { interrupt(); } finally { c2.stopFlush(Arrays.asList(c2.getLocalAddress(), c3.getLocalAddress())); } } }; Listener l1=new Listener("c1", c1, flushStartReceived, flushStopReceived); Listener l2=new Listener("c2", c2, flushStartReceived, flushStopReceived); Listener l3=new Listener("c3", c3, flushStartReceived, flushStopReceived); t1.start(); t2.start(); startFlushLatch.countDown(); assertTrue(flushStartReceived.await(60, TimeUnit.SECONDS)); // at this stage both channels should have started a flush? stopFlushLatch.countDown(); t1.join(); t2.join(); assertTrue(flushStopReceived.await(60, TimeUnit.SECONDS)); assert l1.blockReceived; assert l1.unblockReceived; assert l2.blockReceived; assert l2.unblockReceived; assert l3.blockReceived; assert l3.unblockReceived; } private boolean startFlush(Channel ch, boolean automatic_resume) { log.debug("starting flush on " + ch.getAddress() + " with automatic resume=" + automatic_resume); boolean result=Util.startFlush(ch); if(automatic_resume) { ch.stopFlush(); } return result; } private boolean startFlush(Channel ch, int num_attempts, long timeout, boolean automatic_resume) { log.debug("starting flush on " + ch.getAddress() + " with automatic resume=" + automatic_resume); boolean result=Util.startFlush(ch, num_attempts, 10, timeout); if(automatic_resume) { ch.stopFlush(); } return result; } private void stopFlush(Channel ch) { log.debug("calling stopFlush()"); ch.stopFlush(); } private static void assertViewsReceived(JChannel... channels) { for(JChannel c : channels) assertEquals(c.getView().getMembers().size(), channels.length); } private static class Listener extends ExtendedReceiverAdapter implements EventSequence { final String name; boolean blockReceived, unblockReceived; JChannel channel; CountDownLatch flushStartReceived, flushStopReceived; final List events=new LinkedList(); Listener(String name, JChannel channel, CountDownLatch flushStartReceived, CountDownLatch flushStopReceived) { this.name=name; this.channel=channel; this.flushStartReceived=flushStartReceived; this.flushStopReceived=flushStopReceived; this.channel.setReceiver(this); } public void unblock() { unblockReceived=true; if(flushStopReceived != null) flushStopReceived.countDown(); events.add(new UnblockEvent()); } public void block() { blockReceived=true; if(flushStartReceived != null) flushStartReceived.countDown(); events.add(new BlockEvent()); } public List getEvents() { return events; } public void viewAccepted(View new_view) { events.add(new_view); } public String getName() { return name; } } } libjgroups-java-2.12.2.Final.orig/tests/junit/org/jgroups/tests/ConcurrentStartupTest.java0000644000175000017500000001746411647260573031720 0ustar moellermoellerpackage org.jgroups.tests; import org.jgroups.*; import org.jgroups.util.Util; import org.testng.annotations.Test; import java.io.*; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; /** * Tests concurrent startup and message sending directly after joining. See doc/design/ConcurrentStartupTest.txt * for details. This will only work 100% correctly with FLUSH support.
                    * [1] http://jira.jboss.com/jira/browse/JGRP-236 * @author bela */ @Test(groups={Global.FLUSH},sequential=true) public class ConcurrentStartupTest extends ChannelTestBase { private AtomicInteger mod = new AtomicInteger(0); public void testConcurrentStartupWithState() { final String[] names=new String[] { "A", "B", "C", "D" }; final int count=names.length; final ConcurrentStartupChannel[] channels=new ConcurrentStartupChannel[count]; try { // Create a semaphore and take all its permits Semaphore semaphore=new Semaphore(count); semaphore.acquire(count); // Create activation threads that will block on the semaphore for(int i=0;i < count;i++) { if(i == 0) channels[i]=new ConcurrentStartupChannel(names[i], semaphore); else channels[i]=new ConcurrentStartupChannel((JChannel)channels[0].getChannel(), names[i], semaphore); channels[i].start(); semaphore.release(1); if(i == 0) Util.sleep(1500); // sleep after the first node to educe the chances of a merge } // Make sure everyone is in sync Channel[] tmp=new Channel[channels.length]; for(int i=0; i < channels.length; i++) tmp[i]=channels[i].getChannel(); Util.blockUntilViewsReceived(30000, 500, tmp); System.out.println(">>>> all nodes have the same view <<<<"); // Re-acquire the semaphore tickets; when we have them all we know the threads are done boolean acquired=semaphore.tryAcquire(count, 20, TimeUnit.SECONDS); if(!acquired) { log.warn("Most likely a bug, analyse the stack below:"); log.warn(Util.dumpThreads()); } // Sleep to ensure async messages arrive System.out.println("Waiting for all channels to have received the " + count + " messages:"); long end_time=System.currentTimeMillis() + 10000L; while(System.currentTimeMillis() < end_time) { boolean terminate=true; for(ConcurrentStartupChannel ch: channels) { if(ch.getList().size() != count) { terminate=false; break; } } if(terminate) break; else Util.sleep(500); } System.out.println("++++++++++++++++++++++++++++++++++++++++++++++++"); for(ConcurrentStartupChannel channel:channels) System.out.println(channel.getName() + ": state=" + channel.getList()); System.out.println("++++++++++++++++++++++++++++++++++++++++++++++++"); //Vladimir Oct 1st 2009: if merged occurred state is corrupted. Do not verify state in that case. for(ConcurrentStartupChannel ch: channels) { Set
                    list=ch.getList(); if(!ch.merged) assert list.size() == count : ": list is " + list + ", should have " + count + " elements"; } System.out.println(">>>> done, all messages received by all channels <<<<"); for (ConcurrentStartupChannel channel : channels) if(!channel.merged) checkEventStateTransferSequence(channel); } catch(Exception ex) { } finally { for(ConcurrentStartupChannel channel: channels) channel.getChannel().setReceiver(null); // silence the receivers so they don't logs views for(ConcurrentStartupChannel channel: channels) channel.cleanup(); } } protected int getMod() { return mod.incrementAndGet(); } protected class ConcurrentStartupChannel extends PushChannelApplicationWithSemaphore { private final Set
                    state=new HashSet
                    (); boolean merged = false; public ConcurrentStartupChannel(String name,Semaphore semaphore) throws Exception{ super(name, semaphore); } public ConcurrentStartupChannel(JChannel ch,String name,Semaphore semaphore) throws Exception{ super(ch,name, semaphore); } public void useChannel() throws Exception { channel.connect("test", null, null, 25000); // join and state transfer channel.send(null, null, channel.getAddress()); } Set
                    getList() { synchronized(state) { return state; } } public void receive(Message msg) { if(msg.getBuffer() == null) return; Address obj = (Address)msg.getObject(); log.info(channel.getAddress() + ": received " + obj); synchronized(state) { state.add(obj); } } public void viewAccepted(View new_view) { super.viewAccepted(new_view); if(new_view instanceof MergeView) { merged = true; } } @SuppressWarnings("unchecked") public void setState(byte[] state) { super.setState(state); try{ List
                    tmp = (List) Util.objectFromByteBuffer(state); synchronized(this.state) { this.state.addAll(tmp); log.info(channel.getAddress() + ": state is " + this.state); } }catch(Exception e){ e.printStackTrace(); } } public byte[] getState() { super.getState(); List
                    tmp = null; synchronized(state) { tmp = new LinkedList
                    (state); try{ return Util.objectToByteBuffer(tmp); }catch(Exception e){ e.printStackTrace(); return null; } } } public void getState(OutputStream ostream) { super.getState(ostream); ObjectOutputStream oos = null; try{ oos = new ObjectOutputStream(ostream); List
                    tmp = null; synchronized(state) { tmp = new LinkedList
                    (state); } oos.writeObject(tmp); oos.flush(); }catch(IOException e){ e.printStackTrace(); }finally{ Util.close(oos); } } @SuppressWarnings("unchecked") public void setState(InputStream istream) { super.setState(istream); ObjectInputStream ois = null; try{ ois = new ObjectInputStream(istream); List
                    tmp = (List) ois.readObject(); synchronized(state){ // state.clear(); state.addAll(tmp); log.info(channel.getAddress() + ": state is " + state); } }catch(Exception e){ e.printStackTrace(); }finally{ Util.close(ois); } } } }libjgroups-java-2.12.2.Final.orig/tests/junit/org/jgroups/tests/ConnectStressTest.java0000755000175000017500000001040211647260573030774 0ustar moellermoeller package org.jgroups.tests; import org.jgroups.Global; import org.jgroups.JChannel; import org.jgroups.View; import org.jgroups.protocols.MERGE2; import org.jgroups.protocols.pbcast.GMS; import org.jgroups.protocols.pbcast.NAKACK; import org.jgroups.stack.ProtocolStack; import org.jgroups.util.Util; import org.testng.annotations.Test; import java.util.concurrent.CyclicBarrier; /** * Creates 1 channel, then creates NUM channels, all try to join the same channel concurrently. * @author Bela Ban Nov 20 2003 */ @Test(groups={Global.STACK_INDEPENDENT}, sequential=true) public class ConnectStressTest extends ChannelTestBase { static final int NUM=20; private final CyclicBarrier barrier=new CyclicBarrier(NUM+1); private final MyThread[] threads=new MyThread[NUM]; static final String groupname="ConnectStressTest"; static void log(String msg) { System.out.println("-- [" + Thread.currentThread().getName() + "] " + msg); } public void testConcurrentJoinsAndLeaves() throws Exception { JChannel first=null; channel_conf="udp.xml"; for(int i=0; i < threads.length; i++) { JChannel ch=null; if(i == 0) { ch=createChannel(true, NUM); first=ch; } else ch=createChannel(first); ch.setName("C" + i); changeProperties(ch); threads[i]=new MyThread(ch, i+1, barrier); threads[i].start(); } barrier.await(); // causes all threads to call Channel.connect() System.out.println("*** Starting the connect phase ***"); long target_time=System.currentTimeMillis() + 60000L; while(System.currentTimeMillis() < target_time) { View view=threads[0].getChannel().getView(); if(view != null) { int size=view.size(); System.out.println("channel[0].view has " + size + " members (expected: " + NUM + ")"); if(size >= NUM) break; } Util.sleep(1000L); } for(MyThread thread: threads) { View view=thread.getChannel().getView(); if(view != null) System.out.println(thread.getName() + ": size=" + view.size() + ", view-id: " + view.getViewId()); } for(MyThread thread: threads) { View view=thread.getChannel().getView(); int size=view.size(); assert size == NUM : "view doesn't have size of " + NUM + " (has " + size + "): " + view; } System.out.println("*** Starting the disconnect phase ***"); for(int i=0; i < threads.length; i++) { MyThread thread=threads[i]; System.out.print("disconnecting " + thread.getName()); thread.disconnect(); System.out.println(" OK"); } } public static class MyThread extends Thread { private final CyclicBarrier barrier; private final JChannel ch; public MyThread(JChannel channel, int i, CyclicBarrier barrier) { super("thread #" + i); this.ch=channel; this.barrier=barrier; } public void disconnect() { ch.disconnect(); } public void close() { Util.close(ch); } public JChannel getChannel() { return ch; } public void run() { try { barrier.await(); // wait for all threads to be running ch.connect(groupname); } catch(Exception e) { } } } private static void changeProperties(JChannel ch) { ProtocolStack stack=ch.getProtocolStack(); GMS gms=(GMS)stack.findProtocol("GMS"); if(gms != null) { gms.setViewBundling(true); gms.setMaxBundlingTime(300); gms.setPrintLocalAddr(false); } MERGE2 merge=(MERGE2)stack.findProtocol("MERGE2"); if(merge != null) { merge.setMinInterval(2000); merge.setMaxInterval(5000); } NAKACK nakack=(NAKACK)stack.findProtocol(NAKACK.class); if(nakack != null) nakack.setLogDiscardMsgs(false); } } libjgroups-java-2.12.2.Final.orig/tests/junit/org/jgroups/tests/ConnectTest.java0000644000175000017500000001072311647260573027573 0ustar moellermoeller package org.jgroups.tests; import org.jgroups.*; import org.jgroups.protocols.TP; import org.jgroups.stack.ProtocolStack; import org.jgroups.util.Promise; import org.jgroups.util.Util; import org.jgroups.util.UUID; import org.testng.annotations.AfterMethod; import org.testng.annotations.Test; /** * Runs through multiple channel connect and disconnects, without closing the channel. */ @Test(groups=Global.STACK_DEPENDENT,sequential=true) public class ConnectTest extends ChannelTestBase { JChannel channel, coordinator; @AfterMethod void tearDown() throws Exception { Util.close(channel, coordinator); } @Test public void testConnectAndDisconnect() throws Exception { channel=createChannel(true); final String GROUP=getUniqueClusterName("ConnectTest"); for(int i=0; i < 5; i++) { System.out.print("Attempt #" + (i + 1)); channel.connect(GROUP); System.out.println(": OK"); channel.disconnect(); } } @Test public void testDisconnectConnectOne() throws Exception { channel=createChannel(true); changeProps(channel); channel.connect("ConnectTest.testgroup-1"); channel.disconnect(); channel.connect("ConnectTest.testgroup-2"); View view=channel.getView(); assert view.size() == 1; assert view.containsMember(channel.getAddress()); } /** * Tests connect-disconnect-connect sequence for a group with two members **/ @Test public void testDisconnectConnectTwo() throws Exception { View view; coordinator=createChannel(true); changeProps(coordinator); coordinator.connect("ConnectTest.testgroup-3"); print(coordinator, "coordinator"); view=coordinator.getView(); System.out.println("-- view for coordinator: " + view); assert view.size() == 1; channel=createChannel(coordinator); changeProps(channel); channel.connect("ConnectTest.testgroup-4"); print(channel, "channel"); view=channel.getView(); System.out.println("-- view for channel: " + view); assert view.size() == 1; channel.disconnect(); channel.connect("ConnectTest.testgroup-3"); print(channel, "channel"); view=channel.getView(); System.out.println("-- view for channel: " + view); assert view.size() == 2; assert view.containsMember(channel.getAddress()); assert view.containsMember(coordinator.getAddress()); } static void print(JChannel ch, String msg) { System.out.println(msg + ": name=" + ch.getName() + ", addr=" + ch.getAddress() + ", UUID=" + ch.getAddressAsUUID() + "\nUUID cache:\n" + UUID.printCache() + "\nLogical_addr_cache:\n" + ch.getProtocolStack().getTransport().printLogicalAddressCache()); } /** * Tests connect-disconnect-connect-send sequence for a group with two * members. Test case introduced before fixing pbcast.NAKACK * bug, which used to leave pbcast.NAKACK in a broken state after * DISCONNECT. Because of this problem, the channel couldn't be used to * multicast messages. **/ @Test public void testDisconnectConnectSendTwo() throws Exception { final Promise msgPromise=new Promise(); coordinator=createChannel(true); changeProps(coordinator); coordinator.setReceiver(new PromisedMessageListener(msgPromise)); coordinator.connect("ConnectTest.testgroup-5"); channel=createChannel(coordinator); changeProps(channel); channel.connect("ConnectTest.testgroup-6"); channel.disconnect(); channel.connect("ConnectTest.testgroup-5"); channel.send(new Message(null, null, "payload")); Message msg=msgPromise.getResult(20000); assert msg != null; assert msg.getObject().equals("payload"); } private static void changeProps(Channel ch) { ProtocolStack stack=ch.getProtocolStack(); TP transport=stack.getTransport(); transport.setLogDiscardMessages(false); } private static class PromisedMessageListener extends ReceiverAdapter { private final Promise promise; public PromisedMessageListener(Promise promise) { this.promise=promise; } public void receive(Message msg) { promise.setResult(msg); } } } libjgroups-java-2.12.2.Final.orig/tests/junit/org/jgroups/tests/Deadlock2Test.java0000644000175000017500000001517011647260573027773 0ustar moellermoeller package org.jgroups.tests; import org.jgroups.Address; import org.jgroups.Global; import org.jgroups.JChannel; import org.jgroups.Message; import org.jgroups.blocks.GroupRequest; import org.jgroups.blocks.MethodCall; import org.jgroups.blocks.RpcDispatcher; import org.jgroups.blocks.RequestOptions; import org.jgroups.util.RspList; import org.jgroups.util.Util; import org.testng.Assert; import org.testng.annotations.AfterMethod; import org.testng.annotations.Test; import java.util.Enumeration; import java.util.Vector; /** * Test the distributed RPC deadlock detection mechanism, using one or two channels. * * @author John Giorgiadis * @author Ovidiu Feodorov * * * @version $Revision: 1.23 $ */ @Test(groups=Global.STACK_DEPENDENT,sequential=true) public class Deadlock2Test extends ChannelTestBase { private String name = "Deadlock2Test"; private JChannel c1, c2; @AfterMethod void cleanup() { Util.close(c1, c2); } /** * Tests the deadlock resolution using self-calls on a single channel. The deadlock detection * is turned on so the method call should go straight through. If there is a problem, JUnit will * timeout. * * @throws Exception */ public void testOneChannel() throws Exception { c1 = createChannel(true); ServerObject serverObject = new ServerObject("obj1"); RpcDispatcher disp=new RpcDispatcher(c1, null, null, serverObject); serverObject.setRpcDispatcher(disp); c1.connect(name); Address localAddress = c1.getAddress(); // call the nested group method on itself MethodCall call = new MethodCall("outerMethod", new Object[0], new Class[0]); log("calling outerMethod() on all members"); RspList rspList = disp.callRemoteMethods(null, call, GroupRequest.GET_ALL, 0); log("results of outerMethod(): " + rspList); Assert.assertEquals(1, rspList.size()); assertEquals("outerMethod[innerMethod]", rspList.getValue(localAddress)); assertTrue(rspList.isReceived(localAddress)); assertFalse(rspList.isSuspected(localAddress)); } /** * Tests the deadlock resolution using two different channels. The deadlock detection * is turned on. It implements the following scenario: * * Channel1 Channel2 * | | * + -------------------------------> outerMethod() * | RPC * | | * | | * | | * | <-- innerMethod() <-----------------+ ---------+ * | | | * | | <-- innerMethod() * * If there is a deadlock, JUnit will timeout and fail the test. * */ public void testTwoChannels() throws Throwable { ServerObject obj1, obj2 = null; c1 = createChannel(true); obj1 = new ServerObject("obj1"); RpcDispatcher disp1=new RpcDispatcher(c1, null, null, obj1); obj1.setRpcDispatcher(disp1); c1.connect(name); c2 = createChannel(c1); obj2 = new ServerObject("obj2"); RpcDispatcher disp2=new RpcDispatcher(c2, null, null, obj2); obj2.setRpcDispatcher(disp2); c2.connect(name); Address localAddress2 = c2.getAddress(); // call a point-to-point method on Member 2 that triggers a nested distributed RPC MethodCall call = new MethodCall("outerMethod", new Object[0], new Class[0]); log("calling outerMethod() on " + localAddress2); Object retval = disp1.callRemoteMethod(localAddress2, call, GroupRequest.GET_ALL, 0); log("results of outerMethod(): " + retval); } public void testTwoChannelsWithInitialMulticast() throws Exception { ServerObject obj1, obj2 = null; c1 = createChannel(true); obj1 = new ServerObject("obj1"); RpcDispatcher disp1=new RpcDispatcher(c1, null, null, obj1); obj1.setRpcDispatcher(disp1); c1.connect(name); c2 = createChannel(c1); obj2 = new ServerObject("obj2"); RpcDispatcher disp2=new RpcDispatcher(c2, null, null, obj2); obj2.setRpcDispatcher(disp2); c2.connect(name); Vector
                    dests=new Vector
                    (); dests.add(c1.getAddress()); dests.add(c2.getAddress()); // call a point-to-point method on Member 2 that triggers a nested distributed RPC MethodCall call = new MethodCall("outerMethod", new Object[0], new Class[0]); log("calling outerMethod() on all members"); RspList rsps = disp1.callRemoteMethods(dests, call, GroupRequest.GET_ALL, 0); log("results of outerMethod():\n" + rsps); Assert.assertEquals(2, rsps.size()); } static void log(String msg) { System.out.println("[" + Thread.currentThread() + "] " + msg); } public static class ServerObject { String myName; public ServerObject(String name) { this.myName=name; } private RpcDispatcher disp; /** * The ServerObject keeps a reference to its dispatcher to be able to send nested group * RPCs. */ public void setRpcDispatcher(RpcDispatcher rpcDispatcher) { this.disp = rpcDispatcher; } public String outerMethod() { log("**** outerMethod() received, calling innerMethod() on all members"); MethodCall call = new MethodCall("innerMethod", new Object[0], new Class[0]); // RspList rspList = disp.callRemoteMethods(null, call, GroupRequest.GET_ALL, 5000); RequestOptions opts=new RequestOptions(GroupRequest.GET_ALL, 0, false, null, (byte)0); opts.setFlags(Message.OOB); RspList rspList = disp.callRemoteMethods(null, call, opts); Vector results = rspList.getResults(); log("results of calling innerMethod():\n" + rspList); StringBuilder sb=new StringBuilder("outerMethod["); for(Enumeration e = results.elements(); e.hasMoreElements(); ) { String s = (String)e.nextElement(); sb.append(s); if (e.hasMoreElements()) { sb.append(";"); } } sb.append("]"); return sb.toString(); } public static String innerMethod() { log("**** innerMethod() received, returning result"); return "innerMethod"; } } } libjgroups-java-2.12.2.Final.orig/tests/junit/org/jgroups/tests/DiscardTest.java0000644000175000017500000001202111647260573027544 0ustar moellermoellerpackage org.jgroups.tests; import org.testng.annotations.*; import org.jgroups.*; import org.jgroups.protocols.DISCARD; import org.jgroups.stack.ProtocolStack; import org.jgroups.util.Promise; import org.jgroups.util.Util; import org.testng.Assert; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import java.util.Properties; /** * Tests the NAKACK (retransmission) and STABLE (garbage collection) protocols * by discarding 10% of all network-bound messages * * @author Bela Ban */ @Test(groups=Global.STACK_DEPENDENT,sequential=true) public class DiscardTest extends ChannelTestBase { JChannel ch1, ch2; static final long NUM_MSGS=10000; static final int MSG_SIZE=1000; private static final String GROUP="DiscardTest"; final Promise ch1_all_received=new Promise(); final Promise ch2_all_received=new Promise(); @BeforeMethod protected void setUp() throws Exception { ch1_all_received.reset(); ch2_all_received.reset(); } @AfterMethod protected void tearDown() throws Exception{ Util.close(ch2,ch1); } public void testDiscardProperties() throws Exception { _testLosslessReception(true); } public void testFastProperties() throws Exception { _testLosslessReception(false); } private void _testLosslessReception(boolean discard) throws Exception { Address ch1_addr, ch2_addr; long start, stop; ch1=createChannel(true); ch1.setReceiver(new MyReceiver(ch1_all_received, NUM_MSGS, "ch1")); ch2=createChannel(ch1); ch2.setReceiver(new MyReceiver(ch2_all_received, NUM_MSGS, "ch2")); if(discard) { DISCARD discard_prot=new DISCARD(); Properties properties=new Properties(); properties.setProperty("down", "0.1"); ch1.getProtocolStack().insertProtocol(discard_prot, ProtocolStack.BELOW, "MERGE2"); discard_prot=new DISCARD(); properties=new Properties(); properties.setProperty("down", "0.1"); ch2.getProtocolStack().insertProtocol(discard_prot, ProtocolStack.BELOW, "MERGE2"); } ch1.connect(GROUP); ch1_addr=ch1.getAddress(); ch2.connect(GROUP); ch2_addr=ch2.getAddress(); Util.sleep(2000); View v=ch2.getView(); System.out.println("**** ch2's view: " + v); Assert.assertEquals(2, v.size()); assertTrue(v.getMembers().contains(ch1_addr)); assertTrue(v.getMembers().contains(ch2_addr)); System.out.println("sending " + NUM_MSGS + " 1K messages to all members (including myself)"); start=System.currentTimeMillis(); for(int i=0;i < NUM_MSGS;i++) { Message msg=createMessage(MSG_SIZE); ch1.send(msg); if(i % 1000 == 0) System.out.println("-- sent " + i + " messages"); } System.out.println("-- waiting for ch1 and ch2 to receive " + NUM_MSGS + " messages"); Long num_msgs; num_msgs=ch1_all_received.getResult(); System.out.println("-- received " + num_msgs + " messages on ch1"); num_msgs=ch2_all_received.getResult(); stop=System.currentTimeMillis(); System.out.println("-- received " + num_msgs + " messages on ch2"); long diff=stop - start; double msgs_sec=NUM_MSGS / (diff / 1000.0); System.out.println("== Sent and received " + NUM_MSGS + " in " + diff + "ms, " + msgs_sec + " msgs/sec"); } static class MyReceiver extends ReceiverAdapter { final Promise p; final long num_msgs_expected; long num_msgs=0; String channel_name; boolean operational=true; public MyReceiver(final Promise p,final long num_msgs_expected,String channel_name) { this.p=p; this.num_msgs_expected=num_msgs_expected; this.channel_name=channel_name; } public void receive(Message msg) { if(!operational) return; num_msgs++; if(num_msgs > 0 && num_msgs % 1000 == 0) System.out.println("-- received " + num_msgs + " on " + channel_name); if(num_msgs >= num_msgs_expected) { System.out.println("SUCCESS: received all " + num_msgs_expected + " messages on " + channel_name); operational=false; p.setResult(new Long(num_msgs)); } } public void viewAccepted(View new_view) { System.out.println("-- view (" + channel_name + "): " + new_view); } } private static Message createMessage(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); } } libjgroups-java-2.12.2.Final.orig/tests/junit/org/jgroups/tests/DuplicateTest.java0000644000175000017500000002565511647260573030126 0ustar moellermoellerpackage org.jgroups.tests; import org.jgroups.*; import org.jgroups.protocols.DUPL; import org.jgroups.protocols.UNICAST2; import org.jgroups.protocols.pbcast.NAKACK; import org.jgroups.protocols.pbcast.STABLE; import org.jgroups.stack.ProtocolStack; import org.jgroups.util.Tuple; import org.jgroups.util.Util; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.Collection; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentMap; /** * Tests whether UNICAST or NAKACK prevent delivery of duplicate messages. JGroups guarantees that a message is * delivered once and only once. The test inserts DUPL below both UNICAST and NAKACK and makes it duplicate (1) * unicast, (2) multicast, (3) regular and (4) OOB messages. The receiver(s) then check for the presence of duplicate * messages. * @author Bela Ban */ @Test(groups=Global.STACK_DEPENDENT,sequential=true) public class DuplicateTest extends ChannelTestBase { private JChannel c1, c2, c3; protected Address a1, a2, a3; private MyReceiver r1, r2, r3; @BeforeMethod void init() throws Exception { createChannels(true, true, (short)5, (short)5); c1.setName("C1"); c2.setName("C2"); c3.setName("C3"); a1=c1.getAddress(); a2=c2.getAddress(); a3=c3.getAddress(); r1=new MyReceiver("C1"); r2=new MyReceiver("C2"); r3=new MyReceiver("C3"); c1.setReceiver(r1); c2.setReceiver(r2); c3.setReceiver(r3); } @AfterMethod void tearDown() throws Exception { removeDUPL(c3, c2, c1); Util.close(c3, c2, c1); } public void testRegularUnicastsToSelf() throws Exception { send(c1, c1.getAddress(), false, 10); sendStableMessages(c1, c2, c3); check(r1, 1, false, new Tuple(a1, 10)); } public void testOOBUnicastsToSelf() throws Exception { send(c1, c1.getAddress(), true, 10); sendStableMessages(c1,c2,c3); check(r1, 1, true, new Tuple(a1, 10)); } public void testRegularUnicastsToOthers() throws Exception { send(c1, c2.getAddress(), false, 10); send(c1, c3.getAddress(), false, 10); sendStableMessages(c1,c2,c3); check(r2, 1, false, new Tuple(a1, 10)); check(r3, 1, false, new Tuple(a1, 10)); } @Test(invocationCount=10) public void testOOBUnicastsToOthers() throws Exception { send(c1, c2.getAddress(), true, 10); send(c1, c3.getAddress(), true, 10); sendStableMessages(c1,c2,c3); check(r2, 1, true, new Tuple(a1, 10)); check(r3, 1, true, new Tuple(a1, 10)); } public void testRegularMulticastToAll() throws Exception { send(c1, null /** multicast */, false, 10); sendStableMessages(c1,c2,c3); check(r1, 1, false, new Tuple(a1, 10)); check(r2, 1, false, new Tuple(a1, 10)); check(r3, 1, false, new Tuple(a1, 10)); } public void testOOBMulticastToAll() throws Exception { send(c1, null /** multicast */, true, 10); sendStableMessages(c1,c2,c3); check(r1, 1, true, new Tuple(a1, 10)); check(r2, 1, true, new Tuple(a1, 10)); check(r3, 1, true, new Tuple(a1, 10)); } public void testRegularMulticastToAll3Senders() throws Exception { send(c1, null /** multicast */, false, 10); send(c2, null /** multicast */, false, 10); send(c3, null /** multicast */, false, 10); sendStableMessages(c1,c2,c3); check(r1, 3, false, new Tuple(a1, 10), new Tuple(a2, 10), new Tuple(a3, 10)); check(r2, 3, false, new Tuple(a1, 10), new Tuple(a2, 10), new Tuple(a3, 10)); check(r3, 3, false, new Tuple(a1, 10), new Tuple(a2, 10), new Tuple(a3, 10)); } @Test(invocationCount=5) public void testOOBMulticastToAll3Senders() throws Exception { send(c1, null /** multicast */, true, 10); send(c2, null /** multicast */, true, 10); send(c3, null /** multicast */, true, 10); sendStableMessages(c1,c2,c3); check(r1, 3, true, new Tuple(a1, 10), new Tuple(a2, 10), new Tuple(a3, 10)); check(r2, 3, true, new Tuple(a1, 10), new Tuple(a2, 10), new Tuple(a3, 10)); check(r3, 3, true, new Tuple(a1, 10), new Tuple(a2, 10), new Tuple(a3, 10)); } public void testMixedMulticastsToAll3Members() throws Exception { send(c1, null /** multicast */, false, true, 10); send(c2, null /** multicast */, false, true, 10); send(c3, null /** multicast */, false, true, 10); sendStableMessages(c1,c2,c3); check(r1, 3, true, new Tuple(a1, 10), new Tuple(a2, 10), new Tuple(a3, 10)); check(r2, 3, true, new Tuple(a1, 10), new Tuple(a2, 10), new Tuple(a3, 10)); check(r3, 3, true, new Tuple(a1, 10), new Tuple(a2, 10), new Tuple(a3, 10)); } private static void send(Channel sender_channel, Address dest, boolean oob, int num_msgs) throws Exception { send(sender_channel, dest, oob, false, num_msgs); } private static void send(Channel sender_channel, Address dest, boolean oob, boolean mixed, int num_msgs) throws Exception { long seqno=1; for(int i=0; i < num_msgs; i++) { Message msg=new Message(dest, null, seqno++); if(mixed) { if(i % 2 == 0) msg.setFlag(Message.OOB); } else if(oob) { msg.setFlag(Message.OOB); } sender_channel.send(msg); } } private static void sendStableMessages(JChannel ... channels) { for(JChannel ch: channels) { STABLE stable=(STABLE)ch.getProtocolStack().findProtocol(STABLE.class); if(stable != null) stable.runMessageGarbageCollection(); } } protected static void removeDUPL(JChannel ... channels) { for(JChannel ch: channels) { DUPL dupl=(DUPL)ch.getProtocolStack().findProtocol(DUPL.class); if(dupl != null) { dupl.setCopyMulticastMsgs(false); dupl.setCopyUnicastMsgs(false); } } } private static void sendUnicastStableMessages(JChannel ... channels) { for(JChannel ch: channels) { UNICAST2 unicast=(UNICAST2)ch.getProtocolStack().findProtocol(UNICAST2.class); if(unicast != null) unicast.sendStableMessages(); } } private void createChannels(boolean copy_multicasts, boolean copy_unicasts, int num_outgoing_copies, int num_incoming_copies) throws Exception { c1=createChannel(true, 3); DUPL dupl=new DUPL(copy_multicasts, copy_unicasts, num_incoming_copies, num_outgoing_copies); ProtocolStack stack=c1.getProtocolStack(); stack.insertProtocol(dupl, ProtocolStack.BELOW, NAKACK.class); c2=createChannel(c1); c3=createChannel(c1); c1.connect("DuplicateTest"); c2.connect("DuplicateTest"); c3.connect("DuplicateTest"); assert c3.getView().size() == 3 : "view was " + c1.getView() + " but should have been 3"; } private void check(MyReceiver receiver, int expected_size, boolean oob, Tuple... vals) { Map> msgs=receiver.getMsgs(); for(int i=0; i < 10; i++) { if(msgs.size() >= expected_size) break; Util.sleep(1000); } assert msgs.size() == expected_size : "expected size=" + expected_size + ", msgs: " + msgs.keySet(); for(Tuple tuple: vals) { Address addr=tuple.getVal1(); Collection list=msgs.get(addr); assert list != null : "no list available for " + addr; int expected_values=tuple.getVal2(); for(int i=0; i < 20; i++) { if(list.size() >= expected_values) break; Util.sleep(1000); sendStableMessages(c1,c2,c3); } System.out.println("[" + receiver.getName() + "]: " + addr + ": " + list); assert list.size() == expected_values : addr + "'s list's size is not " + tuple.getVal2() + ", list: " + list + " (size=" + list.size() + ")"; if(!oob) // if OOB messages, ordering is not guaranteed check(addr, list); else checkPresence(list); } } private static void check(Address addr, Collection list) { long id=list.iterator().next(); for(long val: list) { assert val == id : "[" + addr + "]: val=" + val + " (expected " + id + "): list is " + list; id++; } } private static void checkPresence(Collection list) { for(long l=1; l <= 10; l++) { assert list.contains(l) : l + " is not in the list " + list; } } private static class MyReceiver extends ReceiverAdapter { final String name; private final ConcurrentMap> msgs=new ConcurrentHashMap>(); public MyReceiver(String name) { this.name=name; } public String getName() { return name; } public Map> getMsgs() { return msgs; } public void receive(Message msg) { Address addr=msg.getSrc(); Long val=(Long)msg.getObject(); Collection list=msgs.get(addr); if(list == null) { list=new ConcurrentLinkedQueue(); Collection tmp=msgs.putIfAbsent(addr, list); if(tmp != null) list=tmp; } list.add(val); } public void clear() { msgs.clear(); } public String toString() { StringBuilder sb=new StringBuilder(); sb.append("receiver " + name).append(":\n"); for(Map.Entry> entry: msgs.entrySet()) { sb.append(entry.getKey()).append(": ").append(entry.getValue()).append("\n"); } return sb.toString(); } } }libjgroups-java-2.12.2.Final.orig/tests/junit/org/jgroups/tests/EncryptMessageOrderTestCase.java0000644000175000017500000002515511647260573032730 0ustar moellermoellerpackage org.jgroups.tests; import org.jgroups.*; import org.jgroups.conf.ClassConfigurator; import org.testng.Assert; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeClass; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.io.*; 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; *
                    */ @Test(groups={Global.STACK_INDEPENDENT,"broken"},sequential=true) public class EncryptMessageOrderTestCase { 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"; static final short ID=100; boolean orderCounterFailure = false; public static final String properties ="EncryptNoKeyStore.xml"; protected JChannel channel1; protected JChannel channel2; /** * 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); } @BeforeClass static void init() { ClassConfigurator.add((short)24526, EncryptOrderTestHeader.class); } /** * Set up unit test. It might add protocol * stack debuggers if such option was selected at startup. */ @BeforeMethod protected void setUp() throws Exception { printSelectedOptions(); channel1=new JChannel(properties); System.out.print("Connecting to channel..."); channel1.connect(groupName); System.out.println("channel1 connected, view is " + channel1.getView()); channel2=new JChannel(properties); channel2.connect(groupName); System.out.println("channel2 connected, view is " + channel2.getView()); } /** * Tears down test case. This method closes all opened channels. */ @AfterMethod protected void tearDown() throws Exception { channel2.close(); channel1.close(); } protected volatile 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. */ @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(); channel1.setReceiver(new ReceiverAdapter() { private boolean started=false; private boolean stopped=false; private long counter = 0L; 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 { Assert.assertEquals(counter, ((EncryptOrderTestHeader)jgMessage.getHeader(ID)).seqno); counter++; } catch (Exception e){ e.printStackTrace(); orderCounterFailure =true; } if(!started) tooQuickMessages.add(message); else if(!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(ID, 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=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()); assert (!orderCounterFailure) : "Message ordering is incorrect - check log output"; } 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 Global.LONG_SIZE; } public void writeTo(DataOutputStream out) throws IOException { out.writeLong(seqno); } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { seqno=in.readLong(); } public EncryptOrderTestHeader copy(){ return new EncryptOrderTestHeader(seqno); } public String toString(){ StringBuilder ret = new StringBuilder(); ret.append("[ENCRYPT_ORDER_TEST: seqno=" + seqno); ret.append(']'); return ret.toString(); } } } libjgroups-java-2.12.2.Final.orig/tests/junit/org/jgroups/tests/FifoOrderTest.java0000644000175000017500000001423311647260573030061 0ustar moellermoellerpackage org.jgroups.tests; import org.jgroups.*; import org.jgroups.protocols.TP; import org.jgroups.util.Util; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.*; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicInteger; /** * Tests the concurrent stack (TP) * @author Bela Ban */ @Test(groups=Global.STACK_DEPENDENT,sequential=true) public class FifoOrderTest extends ChannelTestBase { JChannel ch1, ch2, ch3; final static int NUM=25, EXPECTED=NUM * 3; final static long SLEEPTIME=100; CyclicBarrier barrier; @BeforeMethod void setUp() throws Exception { barrier=new CyclicBarrier(4); ch1=createChannel(true,3); ch2=createChannel(ch1); ch3=createChannel(ch1); } @AfterMethod protected void tearDown() throws Exception { Util.close(ch3, ch2, ch1); barrier.reset(); } @Test public void testFifoDelivery() throws Exception { long start, stop, diff; modifyDefaultThreadPool(ch1); modifyDefaultThreadPool(ch2); modifyDefaultThreadPool(ch3); MyReceiver r1=new MyReceiver("R1"), r2=new MyReceiver("R2"), r3=new MyReceiver("R3"); ch1.setReceiver(r1); ch2.setReceiver(r2); ch3.setReceiver(r3); ch1.connect("ConcurrentStackTest"); ch2.connect("ConcurrentStackTest"); ch3.connect("ConcurrentStackTest"); View v=ch3.getView(); assert v.size() == 3 : "view is " + v; new Thread(new Sender(ch1)) {}.start(); new Thread(new Sender(ch2)) {}.start(); new Thread(new Sender(ch3)) {}.start(); barrier.await(); // start senders start=System.currentTimeMillis(); Exception ex=null; try { System.out.println("waiting for all messages to be received"); barrier.await((long)(EXPECTED * SLEEPTIME * 1.5), TimeUnit.MILLISECONDS); // wait for all receivers } catch(java.util.concurrent.TimeoutException e) { ex=e; } stop=System.currentTimeMillis(); diff=stop - start; System.out.println("Total time: " + diff + " ms\n"); checkFIFO(r1); checkFIFO(r2); checkFIFO(r3); if(ex != null) throw ex; } private void checkFIFO(MyReceiver r) { Map> map=r.getMessages(); boolean fifo=true; List
                    incorrect_receivers=new LinkedList
                    (); System.out.println("Checking FIFO for " + r.getName() + ":"); for(Address addr: map.keySet()) { List list=map.get(addr); print(addr, list); if(!verifyFIFO(list)) { fifo=false; incorrect_receivers.add(addr); } } System.out.print("\n"); if(!fifo) assert false : "The following receivers didn't receive all messages in FIFO order: " + incorrect_receivers; } private static boolean verifyFIFO(List list) { List list2=new LinkedList(list); Collections.sort(list2); return list.equals(list2); } private static void print(Address addr, List list) { StringBuilder sb=new StringBuilder(); sb.append(addr).append(": "); for(Integer i: list) sb.append(i).append(" "); System.out.println(sb); } private static void modifyDefaultThreadPool(JChannel ch1) { TP transport=ch1.getProtocolStack().getTransport(); ThreadPoolExecutor default_pool=(ThreadPoolExecutor)transport.getDefaultThreadPool(); if(default_pool != null) { default_pool.setCorePoolSize(1); default_pool.setMaximumPoolSize(100); } transport.setThreadPoolQueueEnabled(false); } private class Sender implements Runnable { final Channel ch; final Address local_addr; public Sender(Channel ch) { this.ch=ch; local_addr=ch.getAddress(); } public void run() { Message msg; try { barrier.await(); } catch(Throwable t) { return; } for(int i=1; i <= NUM; i++) { msg=new Message(null, null, new Integer(i)); try { ch.send(msg); } catch(Exception e) { e.printStackTrace(); } } } } private class Pair { K key; V val; public Pair(K key, V val) { this.key=key; this.val=val; } public String toString() { return key + "::" + val; } } private class MyReceiver extends ReceiverAdapter { String name; final ConcurrentMap> msgs=new ConcurrentHashMap>(); AtomicInteger count=new AtomicInteger(0); public MyReceiver(String name) { this.name=name; } public void receive(Message msg) { Util.sleep(SLEEPTIME); Address sender=msg.getSrc(); List list=msgs.get(sender); if(list == null) { list=new LinkedList(); List tmp=msgs.putIfAbsent(sender, list); if(tmp != null) list=tmp; } Integer num=(Integer)msg.getObject(); list.add(num); // no concurrent access: FIFO per sender ! (No need to synchronize on list) if(count.incrementAndGet() >= EXPECTED) { System.out.println("[" + name + "]: received all messages (" + count.get() + ")"); try { barrier.await(); } catch(Exception e) { e.printStackTrace(); } } } public ConcurrentMap> getMessages() {return msgs;} public String getName() { return name; } } } libjgroups-java-2.12.2.Final.orig/tests/junit/org/jgroups/tests/FlushCloseOpenTest.java0000644000175000017500000000756511647260573031105 0ustar moellermoellerpackage org.jgroups.tests; import org.jgroups.*; import org.jgroups.util.Util; import org.testng.annotations.Test; import java.io.*; import java.util.concurrent.atomic.AtomicLong; import java.util.List; import java.util.ArrayList; /** * * coord.stopFlush() and coord.close() called in sequence cause flush lockup * See https://jira.jboss.org/jira/browse/JGRP-959 * * * @author vladimir * @since 2.8 */ @Test(groups = Global.FLUSH, sequential = true) public class FlushCloseOpenTest extends ChannelTestBase { @Test public void testAndLoop() throws Exception { for (int i = 1; i <= 4; i++) { Channel channel = createChannel(true, 2); ReceiverImpl receiver = new ReceiverImpl(); channel.setReceiver(receiver); channel.setName("A"); channel.connect("testClust"); Channel channel2 = createChannel((JChannel) channel); ReceiverImpl receiver2 = new ReceiverImpl(); channel2.setReceiver(receiver2); channel.setName("B"); channel2.connect("testClust"); sendMessage(channel, "msg1"); sendMessage(channel2, "msg2"); assert Util.startFlush(channel); assertCount(receiver, 2, receiver2, 2); channel.stopFlush(); channel.close(); channel = createChannel((JChannel) channel2); channel.setReceiver(receiver); channel.setName("A"); channel.connect("testClust"); sendMessage(channel2, "msg3"); assert Util.startFlush(channel2); assertCount(receiver, 3, receiver2, 3); channel.stopFlush(); channel2.close(); channel2 = createChannel((JChannel) channel); channel2.setReceiver(receiver2); channel.setName("B"); channel2.connect("testClust"); sendMessage(channel2, "msg4"); assert Util.startFlush(channel2); assertCount(receiver, 4, receiver2, 4); channel2.stopFlush(); channel.close(); channel2.close(); receiver.receiveCount.set(0); receiver2.receiveCount.set(0); System.out.println("***** Round " + i + " done *****"); } } private void sendMessage(Channel channel, Serializable obj) throws Exception { if (!channel.isConnected()) { log.warn("Channel disconnected in send, discarding msg"); return; } Message msg = new Message(null, null, obj); log.debug("Sending message: " + msg); channel.send(msg); log.debug("Sent message: " + msg); } private void assertCount(ReceiverImpl srv1, long srv1Count, ReceiverImpl srv2, long srv2Count) throws InterruptedException { long start = System.currentTimeMillis(); for (int i = 0; i < 1000; i++) { if (srv1.receiveCount.get() == srv1Count && srv2.receiveCount.get() == srv2Count) { break; } Thread.sleep(10L); } assert srv1Count == srv1.receiveCount.get() : "expected " + srv1Count + " but got " + srv1.receiveCount; assert srv2Count == srv2.receiveCount.get() : "expected " + srv2Count + " but got " + srv2.receiveCount; log.info("assert OK in " + (System.currentTimeMillis() - start) + "ms"); } private class ReceiverImpl extends ReceiverAdapter { final List msgs = new ArrayList(); public final AtomicLong receiveCount = new AtomicLong(); public List getMsgs() { return msgs; } @Override public void receive(Message msg) { try { Object data = msg.getObject(); msgs.add(data); receiveCount.incrementAndGet(); log.debug("Received msg: " + data); } catch (Exception e) { log.error("Receive failed", e); } } @Override public void viewAccepted(View new_view) { } } }libjgroups-java-2.12.2.Final.orig/tests/junit/org/jgroups/tests/FlushTest.java0000644000175000017500000004267711647260573027300 0ustar moellermoellerpackage org.jgroups.tests; import org.jgroups.*; import org.jgroups.protocols.FD; import org.jgroups.protocols.FD_ALL; import org.jgroups.protocols.pbcast.FLUSH; import org.jgroups.util.Util; import org.testng.annotations.Test; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; /** * Tests the FLUSH protocol. Adds a FLUSH layer on top of the stack unless already present. Should * work with any stack. * * @author Bela Ban */ @Test(groups = Global.FLUSH, sequential = false) public class FlushTest extends ChannelTestBase { @Test public void testSingleChannel() throws Exception { Semaphore s = new Semaphore(1); FlushTestReceiver receivers[] = new FlushTestReceiver[] { new FlushTestReceiver("c1", s, 0, FlushTestReceiver.CONNECT_ONLY) }; receivers[0].start(); s.release(1); // Make sure everyone is in sync Channel[] tmp = new Channel[receivers.length]; for (int i = 0; i < receivers.length; i++) tmp[i] = receivers[i].getChannel(); Util.blockUntilViewsReceived(60000, 1000, tmp); // Reacquire the semaphore tickets; when we have them all // we know the threads are done s.tryAcquire(1, 60, TimeUnit.SECONDS); receivers[0].cleanup(); Util.sleep(1000); checkEventStateTransferSequence(receivers[0]); } /** * Tests issue #1 in http://jira.jboss.com/jira/browse/JGRP-335 * * @throws Exception */ @Test public void testJoinFollowedByUnicast() throws Exception { JChannel c1 = null; JChannel c2 = null; try { c1 = createChannel(true, 2); c1.setReceiver(new SimpleReplier(c1, true)); c1.connect("testJoinFollowedByUnicast"); Address target = c1.getAddress(); Message unicast_msg = new Message(target); c2 = createChannel(c1); c2.setReceiver(new SimpleReplier(c2, false)); c2.connect("testJoinFollowedByUnicast"); // now send unicast, this might block as described in the case c2.send(unicast_msg); // if we don't get here this means we'd time out } finally { Util.close(c2, c1); } } /** * Tests issue #2 in http://jira.jboss.com/jira/browse/JGRP-335 * * @throws Exception */ @Test public void testStateTransferFollowedByUnicast() throws Exception { JChannel c1 = null; JChannel c2 = null; try { c1 = createChannel(true, 2); c1.setReceiver(new SimpleReplier(c1, true)); c1.connect("testStateTransferFollowedByUnicast"); Address target = c1.getAddress(); Message unicast_msg = new Message(target); c2 = createChannel(c1); c2.setReceiver(new SimpleReplier(c2, false)); c2.connect("testStateTransferFollowedByUnicast"); log.info("\n** Getting the state **"); c2.getState(null, 10000); // now send unicast, this might block as described in the case c2.send(unicast_msg); } finally { Util.close(c2, c1); } } @Test public void testSequentialFlushInvocation() throws Exception { Channel channel = null, channel2 = null, channel3 = null; try { channel = createChannel(true, 3); channel.setName("A"); channel2 = createChannel((JChannel) channel); channel2.setName("B"); channel3 = createChannel((JChannel) channel); channel3.setName("C"); channel.connect("x"); channel2.connect("x"); channel3.connect("x"); //we need to sleep here since coordinator (channel) //might be unblocked before channel3.connect() returns Util.sleep(500); for (int i = 0; i < 100; i++) { System.out.print("flush #" + i + ": "); long start = System.currentTimeMillis(); boolean status = channel.startFlush(false); channel.stopFlush(); long diff = System.currentTimeMillis() - start; System.out.println(status ? " OK (in " + diff + " ms)" : " FAIL"); assert status; } } finally { Util.close(channel, channel2, channel3); } } @Test public void testFlushWithCrashedFlushCoordinator() throws Exception { JChannel c1 = null; JChannel c2 = null; JChannel c3 = null; try { c1 = createChannel(true, 3, "C1"); changeProps(c1); c1.connect("testFlushWithCrashedFlushCoordinator"); c2 = createChannel(c1, "C2"); changeProps(c2); c2.connect("testFlushWithCrashedFlushCoordinator"); c3 = createChannel(c1, "C3"); changeProps(c3); c3.connect("testFlushWithCrashedFlushCoordinator"); System.out.println("shutting down flush coordinator C2"); // send out START_FLUSH and then return c2.down(new Event(Event.SUSPEND_BUT_FAIL)); // now shut down C2. This means, after failure detection kicks in and the new coordinator takes over // (either C1 or C3), that the current flush started by C2 will be cancelled and a new flush (by C1 or C3) // will be started Util.shutdown(c2); c1.getProtocolStack().findProtocol(FLUSH.class).setLevel("trace"); c3.getProtocolStack().findProtocol(FLUSH.class).setLevel("trace"); Util.blockUntilViewsReceived(10000, 500, c1, c3); // cluster should not hang and two remaining members should have a correct view assertTrue("correct view size", c1.getView().size() == 2); assertTrue("correct view size", c3.getView().size() == 2); c1.getProtocolStack().findProtocol(FLUSH.class).setLevel("warn"); c3.getProtocolStack().findProtocol(FLUSH.class).setLevel("warn"); } finally { Util.close(c3, c2, c1); } } @Test public void testFlushWithCrashedParticipant() throws Exception { JChannel c1 = null; JChannel c2 = null; JChannel c3 = null; try { c1 = createChannel(true, 3, "C1"); changeProps(c1); c1.connect("testFlushWithCrashedParticipant"); c2 = createChannel(c1, "C2"); changeProps(c2); c2.connect("testFlushWithCrashedParticipant"); c3 = createChannel(c1, "C3"); changeProps(c3); c3.connect("testFlushWithCrashedParticipant"); System.out.println("shutting down C3"); Util.shutdown(c3); // kill a flush participant System.out.println("C2: starting flush"); boolean rc=Util.startFlush(c2); System.out.println("flush " + (rc? " was successful" : "failed")); assert rc; System.out.println("stopping flush"); c2.stopFlush(); System.out.println("waiting for view to contain C1 and C2"); Util.blockUntilViewsReceived(10000, 500, c1, c2); // cluster should not hang and two remaining members should have a correct view System.out.println("C1: view=" + c1.getView() + "\nC2: view=" + c2.getView()); assertTrue("correct view size", c1.getView().size() == 2); assertTrue("correct view size", c2.getView().size() == 2); } finally { Util.close(c3, c2, c1); } } @Test public void testFlushWithCrashedParticipants() throws Exception { JChannel c1 = null; JChannel c2 = null; JChannel c3 = null; try { c1 = createChannel(true, 3, "C1"); changeProps(c1); c1.connect("testFlushWithCrashedFlushCoordinator"); c2 = createChannel(c1, "C2"); changeProps(c2); c2.connect("testFlushWithCrashedFlushCoordinator"); c3 = createChannel(c1, "C3"); changeProps(c3); c3.connect("testFlushWithCrashedFlushCoordinator"); // and then kill members other than flush coordinator Util.shutdown(c3); Util.shutdown(c1); // start flush Util.startFlush(c2); c2.stopFlush(); Util.blockUntilViewsReceived(10000, 500, c2); // cluster should not hang and one remaining member should have a correct view assertTrue("correct view size", c2.getView().size() == 1); } finally { Util.close(c3, c2, c1); } } /** * Tests http://jira.jboss.com/jira/browse/JGRP-661 * * @throws Exception */ @Test public void testPartialFlush() throws Exception { JChannel c1 = null; JChannel c2 = null; try { c1 = createChannel(true, 2); c1.setReceiver(new SimpleReplier(c1, true)); c1.connect("testPartialFlush"); c2 = createChannel(c1); c2.setReceiver(new SimpleReplier(c2, false)); c2.connect("testPartialFlush"); List
                    members = new ArrayList
                    (); members.add(c2.getLocalAddress()); boolean flushedOk = Util.startFlush(c2, members); assertTrue("Partial flush worked", flushedOk); c2.stopFlush(members); } finally { Util.close(c2, c1); } } /** Tests the emition of block/unblock/get|set state events */ @Test public void testBlockingNoStateTransfer() throws Exception { String[] names = { "A", "B", "C", "D" }; _testChannels(names, FlushTestReceiver.CONNECT_ONLY); } /** Tests the emition of block/unblock/get|set state events */ @Test public void testBlockingWithStateTransfer() throws Exception { String[] names = { "A", "B", "C", "D" }; _testChannels(names, FlushTestReceiver.CONNECT_AND_SEPARATE_GET_STATE); } /** Tests the emition of block/unblock/get|set state events */ @Test public void testBlockingWithConnectAndStateTransfer() throws Exception { String[] names = { "A", "B", "C", "D" }; _testChannels(names, FlushTestReceiver.CONNECT_AND_GET_STATE); } private void _testChannels(String names[], int connectType) throws Exception { int count = names.length; List channels = new ArrayList(count); try { // Create a semaphore and take all its permits Semaphore semaphore = new Semaphore(count); semaphore.acquire(count); // Create channels and their threads that will block on the // semaphore boolean first = true; for (String channelName : names) { FlushTestReceiver channel = null; if (first) channel = new FlushTestReceiver(channelName, semaphore, 0, connectType); else { channel = new FlushTestReceiver((JChannel) channels.get(0).getChannel(), channelName, semaphore, 0, connectType); } channels.add(channel); // Release one ticket at a time to allow the thread to start working channel.start(); semaphore.release(1); if (first) Util.sleep(3000); // minimize changes of a merge happening first = false; } Channel[] tmp = new Channel[channels.size()]; int cnt = 0; for (FlushTestReceiver receiver : channels) tmp[cnt++] = receiver.getChannel(); Util.blockUntilViewsReceived(30000, 1000, tmp); // Reacquire the semaphore tickets; when we have them all // we know the threads are done semaphore.tryAcquire(count, 40, TimeUnit.SECONDS); Util.sleep(1000); //let all events propagate... for (FlushTestReceiver app : channels) app.getChannel().setReceiver(null); for (FlushTestReceiver app : channels) app.cleanup(); // verify block/unblock/view/get|set state sequences for all members for (FlushTestReceiver receiver : channels) { checkEventStateTransferSequence(receiver); System.out.println("event sequence is OK"); } } finally { for (FlushTestReceiver app : channels) app.cleanup(); } } private static void changeProps(JChannel ... channels) { for(JChannel ch: channels) { FD fd=(FD)ch.getProtocolStack().findProtocol(FD.class); if(fd != null) { fd.setTimeout(1000); fd.setMaxTries(2); } FD_ALL fd_all=(FD_ALL)ch.getProtocolStack().findProtocol(FD_ALL.class); if(fd_all != null) { fd_all.setTimeout(2000); fd_all.setInterval(800); } } } private class FlushTestReceiver extends PushChannelApplicationWithSemaphore { private int connectMethod; public static final int CONNECT_ONLY = 1; public static final int CONNECT_AND_SEPARATE_GET_STATE = 2; public static final int CONNECT_AND_GET_STATE = 3; int msgCount = 0; protected FlushTestReceiver(String name, Semaphore semaphore, int msgCount, int connectMethod) throws Exception { super(name, semaphore); this.connectMethod = connectMethod; this.msgCount = msgCount; events = Collections.synchronizedList(new LinkedList()); if (connectMethod == CONNECT_ONLY || connectMethod == CONNECT_AND_SEPARATE_GET_STATE) channel.connect("FlushTestReceiver"); if (connectMethod == CONNECT_AND_GET_STATE) { channel.connect("FlushTestReceiver", null, null, 25000); } } protected FlushTestReceiver(JChannel ch, String name, Semaphore semaphore, int msgCount, int connectMethod) throws Exception { super(ch, name, semaphore); this.connectMethod = connectMethod; this.msgCount = msgCount; events = Collections.synchronizedList(new LinkedList()); if (connectMethod == CONNECT_ONLY || connectMethod == CONNECT_AND_SEPARATE_GET_STATE) channel.connect("FlushTestReceiver"); if (connectMethod == CONNECT_AND_GET_STATE) { channel.connect("FlushTestReceiver", null, null, 25000); } } public List getEvents() { return new LinkedList(events); } public byte[] getState() { events.add(new GetStateEvent(null, null)); return new byte[] { 'b', 'e', 'l', 'a' }; } public void getState(OutputStream ostream) { super.getState(ostream); byte[] payload = new byte[] { 'b', 'e', 'l', 'a' }; try { ostream.write(payload); } catch (IOException e) { e.printStackTrace(); } finally { Util.close(ostream); } } public void setState(InputStream istream) { super.setState(istream); byte[] payload = new byte[4]; try { istream.read(payload); } catch (IOException e) { e.printStackTrace(); } finally { Util.close(istream); } } protected void useChannel() throws Exception { if (connectMethod == CONNECT_AND_SEPARATE_GET_STATE) { channel.getState(null, 25000); } if (msgCount > 0) { for (int i = 0; i < msgCount; i++) { channel.send(new Message()); Util.sleep(100); } } } } private class SimpleReplier extends ExtendedReceiverAdapter { Channel channel; boolean handle_requests = false; public SimpleReplier(Channel channel, boolean handle_requests) { this.channel = channel; this.handle_requests = handle_requests; } public void receive(Message msg) { Message reply = new Message(msg.getSrc()); try { log.info("-- MySimpleReplier[" + channel.getAddress() + "]: received message from " + msg.getSrc()); if (handle_requests) { log.info(", sending reply"); channel.send(reply); } else System.out.println("\n"); } catch (Exception e) { e.printStackTrace(); } } public void viewAccepted(View new_view) { log.info("-- MySimpleReplier[" + channel.getAddress() + "]: viewAccepted(" + new_view + ")"); } public void block() { log.info("-- MySimpleReplier[" + channel.getAddress() + "]: block()"); } public void unblock() { log.info("-- MySimpleReplier[" + channel.getAddress() + "]: unblock()"); } } }././@LongLink0000000000000000000000000000015200000000000011563 Lustar rootrootlibjgroups-java-2.12.2.Final.orig/tests/junit/org/jgroups/tests/FlushWithChannelJoinsAndFailuresTest.javalibjgroups-java-2.12.2.Final.orig/tests/junit/org/jgroups/tests/FlushWithChannelJoinsAndFailuresTest0000644000175000017500000000303011647260573033602 0ustar moellermoellerpackage org.jgroups.tests; import org.jgroups.Channel; import org.jgroups.Global; import org.jgroups.JChannel; import org.jgroups.util.Util; import org.testng.annotations.Test; /** * * Flush and join problems during constant node failures and constant joins * https://jira.jboss.org/jira/browse/JGRP-985 * * * @author vladimir * @since 2.8 */ @Test(groups = Global.FLUSH, sequential = true) public class FlushWithChannelJoinsAndFailuresTest extends ChannelTestBase { private static final String cName = "FlushWithChannelFailuresTest"; @Test public void testAndLoop() throws Exception { int numChannels = 10; Channel channels[] = new Channel[numChannels]; for (int j = 0; j < numChannels; j++) { if (j == 0) { channels[j] = createChannel(true, numChannels); } else { channels[j] = createChannel((JChannel) channels[0]); } channels[j].connect(cName); } for (int i = 1; i <= 2; i++) { int killPositions[] = { 0, 3, 5, 8 }; for (int index : killPositions) { Util.shutdown(channels[index]); } for (int index : killPositions) { channels[index] = createChannel((JChannel) channels[1]); channels[index].connect(cName); } System.out.println("***** Round " + i + " done *****"); } for (Channel c : channels) { assert c.getView().getMembers().size() == 10; } } }libjgroups-java-2.12.2.Final.orig/tests/junit/org/jgroups/tests/GossipRouterTest.java0000644000175000017500000001017711647260573030652 0ustar moellermoellerpackage org.jgroups.tests; import org.jgroups.Global; import org.jgroups.JChannel; import org.jgroups.ReceiverAdapter; import org.jgroups.View; import org.jgroups.protocols.MERGE2; import org.jgroups.stack.GossipRouter; import org.jgroups.util.Util; import org.jgroups.util.StackType; import org.testng.annotations.AfterMethod; import org.testng.annotations.Test; import org.testng.annotations.BeforeClass; 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; /** * @author Bela Ban */ @Test(groups={Global.STACK_INDEPENDENT,Global.GOSSIP_ROUTER},sequential=true) public class GossipRouterTest { final static String PROPS="tunnel.xml"; GossipRouter router; JChannel c1, c2; String bind_addr=null; @BeforeClass protected void setUp() { bind_addr=Util.getProperty(Global.BIND_ADDR); if(bind_addr == null) { StackType type=Util.getIpStackType(); if(type == StackType.IPv6) bind_addr="::1"; else bind_addr="127.0.0.1"; } } @AfterMethod (alwaysRun=true) protected void tearDown() throws Exception { if(router != null) { router.stop(); router=null; } Util.close(c2, c1); } /** * Tests the following scenario (http://jira.jboss.com/jira/browse/JGRP-682): * - First node is started with tunnel.xml, cannot connect * - Second node is started *with* GossipRouter * - Now first node should be able to connect and first and second node should be able to merge into a group * - SUCCESS: a view of 2 */ @Test public void testLateStart() throws Exception { final Lock lock=new ReentrantLock(); final Condition cond=lock.newCondition(); AtomicBoolean done=new AtomicBoolean(false); System.out.println("-- starting first channel"); c1=new JChannel(PROPS); changeMergeInterval(c1); c1.setReceiver(new MyReceiver("c1", done, lock, cond)); c1.connect("demo"); System.out.println("-- starting second channel"); c2=new JChannel(PROPS); changeMergeInterval(c2); c2.setReceiver(new MyReceiver("c2", done, lock, cond)); c2.connect("demo"); System.out.println("-- starting GossipRouter"); router=new GossipRouter(12001, bind_addr); router.start(); System.out.println("-- waiting for merge to happen --"); long target_time=System.currentTimeMillis() + 40000; lock.lock(); try { while(System.currentTimeMillis() < target_time && done.get() == false) { cond.await(1000, TimeUnit.MILLISECONDS); } } finally { lock.unlock(); } Util.sleep(500); View view=c1.getView(); System.out.println("view=" + view); assert view.size() == 2 : "view=" + view; Util.close(c2, c1); } private static void changeMergeInterval(JChannel c1) { MERGE2 merge=(MERGE2)c1.getProtocolStack().findProtocol(MERGE2.class); if(merge != null) { merge.setMinInterval(1000); merge.setMaxInterval(3000); } } private static class MyReceiver extends ReceiverAdapter { private final String name; private final Lock lock; private final AtomicBoolean done; private final Condition cond; public MyReceiver(String name, AtomicBoolean done, Lock lock, Condition cond) { this.name=name; this.done=done; this.lock=lock; this.cond=cond; } public void viewAccepted(View new_view) { if(new_view.size() == 2) { System.out.println("[" + name + "]: view=" + new_view); lock.lock(); try { done.set(true); cond.signalAll(); } finally { lock.unlock(); } } } } } libjgroups-java-2.12.2.Final.orig/tests/junit/org/jgroups/tests/JoinTest.java0000644000175000017500000001510411647260573027077 0ustar moellermoellerpackage org.jgroups.tests; import org.jgroups.*; import org.jgroups.protocols.DELAY_JOIN_REQ; import org.jgroups.protocols.Discovery; import org.jgroups.protocols.pbcast.GMS; import org.jgroups.stack.ProtocolStack; import org.jgroups.util.Util; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.List; /** * @author Bela Ban */ @Test(groups=Global.STACK_DEPENDENT,sequential=true) public class JoinTest extends ChannelTestBase { JChannel c1, c2; @BeforeMethod public void setUp() throws Exception { c1=createChannel(true, 2); c2=createChannel(c1); } @AfterMethod public void tearDown() throws Exception { Util.close(c2,c1); } @Test public void testSingleJoin() throws ChannelException { c1.connect("JoinTest"); View v=c1.getView(); assert v != null; assert v.size() == 1; } /** * Tests that immediately after a connect(), a getView() returns the correct view * @throws ChannelException */ @Test public void testJoinsOnTwoChannels() throws ChannelException { c1.connect("JoinTest"); c2.connect("JoinTest"); Util.sleep(2000); //no blocking is used, let the view propagate View v1=c1.getView(), v2=c2.getView(); System.out.println("v1=" + v1 + ", v2=" + v2); assert v1 != null; assert v2 != null; assert v1.size() == 2; assert v2.size() == 2; assert v1.equals(v2); } @Test public void testJoinsOnTwoChannelsAndSend() throws ChannelException { c1.connect("JoinTest"); c2.connect("JoinTest"); MyReceiver r1=new MyReceiver("c1"); MyReceiver r2=new MyReceiver("c2"); c1.setReceiver(r1); c2.setReceiver(r2); Message m1=new Message(null, null, "message-1"), m2=new Message(null, null, "message-2"); c1.connect("JoinTest-2"); View view=c1.getView(); assert view.size() == 2 : "c1's view: " + view; c2.connect("JoinTest-2"); view=c2.getView(); assert view.size() == 2 : "c2's view: " + view; Util.sleep(200); view=c1.getView(); assert view.size() == 2 : "c1's view: " + view; c1.send(m1); c2.send(m2); Util.sleep(1500); Listc1_list=r1.getMsgs(), c2_list=r2.getMsgs(); System.out.println("c1: " + c1_list.size() + " msgs, c2: " + c2_list.size() + " msgs"); assert c1_list.size() == 2 : "cl_list: " + c1_list; assert c2_list.size() == 2 : "c2_list: " + c2_list; assert c1_list.contains("message-1"); assert c2_list.contains("message-1"); assert c1_list.contains("message-2"); assert c2_list.contains("message-2"); } /** * Tests the case where we send a JOIN-REQ, but get back a JOIN-RSP after GMS.join_timeout, so we've already * started another discovery. Tests whether the discovery process is cancelled correctly. * http://jira.jboss.com/jira/browse/JGRP-621 */ @Test public void testDelayedJoinResponse() throws Exception { final long JOIN_TIMEOUT=2000, DELAY_JOIN_REQ=4000; final long DISCOVERY_TIMEOUT=5000; final long TOLERANCE=1000; _testDelayedJoinResponse(DISCOVERY_TIMEOUT, JOIN_TIMEOUT, DELAY_JOIN_REQ, TOLERANCE); } @Test public void testDelayedJoinResponse2() throws Exception { final long JOIN_TIMEOUT=2000, DELAY_JOIN_REQ=4000; final long DISCOVERY_TIMEOUT=5000; final long TOLERANCE=1000; _testDelayedJoinResponse(DISCOVERY_TIMEOUT, JOIN_TIMEOUT, DELAY_JOIN_REQ, TOLERANCE); } @Test public void testDelayedJoinResponse3() throws Exception { final long JOIN_TIMEOUT=5000, DELAY_JOIN_REQ=4000; final long DISCOVERY_TIMEOUT=5000; final long TOLERANCE=1000; _testDelayedJoinResponse(DISCOVERY_TIMEOUT, JOIN_TIMEOUT, DELAY_JOIN_REQ, TOLERANCE); } @Test public void testDelayedJoinResponse4() throws Exception { final long JOIN_TIMEOUT=1000, DELAY_JOIN_REQ=4000; final long DISCOVERY_TIMEOUT=2000; final long TOLERANCE=1000; _testDelayedJoinResponse(DISCOVERY_TIMEOUT, JOIN_TIMEOUT, DELAY_JOIN_REQ, TOLERANCE); } void _testDelayedJoinResponse(long discovery_timeout, long join_timeout, long delay_join_req, long tolerance) throws Exception { c1.connect("JoinTest"); c2.connect("JoinTest"); ProtocolStack stack=c2.getProtocolStack(); GMS gms=(GMS)stack.findProtocol("GMS"); if(gms != null) { gms.setJoinTimeout(join_timeout); } Discovery discovery=(Discovery)stack.findProtocol(Discovery.class); if(discovery != null) { discovery.setNumInitialMembers(10); discovery.setTimeout(discovery_timeout); } stack=c1.getProtocolStack(); DELAY_JOIN_REQ delay=new DELAY_JOIN_REQ(); delay.setDelay(delay_join_req); stack.insertProtocol(delay, ProtocolStack.BELOW, "GMS"); System.out.println(new Date() + ": joining c2"); long start=System.currentTimeMillis(), stop; c2.connect("JoinTest-2"); stop=System.currentTimeMillis(); long join_time=stop-start; long tolerated_join_time=discovery_timeout + delay_join_req + tolerance; // 1 sec more is okay (garbage collection etc) System.out.println(new Date() + ": joining of c2 took " + join_time + " ms (should have taken not more than "+tolerated_join_time +" ms)"); assert join_time <= tolerated_join_time : "join time (" + join_time + ") was > tolerated join time (" + tolerated_join_time + ")"; } private static class MyReceiver extends ReceiverAdapter { private final String name; private final List msgs; public MyReceiver(String name) { this.name=name; msgs = Collections.synchronizedList(new ArrayList()); } public List getMsgs() { return msgs; } public void clear() {msgs.clear();} public void receive(Message msg) { String s=(String)msg.getObject(); msgs.add(s); System.out.println("[" + name + "] received " + s + " from " + msg.getSrc()); } public void viewAccepted(View new_view) { System.out.println("[" + name + "] view: " + new_view); } } } libjgroups-java-2.12.2.Final.orig/tests/junit/org/jgroups/tests/LargeStateTransferTest.java0000755000175000017500000001306611647260573031750 0ustar moellermoellerpackage org.jgroups.tests; import org.jgroups.ChannelException; import org.jgroups.ExtendedReceiverAdapter; import org.jgroups.Global; import org.jgroups.JChannel; import org.jgroups.View; import org.jgroups.protocols.TP; import org.jgroups.protocols.pbcast.GMS; import org.jgroups.stack.ProtocolStack; import org.jgroups.util.Promise; import org.jgroups.util.Util; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.io.*; /** * Tests transfer of large states (http://jira.jboss.com/jira/browse/JGRP-225). * Note that on Mac OS, FRAG2.frag_size and max_bundling_size in the transport should be less than 16'000 due to * http://jira.jboss.com/jira/browse/JGRP-560. As alternative, increase the MTU of the loopback device to a value * greater than max_bundle_size, e.g. * ifconfig lo0 mtu 65000 * @author Bela Ban */ @Test(groups={Global.STACK_DEPENDENT}, sequential=true) public class LargeStateTransferTest extends ChannelTestBase { JChannel provider, requester; Promise p=new Promise(); final static int SIZE_1=100000, SIZE_2=1000000, SIZE_3=5000000, SIZE_4=10000000; protected boolean useBlocking() { return true; } @BeforeMethod protected void setUp() throws Exception { provider=createChannel(true, 2); provider.setName("provider"); modifyStack(provider); requester=createChannel(provider); requester.setName("requester"); setOOBPoolSize(provider, requester); } @AfterMethod protected void tearDown() throws Exception { Util.close(requester, provider); } public void testStateTransfer1() throws ChannelException { _testStateTransfer(SIZE_1, "testStateTransfer1"); } public void testStateTransfer2() throws ChannelException { _testStateTransfer(SIZE_2, "testStateTransfer2"); } public void testStateTransfer3() throws ChannelException { _testStateTransfer(SIZE_3, "testStateTransfer3"); } public void testStateTransfer4() throws ChannelException { _testStateTransfer(SIZE_4, "testStateTransfer4"); } private void _testStateTransfer(int size, String suffix) throws ChannelException { final String GROUP="LargeStateTransferTest-" + suffix; provider.setReceiver(new Provider(size)); provider.connect(GROUP); p.reset(); requester.setReceiver(new Requester(p)); requester.connect(GROUP); View requester_view=requester.getView(); assert requester_view.size() == 2 : "requester view is " + requester_view + ", but should have 2 members"; View provider_view=provider.getView(); assert provider_view.size() == 2 : "provider view is " + provider_view + ", but should have 2 members"; log("requesting state of " + Util.printBytes(size)); long start=System.currentTimeMillis(); requester.getState(provider.getAddress(), 20000); Integer result=p.getResult(20000); long stop=System.currentTimeMillis(); assertNotNull(result); log("received " + Util.printBytes(result) + " (in " + (stop-start) + "ms)"); assert result == size : "result=" + result + ", expected=" + size; } private static void setOOBPoolSize(JChannel... channels) { for(JChannel channel: channels) { TP transport=channel.getProtocolStack().getTransport(); transport.setOOBThreadPoolMinThreads(1); transport.setOOBThreadPoolMaxThreads(2); } } static void log(String msg) { System.out.println(" -- "+ msg); } private static void modifyStack(JChannel ch) { ProtocolStack stack=ch.getProtocolStack(); GMS gms=(GMS)stack.findProtocol(GMS.class); if(gms != null) gms.setLogCollectMessages(false); } private static class Provider extends ExtendedReceiverAdapter { private final byte[] state; public Provider(int size) { state=new byte[size]; } public byte[] getState() { return state; } public void getState(OutputStream ostream){ DataOutputStream out =null; try{ out=new DataOutputStream(ostream); out.writeInt(state.length); out.write(state, 0, state.length); } catch (IOException e){} finally{ Util.close(out); } } public void setState(byte[] state) { throw new UnsupportedOperationException("not implemented by provider"); } } private static class Requester extends ExtendedReceiverAdapter { private final Promise promise; public Requester(Promise p) { this.promise=p; } public byte[] getState() { throw new UnsupportedOperationException("not implemented by requester"); } public void setState(byte[] state) { promise.setResult(new Integer(state.length)); } public void setState(InputStream istream) { DataInputStream in=null; int size=0; try { in=new DataInputStream(istream); size=in.readInt(); byte[] stateReceived=new byte[size]; in.readFully(stateReceived, 0, stateReceived.length); } catch (IOException e) {} finally { Util.close(in); } promise.setResult(size); } } } libjgroups-java-2.12.2.Final.orig/tests/junit/org/jgroups/tests/LastMessageDroppedTest.java0000644000175000017500000000546011647260573031732 0ustar moellermoellerpackage org.jgroups.tests; import org.jgroups.*; import org.jgroups.protocols.DISCARD; import org.jgroups.protocols.pbcast.NAKACK; import org.jgroups.protocols.pbcast.STABLE; import org.jgroups.stack.ProtocolStack; import org.jgroups.util.Util; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.ArrayList; import java.util.List; /** * Tests the last message dropped problem in NAKACK (see doc/design/varia2.txt) * @author Bela Ban */ @Test(groups=Global.STACK_DEPENDENT,sequential=true) public class LastMessageDroppedTest extends ChannelTestBase { JChannel c1, c2; @BeforeMethod void init() throws Exception { c1=createChannel(true, 2); c2=createChannel(c1); // c1.setOpt(Channel.LOCAL, false); modifyStack(c1, c2); c1.connect("LastMessageDroppedTest"); c2.connect("LastMessageDroppedTest"); View view=c2.getView(); log.info("view = " + view); assert view.size() == 2 : "view is " + view; } @AfterMethod void cleanup() { Util.close(c2, c1); } public void testLastMessageDropped() throws Exception { DISCARD discard=new DISCARD(); ProtocolStack stack=c1.getProtocolStack(); stack.insertProtocol(discard, ProtocolStack.BELOW, NAKACK.class); c1.setOpt(Channel.LOCAL, false); Message m1=new Message(null, null, 1); Message m2=new Message(null, null, 2); Message m3=new Message(null, null, 3); MyReceiver receiver=new MyReceiver(); c2.setReceiver(receiver); c1.send(m1); c1.send(m2); discard.setDropDownMulticasts(1); // drop the next multicast c1.send(m3); Util.sleep(100); List list=receiver.getMsgs(); for(int i=0; i < 10; i++) { System.out.println("list=" + list); if(list.size() == 3) break; Util.sleep(1000); } assert list.size() == 3 : "list=" + list; } private static void modifyStack(JChannel ... channels) { for(JChannel ch: channels) { ProtocolStack stack=ch.getProtocolStack(); STABLE stable=(STABLE)stack.findProtocol(STABLE.class); if(stable == null) throw new IllegalStateException("STABLE protocol was not found"); stable.setDesiredAverageGossip(2000); } } private static class MyReceiver extends ReceiverAdapter { private final List msgs=new ArrayList(3); public List getMsgs() { return msgs; } public void receive(Message msg) { // System.out.println("<< " + msg.getObject()); msgs.add((Integer)msg.getObject()); } } } libjgroups-java-2.12.2.Final.orig/tests/junit/org/jgroups/tests/MergeStressTest.java0000644000175000017500000001500611647260573030444 0ustar moellermoeller package org.jgroups.tests; import org.jgroups.*; import org.jgroups.protocols.FD_SOCK; import org.jgroups.protocols.MERGE2; import org.jgroups.protocols.pbcast.NAKACK; import org.jgroups.protocols.pbcast.STABLE; import org.jgroups.stack.Protocol; import org.jgroups.stack.ProtocolStack; import org.jgroups.util.Util; import org.testng.Assert; import org.testng.annotations.Test; import java.util.concurrent.CyclicBarrier; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; /** * Creates NUM channels, all trying to join the same channel concurrently. This * will lead to singleton groups and subsequent merging. To enable merging, * GMS.handle_concurrent_startup has to be set to false. * * @author Bela Ban */ /* * Occasionally fails under tcp stack on Cruise Control. Needs investigation * Vladimir Sept 22, 2008 * * */ @Test(groups="broken") public class MergeStressTest extends ChannelTestBase { CyclicBarrier start_connecting=null; CyclicBarrier received_all_views=null; CyclicBarrier disconnected=null; static final int NUM=10; final MyThread[] threads=new MyThread[NUM]; static String groupname="MergeStressTest"; static void log(String msg) { System.out.println("-- [" + Thread.currentThread().getName() + "] " + msg); } public void testConcurrentStartupAndMerging() throws Exception { start_connecting=new CyclicBarrier(NUM + 1); received_all_views=new CyclicBarrier(NUM + 1); disconnected=new CyclicBarrier(NUM + 1); long start, stop; JChannel first=null; for(int i=0;i < threads.length;i++) { JChannel tmp; if(i == 0) { first=createChannel(true, threads.length); tmp=first; } else { tmp=createChannel(first); } modifyStack(tmp); threads[i]=new MyThread(i, tmp); threads[i].start(); } // signal the threads to start connecting to their channels Util.sleep(1000); start_connecting.await(); start=System.currentTimeMillis(); try { received_all_views.await(45, TimeUnit.SECONDS); stop=System.currentTimeMillis(); System.out.println("-- took " + (stop - start) + " msecs for all " + NUM + " threads to see all views"); int num_members; MyThread t; System.out.print("checking that all views have " + NUM + " members: "); for(int i=0;i < threads.length;i++) { t=threads[i]; num_members=t.numMembers(); Assert.assertEquals(num_members, NUM); } System.out.println("SUCCESSFUL"); } catch(TimeoutException timeoutOnReceiveViews) { for(MyThread channel:threads) { channel.interrupt(); } } catch(Exception ex) { assert false:ex.toString(); } finally { disconnected.await(); } } private static void modifyStack(JChannel ch) { ProtocolStack stack=ch.getProtocolStack(); Protocol prot=stack.findProtocol(MERGE2.class); if(prot != null) { MERGE2 merge=(MERGE2)prot; merge.setMinInterval(3000); merge.setMaxInterval(5000); } prot=stack.findProtocol(STABLE.class); if(prot != null) { STABLE stable=(STABLE)prot; stable.setDesiredAverageGossip(5000); } prot=stack.findProtocol(NAKACK.class); if(prot != null) { ((NAKACK)prot).setLogDiscardMessages(false); } prot=stack.findProtocol(FD_SOCK.class); if(prot != null) { ((FD_SOCK)prot).setLogSuspectedMessages(false); } } public class MyThread extends ReceiverAdapter implements Runnable { int index=-1; long total_connect_time=0, total_disconnect_time=0; private final Channel ch; private Address my_addr=null; private View current_view; private Thread thread; private int num_members=0; public MyThread(int i,Channel ch) { this.ch=ch; thread=new Thread(this, "thread #" + i); index=i; } public void start() { thread.start(); } public void interrupt() { thread.interrupt(); } public void closeChannel() { Util.close(ch); } public int numMembers() { return ch.getView().size(); } public void viewAccepted(View new_view) { current_view=new_view; log("accepted " + new_view); num_members=current_view.getMembers().size(); if(num_members == NUM) { synchronized(this) { this.notifyAll(); } } } public void run() { View view; try { start_connecting.await(); ch.setReceiver(this); log("connecting to channel"); long start=System.currentTimeMillis(), stop; ch.connect(groupname); stop=System.currentTimeMillis(); total_connect_time=stop - start; view=ch.getView(); my_addr=ch.getAddress(); log(my_addr + " connected in " + total_connect_time + " msecs (" + view.getMembers().size() + " members). VID=" + ch.getView()); synchronized(this) { while(num_members < NUM && !Thread.currentThread().isInterrupted()) { try { this.wait(); } catch(InterruptedException e) { Thread.currentThread().interrupt(); } } } } catch(Exception e) { e.printStackTrace(); } finally { log("reached " + num_members + " members"); try { received_all_views.await(); Util.shutdown(ch); disconnected.await(); } catch(Exception e) { } } } } } libjgroups-java-2.12.2.Final.orig/tests/junit/org/jgroups/tests/MergeTest.java0000644000175000017500000001654111647260573027245 0ustar moellermoellerpackage org.jgroups.tests; import org.jgroups.*; import org.jgroups.protocols.MERGE2; import org.jgroups.protocols.MERGE3; import org.jgroups.protocols.MERGEFAST; import org.jgroups.protocols.pbcast.GMS; import org.jgroups.protocols.pbcast.NAKACK; import org.jgroups.stack.ProtocolStack; import org.jgroups.util.Util; import org.testng.annotations.Test; import java.util.*; /** * Tests merging on all stacks * * @author vlada */ @Test(groups=Global.FLUSH,sequential=true) public class MergeTest extends ChannelTestBase { @Test public void testMerging2Members() throws Exception { mergeHelper("MergeTest.testMerging2Members", "A", "B"); } @Test public void testMerging4Members() throws Exception { mergeHelper("MergeTest.testMerging4Members", "A", "B", "C", "D"); } protected void mergeHelper(String cluster_name, String ... members) throws Exception { JChannel[] channels=null; try { channels=createChannels(cluster_name, members); print(channels); System.out.println("\ncreating partitions: "); createPartitions(channels, members); print(channels); for(String member: members) { JChannel ch=findChannel(member, channels); assert ch.getView().size() == 1 : "view of " + ch.getAddress() + ": " + ch.getView(); } System.out.println("\n==== injecting merge event ===="); for(String member: members) { injectMergeEvent(channels, member, members); } for(int i=0; i < 20; i++) { System.out.print("."); if(allChannelsHaveViewOf(channels, members.length)) break; Util.sleep(500); } System.out.println("\n"); print(channels); assertAllChannelsHaveViewOf(channels, members.length); } finally { if(channels != null) close(channels); } } private JChannel[] createChannels(String cluster_name, String[] members) throws Exception { JChannel[] retval=new JChannel[members.length]; JChannel ch=null; for(int i=0; i < retval.length; i++) { JChannel tmp; if(ch == null) { ch=createChannel(true, members.length); tmp=ch; } else { tmp=createChannel(ch); } tmp.setName(members[i]); ProtocolStack stack=tmp.getProtocolStack(); NAKACK nakack=(NAKACK)stack.findProtocol(NAKACK.class); if(nakack != null) nakack.setLogDiscardMessages(false); stack.removeProtocol(MERGE2.class, MERGE3.class, MERGEFAST.class); tmp.connect(cluster_name); retval[i]=tmp; } return retval; } private static void close(JChannel[] channels) { if(channels == null) return; for(int i=channels.length -1; i <= 0; i--) { JChannel ch=channels[i]; Util.close(ch); } } private static void createPartitions(JChannel[] channels, String ... partitions) throws Exception { checkUniqueness(partitions); List views=new ArrayList(partitions.length); for(String partition: partitions) { View view=createView(partition, channels); views.add(view); } applyViews(views, channels); } private static void checkUniqueness(String[] ... partitions) throws Exception { Set set=new HashSet(); for(String[] partition: partitions) { for(String tmp: partition) { if(!set.add(tmp)) throw new Exception("partitions are overlapping: element " + tmp + " is in multiple partitions"); } } } private static void injectMergeEvent(JChannel[] channels, String leader, String ... coordinators) { Address leader_addr=leader != null? findAddress(leader, channels) : determineLeader(channels); injectMergeEvent(channels, leader_addr, coordinators); } private static void injectMergeEvent(JChannel[] channels, Address leader_addr, String ... coordinators) { Map views=new HashMap(); for(String tmp: coordinators) { Address coord=findAddress(tmp, channels); views.put(coord, findView(tmp, channels)); } JChannel coord=findChannel(leader_addr, channels); GMS gms=(GMS)coord.getProtocolStack().findProtocol(GMS.class); gms.setLevel("trace"); gms.up(new Event(Event.MERGE, views)); } private static View createView(String partition, JChannel[] channels) throws Exception { Vector
                    members=new Vector
                    (); Address addr=findAddress(partition, channels); if(addr == null) throw new Exception(partition + " not associated with a channel"); members.add(addr); return new View(members.firstElement(), 10, members); } private static JChannel findChannel(String tmp, JChannel[] channels) { for(JChannel ch: channels) { if(ch.getName().equals(tmp)) return ch; } return null; } private static JChannel findChannel(Address addr, JChannel[] channels) { for(JChannel ch: channels) { if(ch.getAddress().equals(addr)) return ch; } return null; } private static View findView(String tmp, JChannel[] channels) { for(JChannel ch: channels) { if(ch.getName().equals(tmp)) return ch.getView(); } return null; } private static boolean allChannelsHaveViewOf(JChannel[] channels, int count) { for(JChannel ch: channels) { if(ch.getView().size() != count) return false; } return true; } private static void assertAllChannelsHaveViewOf(JChannel[] channels, int count) { for(JChannel ch: channels) assert ch.getView().size() == count : ch.getName() + " has view " + ch.getView(); } private static Address determineLeader(JChannel[] channels, String ... coords) { Membership membership=new Membership(); for(String coord: coords) membership.add(findAddress(coord, channels)); membership.sort(); return membership.elementAt(0); } private static Address findAddress(String tmp, JChannel[] channels) { for(JChannel ch: channels) { if(ch.getName().equals(tmp)) return ch.getAddress(); } return null; } private static void applyViews(List views, JChannel[] channels) { for(View view: views) { Collection
                    members=view.getMembers(); for(JChannel ch: channels) { Address addr=ch.getAddress(); if(members.contains(addr)) { GMS gms=(GMS)ch.getProtocolStack().findProtocol(GMS.class); gms.installView(view); } } } } private static void print(JChannel[] channels) { for(JChannel ch: channels) { System.out.println(ch.getName() + ": " + ch.getView()); } } } libjgroups-java-2.12.2.Final.orig/tests/junit/org/jgroups/tests/MessageBundlingTest.java0000644000175000017500000001742711647260573031261 0ustar moellermoellerpackage org.jgroups.tests; import org.jgroups.*; import org.jgroups.protocols.TP; import org.jgroups.protocols.pbcast.GMS; import org.jgroups.stack.Protocol; import org.jgroups.stack.ProtocolStack; import org.jgroups.util.Promise; import org.jgroups.util.Util; import org.testng.Assert; import org.testng.annotations.AfterMethod; import org.testng.annotations.Test; import java.util.*; /** * Measure the latency between messages with message bundling enabled at the transport level * @author Bela Ban */ @Test(groups=Global.STACK_DEPENDENT,sequential=true) public class MessageBundlingTest extends ChannelTestBase { private JChannel ch1, ch2; private MyReceiver r2; private final static long LATENCY=1500L; private final static long SLEEP=5000L; private static final boolean BUNDLING=true; private static final int MAX_BYTES=64000; @AfterMethod void tearDown() throws Exception { closeChannel(ch2); closeChannel(ch1); } protected boolean useBlocking() { return false; } public void testLatencyWithoutMessageBundling() throws Exception { createChannels("testLatencyWithoutMessageBundling"); Message tmp=new Message(); setBundling(ch1, false, MAX_BYTES, 30); r2.setNumExpectedMesssages(1); Promise promise=new Promise(); r2.setPromise(promise); long time=System.currentTimeMillis(); ch1.send(tmp); System.out.println(">>> sent message at " + new Date()); promise.getResult(SLEEP); List list=r2.getTimes(); Assert.assertEquals(1, list.size()); Long time2=list.get(0); long diff=time2 - time; System.out.println("latency: " + diff + " ms"); assertTrue("latency (" + diff + "ms) should be less than " + LATENCY + " ms", diff <= LATENCY); } public void testLatencyWithMessageBundling() throws Exception { createChannels("testLatencyWithMessageBundling"); Message tmp=new Message(); r2.setNumExpectedMesssages(1); Promise promise=new Promise(); r2.setPromise(promise); long time=System.currentTimeMillis(); ch1.send(tmp); System.out.println(">>> sent message at " + new Date()); promise.getResult(SLEEP); List list=r2.getTimes(); Assert.assertEquals(1, list.size()); Long time2=list.get(0); long diff=time2 - time; System.out.println("latency: " + diff + " ms"); assertTrue("latency (" + diff + "ms) should be less than 2 times the LATENCY (" + LATENCY *2 + ")", diff <= LATENCY * 2); } public void testLatencyWithMessageBundlingAndLoopback() throws Exception { createChannels("testLatencyWithMessageBundlingAndLoopback"); Message tmp=new Message(); setLoopback(ch1, true); setLoopback(ch2, true); r2.setNumExpectedMesssages(1); Promise promise=new Promise(); r2.setPromise(promise); long time=System.currentTimeMillis(); System.out.println(">>> sending message at " + new Date()); ch1.send(tmp); promise.getResult(SLEEP); List list=r2.getTimes(); Assert.assertEquals(1, list.size()); Long time2=list.get(0); long diff=time2 - time; System.out.println("latency: " + diff + " ms"); assertTrue("latency (" + diff + "ms) should be less than 2 times the LATENCY (" + LATENCY *2 + ")", diff <= LATENCY * 2); } public void testLatencyWithMessageBundlingAndMaxBytes() throws Exception { createChannels("testLatencyWithMessageBundlingAndMaxBytes"); setLoopback(ch1, true); setLoopback(ch2, true); r2.setNumExpectedMesssages(10); Promise promise=new Promise(); r2.setPromise(promise); Util.sleep(LATENCY *2); System.out.println(">>> sending 10 messages at " + new Date()); for(int i=0; i < 10; i++) ch1.send(new Message(null, null, new byte[2000])); promise.getResult(SLEEP); // we should get the messages immediately because max_bundle_size has been exceeded by the 20 messages List list=r2.getTimes(); Assert.assertEquals(10, list.size()); for(Iterator it=list.iterator(); it.hasNext();) { Long val=it.next(); System.out.println(val); } } public void testSimple() throws Exception { createChannels("testSimple"); Message tmp=new Message(); ch2.setReceiver(new SimpleReceiver()); ch1.send(tmp); System.out.println(">>> sent message at " + new Date()); Util.sleep(5000); } private void createChannels(String cluster) throws Exception { ch1=createChannel(true, 2); ch1.setName("A"); setBundling(ch1, BUNDLING, MAX_BYTES, LATENCY); setLoopback(ch1, false); ch1.setReceiver(new NullReceiver()); ch1.connect("MessageBundlingTest-" + cluster); ch2=createChannel(ch1); ch2.setName("B"); // setBundling(ch2, BUNDLING, MAX_BYTES, LATENCY); // setLoopback(ch2, false); r2=new MyReceiver(); ch2.setReceiver(r2); ch2.connect("MessageBundlingTest-" + cluster); View view=ch2.getView(); assert view.size() == 2 : " view=" + view; } private static void setLoopback(JChannel ch, boolean b) { ProtocolStack stack=ch.getProtocolStack(); List prots=stack.getProtocols(); TP transport=(TP)prots.get(prots.size() -1); transport.setLoopback(b); } private static void setBundling(JChannel ch, boolean enabled, int max_bytes, long timeout) { ProtocolStack stack=ch.getProtocolStack(); List prots=stack.getProtocols(); TP transport=(TP)prots.get(prots.size() -1); transport.setEnableBundling(enabled); if(enabled) { transport.setMaxBundleSize(max_bytes); transport.setMaxBundleTimeout(timeout); } transport.setEnableUnicastBundling(false); if(enabled) { GMS gms=(GMS)stack.findProtocol("GMS"); gms.setViewAckCollectionTimeout(LATENCY * 2); gms.setJoinTimeout(LATENCY * 2); } } private static void closeChannel(Channel c) { if(c != null && (c.isOpen() || c.isConnected())) { c.close(); } } private static class NullReceiver extends ReceiverAdapter { } private static class SimpleReceiver extends ReceiverAdapter { long start=System.currentTimeMillis(); public void receive(Message msg) { System.out.println("<<< received message from " + msg.getSrc() + " at " + new Date() + ", latency=" + (System.currentTimeMillis() - start) + " ms"); } } private static class MyReceiver extends ReceiverAdapter { private final List times=new LinkedList(); private int num_expected_msgs; private Promise promise; public List getTimes() { return times; } public void setNumExpectedMesssages(int num_expected_msgs) { this.num_expected_msgs=num_expected_msgs; } public void setPromise(Promise promise) { this.promise=promise; } public int size() { return times.size(); } public void receive(Message msg) { times.add(new Long(System.currentTimeMillis())); System.out.println("<<< received message from " + msg.getSrc() + " at " + new Date()); if(times.size() >= num_expected_msgs && promise != null) { promise.setResult(times.size()); } } } } libjgroups-java-2.12.2.Final.orig/tests/junit/org/jgroups/tests/MessageDispatcherUnitTest.java0000644000175000017500000001373611647260573032444 0ustar moellermoellerpackage org.jgroups.tests; import org.jgroups.Global; import org.jgroups.JChannel; import org.jgroups.Message; import org.jgroups.blocks.*; import org.jgroups.protocols.TP; import org.jgroups.protocols.pbcast.GMS; import org.jgroups.stack.ProtocolStack; import org.jgroups.util.Rsp; import org.jgroups.util.RspList; import org.jgroups.util.Util; import org.testng.Assert; import org.testng.annotations.AfterClass; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; /** * Tests return values from MessageDispatcher.castMessage() * * @author Bela Ban * belaban Exp $ */ @Test(groups=Global.STACK_DEPENDENT, sequential=true) public class MessageDispatcherUnitTest extends ChannelTestBase { MessageDispatcher d1, d2; JChannel c1, c2; @BeforeClass protected void setUp() throws Exception { c1=createChannel(true); c1.setName("A"); GMS gms=(GMS)c1.getProtocolStack().findProtocol(GMS.class); if(gms != null) gms.setPrintLocalAddress(false); disableBundling(c1); d1=new MessageDispatcher(c1, null, null, null); c1.connect("MessageDispatcherUnitTest"); } @AfterClass protected void tearDown() throws Exception { d1.stop(); c1.close(); Util.sleep(500); } @AfterMethod protected void closeSecondChannel() { if(c2 != null) { d2.stop(); c2.close(); Util.sleep(500); } } public void testNullMessageToSelf() { MyHandler handler=new MyHandler(null); d1.setRequestHandler(handler); RspList rsps=d1.castMessage(null, new Message(), new RequestOptions(Request.GET_ALL, 0)); System.out.println("rsps:\n" + rsps); assertNotNull(rsps); Assert.assertEquals(1, rsps.size()); Object obj=rsps.getFirst(); assert obj == null; } public void test200ByteMessageToSelf() { sendMessage(200); } public void test2000ByteMessageToSelf() { sendMessage(2000); } public void test20000ByteMessageToSelf() { sendMessage(20000); } public void testNullMessageToAll() throws Exception { d1.setRequestHandler(new MyHandler(null)); c2=createChannel(c1); c2.setName("B"); disableBundling(c2); long stop, start=System.currentTimeMillis(); d2=new MessageDispatcher(c2, null, null, new MyHandler(null)); stop=System.currentTimeMillis(); c2.connect("MessageDispatcherUnitTest"); Assert.assertEquals(2, c2.getView().size()); System.out.println("view: " + c2.getView()); System.out.println("casting message"); start=System.currentTimeMillis(); RspList rsps=d1.castMessage(null, new Message(), new RequestOptions(Request.GET_ALL, 0)); stop=System.currentTimeMillis(); System.out.println("rsps:\n" + rsps); System.out.println("call took " + (stop - start) + " ms"); assertNotNull(rsps); Assert.assertEquals(2, rsps.size()); Rsp rsp=rsps.get(c1.getAddress()); assertNotNull(rsp); Object ret=rsp.getValue(); assert ret == null; rsp=rsps.get(c2.getAddress()); assertNotNull(rsp); ret=rsp.getValue(); assert ret == null; Util.close(c2); } public void test200ByteMessageToAll() throws Exception { sendMessageToBothChannels(200); } public void test2000ByteMessageToAll() throws Exception { sendMessageToBothChannels(2000); } public void test20000ByteMessageToAll() throws Exception { sendMessageToBothChannels(20000); } private void sendMessage(int size) { long start, stop; MyHandler handler=new MyHandler(new byte[size]); d1.setRequestHandler(handler); start=System.currentTimeMillis(); RspList rsps=d1.castMessage(null, new Message(), new RequestOptions(Request.GET_ALL, 0)); stop=System.currentTimeMillis(); System.out.println("rsps:\n" + rsps); System.out.println("call took " + (stop - start) + " ms"); assertNotNull(rsps); Assert.assertEquals(1, rsps.size()); byte[] buf=(byte[])rsps.getFirst(); assertNotNull(buf); Assert.assertEquals(size, buf.length); } private void sendMessageToBothChannels(int size) throws Exception { long start, stop; d1.setRequestHandler(new MyHandler(new byte[size])); c2=createChannel(c1); c2.setName("B"); disableBundling(c2); d2=new MessageDispatcher(c2, null, null, new MyHandler(new byte[size])); c2.connect("MessageDispatcherUnitTest"); Assert.assertEquals(2, c2.getView().size()); System.out.println("casting message"); start=System.currentTimeMillis(); RspList rsps=d1.castMessage(null, new Message(), new RequestOptions(Request.GET_ALL, 0)); stop=System.currentTimeMillis(); System.out.println("rsps:\n" + rsps); System.out.println("call took " + (stop - start) + " ms"); assertNotNull(rsps); Assert.assertEquals(2, rsps.size()); Rsp rsp=rsps.get(c1.getAddress()); assertNotNull(rsp); byte[] ret=(byte[])rsp.getValue(); Assert.assertEquals(size, ret.length); rsp=rsps.get(c2.getAddress()); assertNotNull(rsp); ret=(byte[])rsp.getValue(); Assert.assertEquals(size, ret.length); Util.close(c2); } private static void disableBundling(JChannel ch) { ProtocolStack stack=ch.getProtocolStack(); TP transport=stack.getTransport(); if(transport != null) { transport.setEnableBundling(false); } } private static class MyHandler implements RequestHandler { byte[] retval=null; public MyHandler(byte[] retval) { this.retval=retval; } public Object handle(Message msg) { return retval; } } } libjgroups-java-2.12.2.Final.orig/tests/junit/org/jgroups/tests/NAKACK_Test.java0000644000175000017500000001033011647260573027263 0ustar moellermoellerpackage org.jgroups.tests; import org.jgroups.Global; 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 org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.Collection; import java.util.concurrent.ConcurrentLinkedQueue; /** * Tests the NAKACK protocol for OOB and regular msgs, tests http://jira.jboss.com/jira/browse/JGRP-379 * @author Bela Ban */ @Test(groups=Global.STACK_DEPENDENT,sequential=true) public class NAKACK_Test extends ChannelTestBase { JChannel c1, c2, c3; @BeforeMethod public void setUp() throws Exception { c1=createChannel(true, 3); c2=createChannel(c1); c3=createChannel(c1); } @AfterMethod public void tearDown() throws Exception { Util.close(c3, c2, c1); } /** * 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. * Note that OOB messages *destroys* FIFO ordering (or whatever ordering properties are set) ! * @throws Exception */ @Test @SuppressWarnings("unchecked") public void testOutOfBandMessages() throws Exception { NAKACK_Test.MyReceiver receiver1=new NAKACK_Test.MyReceiver(); NAKACK_Test.MyReceiver receiver2=new NAKACK_Test.MyReceiver(); NAKACK_Test.MyReceiver receiver3=new NAKACK_Test.MyReceiver(); c1.setReceiver(receiver1); c2.setReceiver(receiver2); c3.setReceiver(receiver3); c1.getProtocolStack().insertProtocol(new DISCARD_PAYLOAD(), ProtocolStack.BELOW, "NAKACK"); c1.connect("NAKACK_OOB_Test"); c2.connect("NAKACK_OOB_Test"); c3.connect("NAKACK_OOB_Test"); assert c3.getView().getMembers().size() == 3 : "view is " + c3.getView() + ", expected view of 3 members"; 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); c1.send(msg); Util.sleep(100); } Collection seqnos1=receiver1.getSeqnos(); Collection seqnos2=receiver2.getSeqnos(); Collection seqnos3=receiver3.getSeqnos(); // wait until retransmission of seqno #3 happens, so that 4 and 5 are received as well long target_time=System.currentTimeMillis() + 20000; do { if(seqnos1.size() >= 5 && seqnos2.size() >= 5 && seqnos3.size() >= 5) break; Util.sleep(500); } while(target_time > System.currentTimeMillis()); System.out.println("sequence numbers:"); System.out.println("c1: " + seqnos1); System.out.println("c2: " + seqnos2); System.out.println("c3: " + seqnos3); checkOrder(seqnos1, seqnos2, seqnos3); } /** * Checks whether the numbers are in order *after* removing 4: the latter is OOB and can therefore appear anywhere * in the sequence * @param lists */ private static void checkOrder(Collection ... lists) throws Exception { for(Collection list: lists) { list.remove(4L); long prev_val=0; for(long val: list) { if(val <= prev_val) throw new Exception("elements are not ordered in list: " + list); prev_val=val; } } } public static class MyReceiver extends ReceiverAdapter { /** List of unicast sequence numbers */ Collection seqnos=new ConcurrentLinkedQueue(); public MyReceiver() { } public Collection getSeqnos() { return seqnos; } public void receive(Message msg) { if(msg != null) { Long num=(Long)msg.getObject(); seqnos.add(num); } } public int size() {return seqnos.size();} } } libjgroups-java-2.12.2.Final.orig/tests/junit/org/jgroups/tests/OOBTest.java0000644000175000017500000004036311647260573026624 0ustar moellermoellerpackage org.jgroups.tests; import org.jgroups.*; import org.jgroups.protocols.DISCARD; import org.jgroups.protocols.TP; import org.jgroups.protocols.UNICAST; import org.jgroups.protocols.UNICAST2; import org.jgroups.protocols.pbcast.NAKACK; import org.jgroups.protocols.pbcast.STABLE; import org.jgroups.stack.ProtocolStack; import org.jgroups.util.Util; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.*; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.ConcurrentLinkedQueue; /** * Tests whether OOB multicast/unicast messages are blocked by regular messages (which block) - should NOT be the case. * The class name is a misnomer, both multicast *and* unicast messages are tested * @author Bela Ban */ @Test(groups=Global.STACK_DEPENDENT,sequential=true) public class OOBTest extends ChannelTestBase { private JChannel c1, c2; @BeforeMethod public void init() throws Exception { c1=createChannel(true, 2); c1.setName("C1"); c2=createChannel(c1); c2.setName("C2"); setOOBPoolSize(c1, c2); setStableGossip(c1, c2); c1.connect("OOBTest"); c2.connect("OOBTest"); View view=c2.getView(); log.info("view = " + view); assert view.size() == 2 : "view is " + view; } @AfterMethod public void cleanup() { Util.sleep(1000); Util.close(c2, c1); } /** * A and B. A multicasts a regular message, which blocks in B. Then A multicasts an OOB message, which must be * received by B. */ public void testNonBlockingUnicastOOBMessage() throws ChannelNotConnectedException, ChannelClosedException { Address dest=c2.getAddress(); send(dest); } public void testNonBlockingMulticastOOBMessage() throws ChannelNotConnectedException, ChannelClosedException { send(null); } /** * Tests sending 1, 2 (OOB) and 3, where they are received in the order 1, 3, 2. Message 3 should not get delivered * until message 4 is received (http://jira.jboss.com/jira/browse/JGRP-780) */ public void testRegularAndOOBUnicasts() throws Exception { DISCARD discard=new DISCARD(); ProtocolStack stack=c1.getProtocolStack(); stack.insertProtocol(discard, ProtocolStack.BELOW, UNICAST.class, UNICAST2.class); Address dest=c2.getAddress(); Message m1=new Message(dest, null, 1); Message m2=new Message(dest, null, 2); m2.setFlag(Message.OOB); Message m3=new Message(dest, null, 3); MyReceiver receiver=new MyReceiver("C2"); c2.setReceiver(receiver); c1.send(m1); discard.setDropDownUnicasts(1); c1.send(m2); c1.send(m3); sendStableMessages(c1,c2); Util.sleep(1000); // time for potential retransmission Collection list=receiver.getMsgs(); assert list.size() == 3 : "list is " + list; assert list.contains(1) && list.contains(2) && list.contains(3); } public void testRegularAndOOBUnicasts2() throws Exception { DISCARD discard=new DISCARD(); ProtocolStack stack=c1.getProtocolStack(); stack.insertProtocol(discard, ProtocolStack.BELOW, UNICAST.class, UNICAST2.class); Address dest=c2.getAddress(); Message m1=new Message(dest, null, 1); Message m2=new Message(dest, null, 2); m2.setFlag(Message.OOB); Message m3=new Message(dest, null, 3); m3.setFlag(Message.OOB); Message m4=new Message(dest, null, 4); MyReceiver receiver=new MyReceiver("C2"); c2.setReceiver(receiver); c1.send(m1); discard.setDropDownUnicasts(1); c1.send(m3); discard.setDropDownUnicasts(1); c1.send(m2); c1.send(m4); Util.sleep(1000); // sleep some time to receive all messages Collection list=receiver.getMsgs(); int count=10; while(list.size() < 4 && --count > 0) { Util.sleep(500); // time for potential retransmission sendStableMessages(c1,c2); } log.info("list = " + list); assert list.size() == 4 : "list is " + list; assert list.contains(1) && list.contains(2) && list.contains(3) && list.contains(4); } public void testRegularAndOOBMulticasts() throws Exception { DISCARD discard=new DISCARD(); ProtocolStack stack=c1.getProtocolStack(); stack.insertProtocol(discard, ProtocolStack.BELOW, NAKACK.class); c1.setOpt(Channel.LOCAL, false); Address dest=null; // send to all Message m1=new Message(dest, null, 1); Message m2=new Message(dest, null, 2); m2.setFlag(Message.OOB); Message m3=new Message(dest, null, 3); MyReceiver receiver=new MyReceiver("C2"); c2.setReceiver(receiver); c1.send(m1); discard.setDropDownMulticasts(1); c1.send(m2); c1.send(m3); Util.sleep(500); Collection list=receiver.getMsgs(); for(int i=0; i < 10; i++) { log.info("list = " + list); if(list.size() == 3) break; Util.sleep(1000); // give the asynchronous msgs some time to be received sendStableMessages(c1,c2); } assert list.size() == 3 : "list is " + list; assert list.contains(1) && list.contains(2) && list.contains(3); } @Test(invocationCount=5) @SuppressWarnings("unchecked") public void testRandomRegularAndOOBMulticasts() throws Exception { DISCARD discard=new DISCARD(); discard.setLocalAddress(c1.getAddress()); discard.setDownDiscardRate(0.5); ProtocolStack stack=c1.getProtocolStack(); stack.insertProtocol(discard, ProtocolStack.BELOW, NAKACK.class); MyReceiver r1=new MyReceiver("C1"), r2=new MyReceiver("C2"); c1.setReceiver(r1); c2.setReceiver(r2); final int NUM_MSGS=20; final int NUM_THREADS=10; send(null, NUM_MSGS, NUM_THREADS, 0.5); // send on random channel (c1 or c2) Collection one=r1.getMsgs(), two=r2.getMsgs(); for(int i=0; i < 10; i++) { if(one.size() == NUM_MSGS && two.size() == NUM_MSGS) break; log.info("one size " + one.size() + ", two size " + two.size()); Util.sleep(1000); sendStableMessages(c1,c2); } log.info("one size " + one.size() + ", two size " + two.size()); stack.removeProtocol("DISCARD"); for(int i=0; i < 5; i++) { if(one.size() == NUM_MSGS && two.size() == NUM_MSGS) break; sendStableMessages(c1,c2); Util.sleep(500); } System.out.println("C1 received " + one.size() + " messages ("+ NUM_MSGS + " expected)" + "\nC2 received " + two.size() + " messages ("+ NUM_MSGS + " expected)"); check(NUM_MSGS, one, two); } /** * Tests https://jira.jboss.org/jira/browse/JGRP-1079 * @throws ChannelNotConnectedException * @throws ChannelClosedException */ public void testOOBMessageLoss() throws ChannelNotConnectedException, ChannelClosedException { Util.close(c2); // we only need 1 channel MyReceiver receiver=new MySleepingReceiver("C1", 1000); c1.setReceiver(receiver); TP transport=c1.getProtocolStack().getTransport(); transport.setOOBRejectionPolicy("discard"); final int NUM=10; for(int i=1; i <= NUM; i++) { Message msg=new Message(null, null, i); msg.setFlag(Message.OOB); c1.send(msg); } STABLE stable=(STABLE)c1.getProtocolStack().findProtocol(STABLE.class); if(stable != null) stable.runMessageGarbageCollection(); Collection msgs=receiver.getMsgs(); for(int i=0; i < 20; i++) { if(msgs.size() == NUM) break; Util.sleep(1000); sendStableMessages(c1,c2); } System.out.println("msgs = " + Util.print(msgs)); assert msgs.size() == NUM : "expected " + NUM + " messages but got " + msgs.size() + ", msgs=" + Util.print(msgs); for(int i=1; i <= NUM; i++) { assert msgs.contains(i); } } /** * Tests https://jira.jboss.org/jira/browse/JGRP-1079 for unicast messages * @throws ChannelNotConnectedException * @throws ChannelClosedException */ public void testOOBUnicastMessageLoss() throws ChannelNotConnectedException, ChannelClosedException { MyReceiver receiver=new MySleepingReceiver("C2", 1000); c2.setReceiver(receiver); c1.getProtocolStack().getTransport().setOOBRejectionPolicy("discard"); final int NUM=10; final Address dest=c2.getAddress(); for(int i=1; i <= NUM; i++) { Message msg=new Message(dest, null, i); msg.setFlag(Message.OOB); c1.send(msg); } Collection msgs=receiver.getMsgs(); for(int i=0; i < 20; i++) { if(msgs.size() == NUM) break; Util.sleep(1000); // sendStableMessages(c1,c2); // not needed for unicasts ! } assert msgs.size() == NUM : "expected " + NUM + " messages but got " + msgs.size() + ", msgs=" + Util.print(msgs); for(int i=1; i <= NUM; i++) { assert msgs.contains(i); } } private void send(final Address dest, final int num_msgs, final int num_threads, final double oob_prob) throws Exception { if(num_threads <= 0) throw new IllegalArgumentException("number of threads <= 0"); if(num_msgs % num_threads != 0) throw new IllegalArgumentException("number of messages ( " + num_msgs + ") needs to be divisible by " + "the number o threads (" + num_threads + ")"); if(num_threads > 1) { final int msgs_per_thread=num_msgs / num_threads; Thread[] threads=new Thread[num_threads]; final AtomicInteger counter=new AtomicInteger(0); for(int i=0; i < threads.length; i++) { threads[i]=new Thread() { public void run() { for(int j=0; j < msgs_per_thread; j++) { Channel sender=Util.tossWeightedCoin(0.5) ? c1 : c2; boolean oob=Util.tossWeightedCoin(oob_prob); int num=counter.incrementAndGet(); Message msg=new Message(dest, null, num); if(oob) msg.setFlag(Message.OOB); try { sender.send(msg); } catch(Exception e) { e.printStackTrace(); } } } }; threads[i].start(); } for(int i=0; i < threads.length; i++) { threads[i].join(20000); } return; } for(int i=0; i < num_msgs; i++) { Channel sender=Util.tossWeightedCoin(0.5) ? c1 : c2; boolean oob=Util.tossWeightedCoin(oob_prob); Message msg=new Message(dest, null, i); if(oob) msg.setFlag(Message.OOB); sender.send(msg); } } private void send(Address dest) throws ChannelNotConnectedException, ChannelClosedException { final ReentrantLock lock=new ReentrantLock(); final BlockingReceiver receiver=new BlockingReceiver(lock); final int NUM=10; c2.setReceiver(receiver); System.out.println("[" + Thread.currentThread().getName() + "]: locking lock"); lock.lock(); c1.send(new Message(dest, null, 1)); for(int i=2; i <= NUM; i++) { Message msg=new Message(dest, null, i); msg.setFlag(Message.OOB); c1.send(msg); } sendStableMessages(c1, c2); Util.sleep(500); List list=receiver.getMsgs(); for(int i=0; i < 20; i++) { if(list.size() == NUM-1) break; System.out.println("list = " + list); Util.sleep(1000); // give the asynchronous msgs some time to be received } System.out.println("list = " + list); assert list.size() == NUM-1 : "list is " + list; assert list.contains(2) && list.contains(10); System.out.println("[" + Thread.currentThread().getName() + "]: unlocking lock"); lock.unlock(); for(int i=0; i < 20; i++) { if(list.size() == NUM) break; System.out.println("list = " + list); Util.sleep(1000); // give the asynchronous msgs some time to be received } System.out.println("list = " + list); assert list.size() == NUM : "list is " + list; for(int i=1; i <= NUM; i++) assert list.contains(i); } @SuppressWarnings("unchecked") private void check(final int num_expected_msgs, Collection... lists) { for(Collection list: lists) { log.info("list: " + list); } for(Collection list: lists) { Collection missing=new TreeSet(); if(list.size() != num_expected_msgs) { for(int i=0; i < num_expected_msgs; i++) missing.add(i); missing.removeAll(list); assert list.size() == num_expected_msgs : "expected " + num_expected_msgs + " elements, but got " + list.size() + " (list=" + list + "), missing=" + missing; } } } private static void setOOBPoolSize(JChannel... channels) { for(Channel channel: channels) { TP transport=channel.getProtocolStack().getTransport(); transport.setOOBThreadPoolMinThreads(1); transport.setOOBThreadPoolMaxThreads(2); } } private static void setStableGossip(JChannel... channels) { for(Channel channel: channels) { ProtocolStack stack=channel.getProtocolStack(); STABLE stable=(STABLE)stack.findProtocol(STABLE.class); stable.setDesiredAverageGossip(2000); } } private static void sendStableMessages(JChannel ... channels) { for(JChannel ch: channels) { STABLE stable=(STABLE)ch.getProtocolStack().findProtocol(STABLE.class); if(stable != null) stable.runMessageGarbageCollection(); } } private static class BlockingReceiver extends ReceiverAdapter { final Lock lock; final List msgs=Collections.synchronizedList(new LinkedList()); public BlockingReceiver(Lock lock) { this.lock=lock; } public List getMsgs() { return msgs; } public void receive(Message msg) { if(!msg.isFlagSet(Message.OOB)) { lock.lock(); lock.unlock(); } msgs.add((Integer)msg.getObject()); } } private static class MyReceiver extends ReceiverAdapter { private final Collection msgs=new ConcurrentLinkedQueue(); final String name; public MyReceiver(String name) { this.name=name; } public Collection getMsgs() { return msgs; } public void receive(Message msg) { Integer val=(Integer)msg.getObject(); msgs.add(val); } } private static class MySleepingReceiver extends MyReceiver { final long sleep_time; public MySleepingReceiver(String name, long sleep_time) { super(name); this.sleep_time=sleep_time; } public void receive(Message msg) { super.receive(msg); System.out.println("-- received " + msg.getObject()); Util.sleep(sleep_time); } } } libjgroups-java-2.12.2.Final.orig/tests/junit/org/jgroups/tests/OverlappingMergeTest.java0000644000175000017500000003524111647260573031452 0ustar moellermoellerpackage org.jgroups.tests; import org.jgroups.*; import org.jgroups.protocols.pbcast.GMS; import org.jgroups.protocols.pbcast.NAKACK; import org.jgroups.protocols.pbcast.STABLE; import org.jgroups.stack.ProtocolStack; import org.jgroups.util.Digest; import org.jgroups.util.Tuple; import org.jgroups.util.Util; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.*; /** * Tests overlapping merges, e.g. A: {A,B}, B: {A,B} and C: {A,B,C}. Tests unicast as well as multicast seqno tables.
                    * Related JIRA: https://jira.jboss.org/jira/browse/JGRP-940 * @author Bela Ban */ @Test(groups=Global.STACK_DEPENDENT,sequential=true) public class OverlappingMergeTest extends ChannelTestBase { protected JChannel a, b, c; protected MyReceiver ra, rb, rc; protected boolean multicast_transport; @BeforeMethod protected void start() throws Exception { a=createChannel(true, 3); a.setName("A"); ra=new MyReceiver("A", a); a.setReceiver(ra); b=createChannel(a); b.setName("B"); rb=new MyReceiver("B", b); b.setReceiver(rb); c=createChannel(a); c.setName("C"); rc=new MyReceiver("C", c); c.setReceiver(rc); modifyConfigs(a, b, c); a.connect("OverlappingMergeTest"); b.connect("OverlappingMergeTest"); c.connect("OverlappingMergeTest"); View view=c.getView(); assert view.size() == 3 : "view is " + view; multicast_transport=isMulticastTransport(a); } @AfterMethod protected void stop() throws Exception { Util.close(c,b,a); ra.clear(); rb.clear(); rc.clear(); } public void testRegularMessageSending() throws Exception { sendMessages(5, a, b, c); checkReceivedMessages(make(ra, 15), make(rb,15), make(rc,15)); } /** * Verifies that unicasts are received correctly by all participants after an overlapping merge. The following steps * are executed: *
                      *
                    1. Group is {A,B,C}, A is the coordinator *
                    2. MERGE2 is removed from all members *
                    3. VERIFY_SUSPECT is removed from all members *
                    4. Everyone sends 5 unicast messages to everyone else *
                    5. Everyone sends 5 multicasts *
                    6. A SUSPECT(A) event is injected into B's stack (GMS). This causes a new view {B,C} to be multicast by B *
                    7. B and C install {B,C} *
                    8. B and C trash the connection table for A in UNICAST *
                    9. A ignores the view, it still has view {A,B,C} and all connection tables intact in UNICAST *
                    10. We now inject a MERGE(A,B) event into A. This should ause A and B as coords to create a new MergeView {A,B,C} *
                    11. The merge already fails because the unicast between A and B fails due to the reason given below ! * Once this is fixed, the next step below should work, too ! *
                    12. A sends a unicast to B and C. This should fail until JGRP-940 has been fixed ! *
                    13. Reason: B and C trashed A's conntables in UNICAST, but A didn't trash its conn tables for B and C, so * we have non-matching seqnos ! *
                    */ public void testOverlappingMergeWithBC() throws Exception { sendMessages(5, a, b, c); checkReceivedMessages(make(ra, 15), make(rb,15), make(rc,15)); // Inject view {B,C} into B and C: View new_view=Util.createView(b.getAddress(), 10, b.getAddress(), c.getAddress()); System.out.println("\n ==== Injecting view " + new_view + " into B and C ===="); injectView(new_view, b, c); makeCoordinator(b); assert Util.isCoordinator(a); assert Util.isCoordinator(b); assert !Util.isCoordinator(c); System.out.println("A's view: " + a.getView()); System.out.println("B's view: " + b.getView()); System.out.println("C's view: " + c.getView()); assert a.getView().size() == 3 : "A's view is " + a.getView(); assert b.getView().size() == 2 : "B's view is " + b.getView(); assert c.getView().size() == 2 : "C's view is " + c.getView(); System.out.println("\n==== Sending messages while the cluster is partitioned ===="); sendMessages(5, a, b, c); if(multicast_transport) { // B and C drop A's multicasts, but A will receive B's and C's multicasts checkReceivedMessages(make(ra, 15), make(rb,10), make(rc,10)); } else { // B and C drop A's multicasts, and won't send their multicasts to A (A only receives its owm multicasts) checkReceivedMessages(make(ra, 5), make(rb,10), make(rc,10)); } System.out.println("\n ==== Digests are:\n" + dumpDigests(a,b,c)); // start merging Map views=new HashMap(); views.put(a.getAddress(), a.getView()); views.put(b.getAddress(), b.getView()); views.put(c.getAddress(), c.getView()); Event merge_evt=new Event(Event.MERGE, views); JChannel merge_leader=determineMergeLeader(a, b); System.out.println("\n==== Injecting a merge event (leader=" + merge_leader.getAddress() + ") ===="); injectMergeEvent(merge_evt, merge_leader); System.out.println("\n==== checking views after merge ====:"); for(int i=0; i < 10; i++) { if(a.getView().size() == 3 && b.getView().size() == 3 && c.getView().size() == 3) { System.out.println("views are correct: all views have a size of 3"); break; } System.out.print("."); runStableProtocol(a); runStableProtocol(b); runStableProtocol(c); Util.sleep(1000); } System.out.println("\n ==== Digests after the merge:\n" + dumpDigests(a,b,c)); View va=a.getView(), vb=b.getView(), vc=c.getView(); System.out.println("\nA's view: " + va); System.out.println("B's view: " + vb); System.out.println("C's view: " + vc); assert va.size() == 3 : "A's view is " + va; assert vb.size() == 3 : "B's view is " + vb; assert vc.size() == 3 : "C's view is " + vc; System.out.println("\n==== Sending messages after merge ===="); sendMessages(5, a, b, c); checkReceivedMessages(make(ra, 15), make(rb,15), make(rc,15)); } /** * Verifies that unicasts are received correctly by all participants after an overlapping merge. The following steps * are executed: *
                      *
                    1. Group is {A,B,C} *
                    2. Install view {A,C} in A and {A,B,C} in B and C *
                    3. Try to initiate a merge. This should FAIL until https://jira.jboss.org/jira/browse/JGRP-937 has * been implemented: B and C's MERGE2 protocols will never send out merge requests as they see A as coord *
                    */ @Test(enabled=true) public void testOverlappingMergeWithABC() throws Exception { sendMessages(5, a, b, c); checkReceivedMessages(make(ra, 15), make(rb,15), make(rc,15)); // Inject view {A,C} into A: View new_view=Util.createView(a.getAddress(), 4, a.getAddress(), c.getAddress()); System.out.println("\n ==== Injecting view " + new_view + " into A ===="); injectView(new_view, a); assertTrue(Util.isCoordinator(a)); assertFalse(Util.isCoordinator(b)); assertFalse(Util.isCoordinator(c)); System.out.println("A's view: " + a.getView()); System.out.println("B's view: " + b.getView()); System.out.println("C's view: " + c.getView()); assertEquals("A's view is " + a.getView(), 2, a.getView().size()); assertEquals("B's view is " + b.getView(), 3, b.getView().size()); assertEquals("C's view is " + c.getView(), 3, c.getView().size()); // start merging Map views=new HashMap(); views.put(a.getAddress(), a.getView()); views.put(b.getAddress(), b.getView()); views.put(c.getAddress(), c.getView()); Event merge_evt=new Event(Event.MERGE, views); System.out.println("\n==== Injecting a merge event (leader=" + a.getAddress() + ") ===="); injectMergeEvent(merge_evt, a); System.out.println("\n==== checking views after merge ====:"); for(int i=0; i < 10; i++) { if(a.getView().size() == 3 && b.getView().size() == 3 && c.getView().size() == 3) { System.out.println("views are correct: all views have a size of 3"); break; } System.out.print("."); for(JChannel ch: new JChannel[]{a,b,c}) runStableProtocol(ch); Util.sleep(1000); } System.out.println("\n ==== Digests after the merge:\n" + dumpDigests(a,b,c)); View va=a.getView(), vb=b.getView(), vc=c.getView(); System.out.println("\nA's view: " + va); System.out.println("B's view: " + vb); System.out.println("C's view: " + vc); assertEquals("A's view is " + va, 3, va.size()); assertEquals("B's view is " + vb, 3, vb.size()); assertEquals("C's view is " + vc, 3, vc.size()); System.out.println("\n==== Sending messages after merge ===="); sendMessages(5, a, b, c); checkReceivedMessages(make(ra, 15), make(rb,15), make(rc,15)); } private static void makeCoordinator(JChannel ch) { GMS gms=(GMS)ch.getProtocolStack().findProtocol(GMS.class); gms.becomeCoordinator(); } private static String dumpDigests(JChannel ... channels) { StringBuilder sb=new StringBuilder(); for(JChannel ch: channels) { sb.append(ch.getAddress()).append(": "); NAKACK nakack=(NAKACK)ch.getProtocolStack().findProtocol(NAKACK.class); Digest digest=nakack.getDigest(); sb.append(digest).append("\n"); } return sb.toString(); } private static JChannel determineMergeLeader(JChannel ... coords) { Membership tmp=new Membership(); for(JChannel ch: coords) { tmp.add(ch.getAddress()); } tmp.sort(); Address merge_leader=tmp.elementAt(0); for(JChannel ch: coords) { if(ch.getAddress().equals(merge_leader)) return ch; } return null; } private static void injectView(View view, JChannel ... channels) { for(JChannel ch: channels) { GMS gms=(GMS)ch.getProtocolStack().findProtocol(GMS.class); gms.installView(view); } for(JChannel ch: channels) { MyReceiver receiver=(MyReceiver)ch.getReceiver(); System.out.println("[" + receiver.name + "] view=" + ch.getView()); } } private static void injectMergeEvent(Event evt, JChannel ... channels) { for(JChannel ch: channels) { GMS gms=(GMS)ch.getProtocolStack().findProtocol(GMS.class); gms.up(evt); } } private void sendMessages(int num_msgs, JChannel... channels) throws Exception { ra.clear(); rb.clear(); rc.clear(); for(JChannel ch: channels) { for(int i=1; i <= num_msgs; i++) ch.send(null, null, "#" + i); } } private static void runStableProtocol(JChannel ch) { STABLE stable=(STABLE)ch.getProtocolStack().findProtocol(STABLE.class); if(stable != null) stable.runMessageGarbageCollection(); } protected boolean isMulticastTransport(JChannel ch) { return ch.getProtocolStack().getTransport().supportsMulticasting(); } protected void checkReceivedMessages(Tuple ... expected_messages) { for(int i=0; i < 30; i++) { boolean all_received=true; for(Tuple tuple: expected_messages) { MyReceiver receiver=tuple.getVal1(); List mcasts=receiver.getMulticasts(); int mcasts_received=mcasts.size(); int expected_mcasts=tuple.getVal2(); if(mcasts_received != expected_mcasts) { all_received=false; break; } runStableProtocol(receiver.ch); } if(all_received) break; Util.sleep(500); } for(Tuple tuple: expected_messages) { MyReceiver receiver=tuple.getVal1(); List mcasts=receiver.getMulticasts(); int mcasts_received=mcasts.size(); System.out.println("receiver " + receiver + ": mcasts=" + mcasts_received); } for(Tuple tuple: expected_messages) { MyReceiver receiver=tuple.getVal1(); List mcasts=receiver.getMulticasts(); int mcasts_received=mcasts.size(); int expected_mcasts=tuple.getVal2(); assert mcasts_received == expected_mcasts : "(" + receiver.name + ") num_mcasts=" + print(mcasts) + " expected: " + expected_mcasts + ")"; } } protected Tuple make(MyReceiver r, int expected_msgs) { return new Tuple(r, expected_msgs); } private static String print(List msgs) { StringBuilder sb=new StringBuilder(); for(Message msg: msgs) { sb.append(msg.getSrc()).append(": ").append(msg.getObject()).append(" "); } return sb.toString(); } private static void modifyConfigs(JChannel ... channels) throws Exception { for(JChannel ch: channels) { ProtocolStack stack=ch.getProtocolStack(); stack.removeProtocol("MERGE2"); stack.removeProtocol("FC"); stack.removeProtocol("VERIFY_SUSPECT"); NAKACK nak=(NAKACK)stack.findProtocol(NAKACK.class); if(nak != null) nak.setLogDiscardMessages(false); } } protected static class MyReceiver extends ReceiverAdapter { final String name; View view=null; final JChannel ch; final List mcasts=new ArrayList(20); public MyReceiver(String name, JChannel ch) { this.name=name; this.ch=ch; } public void receive(Message msg) { Address dest=msg.getDest(); if(dest == null) mcasts.add(msg); } public void viewAccepted(View new_view) { view=new_view; } public List getMulticasts() { return mcasts; } public void clear() {mcasts.clear();} public Address getAddress() {return ch != null? ch.getAddress() : null;} public String toString() { return name; } } }libjgroups-java-2.12.2.Final.orig/tests/junit/org/jgroups/tests/OverlappingUnicastMergeTest.java0000644000175000017500000001615311647260573033002 0ustar moellermoellerpackage org.jgroups.tests; import org.jgroups.*; import org.jgroups.stack.ProtocolStack; import org.jgroups.util.Util; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; /** * Tests overlapping merges, e.g. A: {A,B}, B: {A,B} and C: {A,B,C}. Tests unicast tables
                    * Related JIRA: https://jira.jboss.org/jira/browse/JGRP-940 * @author Bela Ban */ @Test(groups=Global.STACK_DEPENDENT,sequential=true) public class OverlappingUnicastMergeTest extends ChannelTestBase { private JChannel a, b, c; private MyReceiver ra, rb, rc; @BeforeMethod void start() throws Exception { ra=new MyReceiver("A"); rb=new MyReceiver("B"); rc=new MyReceiver("C"); a=createChannel(true, 3); a.setReceiver(ra); b=createChannel(a); b.setReceiver(rb); c=createChannel(a); c.setReceiver(rc); modifyConfigs(a, b, c); a.connect("OverlappingUnicastMergeTest"); b.connect("OverlappingUnicastMergeTest"); c.connect("OverlappingUnicastMergeTest"); View view=c.getView(); assertEquals("view is " + view, 3, view.size()); } @AfterMethod void tearDown() throws Exception { Util.close(c,b,a); } public void testWithAllViewsInSync() throws Exception { sendAndCheckMessages(5, a, b, c); } /** * Verifies that unicasts are received correctly by all participants after an overlapping merge. The following steps * are executed: *
                      *
                    1. Group is {A,B,C}, A is the coordinator *
                    2. MERGE2 is removed from all members *
                    3. VERIFY_SUSPECT is removed from all members *
                    4. Everyone sends 5 unicast messages to everyone else *
                    5. A VIEW(B,C) is injected into B and C *
                    6. B and C install {B,C} *
                    7. B and C trash the connection table for A in UNICAST *
                    8. A still has view {A,B,C} and all connection tables intact in UNICAST *
                    9. We now send N unicasts from everyone to everyone else, all the unicasts should be received. *
                    */ public void testWithViewBC() throws Exception { System.out.println("A's view: " + a.getView()); // Inject view {B,C} into B and C: View new_view=Util.createView(b.getAddress(), 10, b.getAddress(), c.getAddress()); injectView(new_view, b, c); assertEquals("A's view is " + a.getView(), 3, a.getView().size()); assertEquals("B's view is " + b.getView(), 2, b.getView().size()); assertEquals("C's view is " + c.getView(), 2, c.getView().size()); sendAndCheckMessages(5, a, b, c); } public void testWithViewA() throws Exception { // Inject view {A} into A, B and C: View new_view=Util.createView(a.getAddress(), 10, a.getAddress()); injectView(new_view, a, b, c); sendAndCheckMessages(5, a, b, c); } public void testWithViewC() throws Exception { // Inject view {A} into A, B and C: View new_view=Util.createView(c.getAddress(), 10, c.getAddress()); injectView(new_view, a, b, c); sendAndCheckMessages(5, a, b, c); } public void testWithEveryoneHavingASingletonView() throws Exception { // Inject view {A} into A, B and C: injectView(Util.createView(a.getAddress(), 10, a.getAddress()), a); injectView(Util.createView(b.getAddress(), 10, b.getAddress()), b); injectView(Util.createView(c.getAddress(), 10, c.getAddress()), c); sendAndCheckMessages(5, a, b, c); } private static void injectView(View view, JChannel ... channels) { for(JChannel ch: channels) { ch.down(new Event(Event.VIEW_CHANGE, view)); ch.up(new Event(Event.VIEW_CHANGE, view)); } for(JChannel ch: channels) { MyReceiver receiver=(MyReceiver)ch.getReceiver(); System.out.println("[" + receiver.name + "] view=" + ch.getView()); } } private void sendAndCheckMessages(int num_msgs, JChannel ... channels) throws Exception { ra.clear(); rb.clear(); rc.clear(); // 1. send unicast messages Set
                    mbrs=new HashSet
                    (channels.length); for(JChannel ch: channels) mbrs.add(ch.getAddress()); for(JChannel ch: channels) { Address addr=ch.getAddress(); for(Address dest: mbrs) { for(int i=1; i <= num_msgs; i++) { ch.send(dest, null, "unicast msg #" + i + " from " + addr); } } } int total_msgs=num_msgs * channels.length; MyReceiver[] receivers=new MyReceiver[channels.length]; for(int i=0; i < channels.length; i++) receivers[i]=(MyReceiver)channels[i].getReceiver(); checkReceivedMessages(total_msgs, receivers); } private static void checkReceivedMessages(int num_ucasts, MyReceiver ... receivers) { for(int i=0; i < 20; i++) { boolean all_received=true; for(MyReceiver receiver: receivers) { List ucasts=receiver.getUnicasts(); int ucasts_received=ucasts.size(); if(num_ucasts != ucasts_received) { all_received=false; break; } } if(all_received) break; Util.sleep(500); } for(MyReceiver receiver: receivers) { List ucasts=receiver.getUnicasts(); int ucasts_received=ucasts.size(); System.out.println("receiver " + receiver + ": ucasts=" + ucasts_received); assertEquals("ucasts for " + receiver + ": " + print(ucasts), num_ucasts, ucasts_received); } } public static String print(List list) { StringBuilder sb=new StringBuilder(); for(Message msg: list) { sb.append(msg.getSrc()).append(": ").append(msg.getObject()).append(" "); } return sb.toString(); } private static void modifyConfigs(JChannel ... channels) throws Exception { for(JChannel ch: channels) { ProtocolStack stack=ch.getProtocolStack(); stack.removeProtocol("MERGE2"); stack.removeProtocol("VERIFY_SUSPECT"); stack.removeProtocol("FC"); } } private static class MyReceiver extends ReceiverAdapter { final String name; final List ucasts=new ArrayList(20); public MyReceiver(String name) { this.name=name; } public void receive(Message msg) { Address dest=msg.getDest(); boolean mcast=dest == null; if(!mcast) ucasts.add(msg); } public void viewAccepted(View new_view) { // System.out.println("[" + name + "] " + new_view); } public List getUnicasts() { return ucasts; } public void clear() {ucasts.clear();} public String toString() { return name; } } }libjgroups-java-2.12.2.Final.orig/tests/junit/org/jgroups/tests/PrioTest.java0000644000175000017500000001232211647260573027110 0ustar moellermoellerpackage org.jgroups.tests; import org.jgroups.Global; import org.jgroups.JChannel; import org.jgroups.Message; import org.jgroups.ReceiverAdapter; import org.jgroups.conf.ClassConfigurator; import org.jgroups.protocols.PRIO; import org.jgroups.protocols.PrioHeader; import org.jgroups.protocols.pbcast.NAKACK; import org.jgroups.stack.ProtocolStack; import org.jgroups.util.Util; import org.testng.annotations.AfterTest; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; import java.util.LinkedList; import java.util.List; import java.util.concurrent.CyclicBarrier; /** * @author Bela Ban */ @Test(groups=Global.STACK_DEPENDENT,sequential=false) public class PrioTest extends ChannelTestBase { protected JChannel c1, c2; protected PrioReceiver r1, r2; protected static final short PRIO_ID=ClassConfigurator.getProtocolId(PRIO.class); @BeforeTest void init() throws Exception { c1=createChannel(true, 2, "A"); c1.getProtocolStack().insertProtocol(new PRIO(), ProtocolStack.ABOVE, NAKACK.class); c2=createChannel(c1, "B"); c1.connect("PrioTest"); r1=new PrioReceiver(); c1.setReceiver(r1); c2.connect("PrioTest"); r2=new PrioReceiver(); c2.setReceiver(r2); assert c2.getView().size() == 2; } @AfterTest void destroy() { Util.close(c2, c1); } public void testPrioritizedMessages() throws Exception { byte[] prios={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30}; PrioSender[] senders=new PrioSender[prios.length]; final CyclicBarrier barrier=new CyclicBarrier(prios.length +1); for(int i=0; i < prios.length; i++) { senders[i]=new PrioSender(c1, prios[i], barrier); senders[i].start(); } Util.sleep(500); barrier.await(); // starts the senders for(PrioSender sender: senders) sender.join(); List list1=r1.getMsgs(), list2=r2.getMsgs(); for(int i=0; i < 20; i++) { if(list1.size() == prios.length && list2.size() == prios.length) break; Util.sleep(1000); } System.out.println("R1: " + Util.print(list1) + "\nR2: " + Util.print(list2)); assert list1.size() == prios.length; assert list2.size() == prios.length; // Mike: how can we test this ? It seems this is not deterministic... which is fine, I guess, but hard to test ! checkOrdering(list1, list2); } /** * Verifies that the messages are predominantly in prioritized order. * The latter means that messages with higher prio should be mostly delivered before messages with lower prio. * 'Mostly' is needed because we cannot guarantee that all messages with higher prio are delivered before messages * with lower prio. * @param list1 * @param list2 */ protected void checkOrdering(List list1, List list2) { // check that the left half of the messages have a higher prio than the right half System.out.print("checking the ordering of list1: "); _check(list1); System.out.print("checking the ordering of list2: "); _check(list2); } protected void _check(List list) { int middle=list.size() / 2; int sum=0; for(int num: list) sum+=num; double median_val=sum / list.size(); // make sure the values [0 .. middle] have smaller values than list[middle] int correct=0; for(int i=0; i <= middle; i++) { if(list.get(i) <= median_val) correct++; } // make sure the values [middle+1 .. list.size() -1] have bigger values than list[middle+1] for(int i=middle+1; i < list.size() -1; i++) { if(list.get(i) >= median_val) correct++; } double correct_percentage=correct / (double)list.size(); System.out.println("OK. The percentage of correct values is " + (correct_percentage * 100) + "%"); assert correct_percentage >= 0.7 : "FAIL. The percentage of correct values is " + (correct_percentage * 100) + "%"; } protected static class PrioSender extends Thread { protected final JChannel ch; protected final byte prio; protected final CyclicBarrier barrier; public PrioSender(JChannel ch, byte prio, CyclicBarrier barrier) { this.ch=ch; this.prio=prio; this.barrier=barrier; } public void run() { Message msg=new Message(null, null, new Integer(prio)); PrioHeader hdr=new PrioHeader(prio); msg.putHeader(PRIO_ID, hdr); try { barrier.await(); ch.send(msg); } catch(Exception e) { e.printStackTrace(); } } } protected static class PrioReceiver extends ReceiverAdapter { protected final List msgs=new LinkedList(); public List getMsgs() { return msgs; } public void receive(Message msg) { msgs.add((Integer)msg.getObject()); } } } libjgroups-java-2.12.2.Final.orig/tests/junit/org/jgroups/tests/ReconciliationTest.java0000644000175000017500000004170611647260573031151 0ustar moellermoellerpackage org.jgroups.tests; import org.jgroups.*; import org.jgroups.protocols.DISCARD; import org.jgroups.protocols.pbcast.NAKACK; import org.jgroups.stack.ProtocolStack; import org.jgroups.util.Util; import org.testng.Assert; import org.testng.annotations.AfterMethod; import org.testng.annotations.Test; import java.io.*; import java.util.*; /** * Various tests for the FLUSH protocol * @author Bela Ban */ @Test(groups=Global.FLUSH,sequential=true) public class ReconciliationTest extends ChannelTestBase { private List channels; private List receivers; @AfterMethod void tearDown() throws Exception { if(channels != null) { for(JChannel channel:channels) { channel.close(); } } Util.sleep(500); } /** * Test scenario: *
                      *
                    • 3 members: A,B,C *
                    • All members have DISCARD which does not discard any * messages ! *
                    • B (in DISCARD) ignores all messages from C *
                    • C multicasts 5 messages to the cluster, A and C receive them *
                    • New member D joins *
                    • Before installing view {A,B,C,D}, FLUSH updates B with all of C's 5 * messages *
                    */ public void testReconciliationFlushTriggeredByNewMemberJoin() throws Exception { FlushTrigger t=new FlushTrigger() { public void triggerFlush() { log.info("Joining D, this will trigger FLUSH and a subsequent view change to {A,B,C,D}"); JChannel newChannel; try { newChannel=createChannel(channels.get(0)); newChannel.connect("ReconciliationTest"); channels.add(newChannel); } catch(Exception e) { e.printStackTrace(); } }; }; String apps[]={"A", "B", "C"}; reconciliationHelper(apps, t); } /** * Test scenario: *
                      *
                    • 3 members: A,B,C *
                    • All members have DISCARD which does not discard any * messages ! *
                    • B (in DISCARD) ignores all messages from C *
                    • C multicasts 5 messages to the cluster, A and C receive them *
                    • A then runs a manual flush by calling Channel.start/stopFlush() *
                    • Before installing view {A,B}, FLUSH makes A sends its 5 messages * received from C to B *
                    */ public void testReconciliationFlushTriggeredByManualFlush() throws Exception { FlushTrigger t=new FlushTrigger() { public void triggerFlush() { JChannel channel=channels.get(0); boolean rc=Util.startFlush(channel); log.info("manual flush success=" + rc); channel.stopFlush(); }; }; String apps[]={"A", "B", "C"}; reconciliationHelper(apps, t); } /** * Test scenario: *
                      *
                    • 3 members: A,B,C *
                    • All members have DISCARD which does not discard any * messages ! *
                    • B (in DISCARD) ignores all messages from C *
                    • C multicasts 5 messages to the cluster, A and C receive them *
                    • C then 'crashes' (Channel.shutdown()) *
                    • Before installing view {A,B}, FLUSH makes A sends its 5 messages * received from C to B *
                    */ public void testReconciliationFlushTriggeredByMemberCrashing() throws Exception { FlushTrigger t=new FlushTrigger() { public void triggerFlush() { JChannel channel=channels.remove(channels.size() - 1); try { Util.shutdown(channel); } catch(Exception e) { log.error("failed shutting down the channel", e); } }; }; String apps[]={"A", "B", "C"}; reconciliationHelper(apps, t); } /** * Tests reconciliation. Creates N channels, based on 'names'. Say we have A, B and C. Then we have the second but * last node (B) discard all messages from the last node (C). Then the last node (C) multicasts 5 messages. We check * that the 5 messages have been received correctly by all nodes but the second-but-last node (B). Then we remove * DISCARD from B and trigger a manual flush. After the flush, B should also have received the 5 messages sent * by C. * @param names * @param ft * @throws Exception */ protected void reconciliationHelper(String[] names, FlushTrigger ft) throws Exception { // create channels and setup receivers int channelCount=names.length; channels=new ArrayList(names.length); receivers=new ArrayList(names.length); for(int i=0;i < channelCount;i++) { JChannel channel; if(i == 0) { channel=createChannel(true, names.length+2, names[i]); modifyNAKACK(channel); } else channel=createChannel(channels.get(0), names[i]); MyReceiver r=new MyReceiver(channel, names[i]); receivers.add(r); channels.add(channel); channel.setReceiver(r); channel.connect("ReconciliationTest"); Util.sleep(250); } View view=channels.get(channels.size() -1).getView(); System.out.println("view: " + view); assert view.size() == channels.size(); JChannel last=channels.get(channels.size() - 1); JChannel nextToLast=channels.get(channels.size() - 2); System.out.println(nextToLast.getAddress() + " is now discarding messages from " + last.getAddress()); insertDISCARD(nextToLast, last.getAddress()); String lastsName=names[names.length - 1]; String nextToLastName=names[names.length - 2]; printDigests(channels, "\nDigests before " + lastsName + " sends any messages:"); // now last sends 5 messages: System.out.println("\n" + lastsName + " sending 5 messages; " + nextToLastName + " will ignore them, but others will receive them"); for(int i=1;i <= 5;i++) last.send(null, null, new Integer(i)); Util.sleep(1000); // until al messages have been received, this is asynchronous so we need to wait a bit printDigests(channels, "\nDigests after " + lastsName + " sent messages:"); MyReceiver lastReceiver=receivers.get(receivers.size() - 1); MyReceiver nextToLastReceiver=receivers.get(receivers.size() - 2); // check last (must have received its own messages) Map> map=lastReceiver.getMsgs(); Assert.assertEquals(map.size(), 1, "we should have only 1 sender, namely C at this time"); List list=map.get(last.getAddress()); System.out.println("\n" + lastsName + ": messages received from " + lastsName + ": " + list); Assert.assertEquals(list.size(), 5, "correct msgs: " + list); // check nextToLast (should have received none of last messages) map=nextToLastReceiver.getMsgs(); Assert.assertEquals(map.size(), 0, "we should have no sender at this time"); list=map.get(last.getAddress()); System.out.println(nextToLastName + ": messages received from " + lastsName + ": " + list); assert list == null; List otherReceivers=receivers.subList(0, receivers.size() - 2); // check other (should have received last's messages) for(MyReceiver receiver:otherReceivers) { map=receiver.getMsgs(); Assert.assertEquals(map.size(), 1, "we should have only 1 sender"); list=map.get(last.getAddress()); System.out.println(receiver.name + ": messages received from " + lastsName + ": " + list); Assert.assertEquals(list.size(), 5, "correct msgs" + list); } removeDISCARD(nextToLast); Address address=last.getAddress(); ft.triggerFlush(); int cnt=20; View v; while((v=channels.get(0).getView()) != null && cnt > 0) { cnt--; if(v.size() == channels.size()) break; Util.sleep(1000); } assert channels.get(0).getView().size() == channels.size(); printDigests(channels, "\nDigests after reconciliation (B should have received the 5 messages from B now):"); // check that member with discard (should have received all missing // messages map=nextToLastReceiver.getMsgs(); Assert.assertEquals(map.size(), 1, "we should have 1 sender at this time"); list=map.get(address); System.out.println("\n" + nextToLastName + ": messages received from " + lastsName + " : " + list); Assert.assertEquals(5, list.size()); } /** Sets discard_delivered_msgs to false */ protected void modifyNAKACK(JChannel ch) { if(ch == null) return; NAKACK nakack=(NAKACK)ch.getProtocolStack().findProtocol(NAKACK.class); if(nakack != null) nakack.setDiscardDeliveredMsgs(false); } private static void printDigests(List channels, String message) { System.out.println(message); for(JChannel channel:channels) { System.out.println("[" + channel.getAddress() + "] " + channel.downcall(Event.GET_DIGEST_EVT).toString()); } } private static void insertDISCARD(JChannel ch, Address exclude) throws Exception { DISCARD discard=new DISCARD(); discard.setExcludeItself(true); discard.addIgnoreMember(exclude); // ignore messages from this member ch.getProtocolStack().insertProtocol(discard, ProtocolStack.BELOW, "NAKACK"); } private static void removeDISCARD(JChannel...channels) throws Exception { for(JChannel ch:channels) { ch.getProtocolStack().removeProtocol("DISCARD"); } } private interface FlushTrigger { void triggerFlush(); } private class MyReceiver extends ExtendedReceiverAdapter { Map> msgs=new HashMap>(10); Channel channel; String name; public MyReceiver(Channel ch,String name) { this.channel=ch; this.name=name; } public Map> getMsgs() { return msgs; } public void reset() { msgs.clear(); } public void receive(Message msg) { List list=msgs.get(msg.getSrc()); if(list == null) { list=new ArrayList(); msgs.put(msg.getSrc(), list); } list.add((Integer)msg.getObject()); log.debug("[" + name + " / " + channel.getAddress() + "]: received message from " + msg.getSrc() + ": " + msg.getObject()); } } // @Test(invocationCount=10) public void testVirtualSynchrony() throws Exception { JChannel c1 = createChannel(true,2); Cache cache_1 = new Cache(c1, "cache-1"); c1.connect("testVirtualSynchrony"); JChannel c2 = createChannel(c1); Cache cache_2 = new Cache(c2, "cache-2"); c2.connect("testVirtualSynchrony"); Assert.assertEquals(c2.getView().size(), 2, "view: " + c1.getView()); // start adding messages flush(c1, 5000); // flush all pending message out of the system so // everyone receives them for(int i = 1;i <= 20;i++) { if(i % 2 == 0) cache_1.put(i, true); // even numbers else cache_2.put(i, true); // odd numbers } flush(c1, 5000); System.out.println("cache_1 (" + cache_1.size() + " elements): " + cache_1 + "\ncache_2 (" + cache_2.size() + " elements): " + cache_2); Assert.assertEquals(cache_1.size(), cache_2.size(), "cache 1: " + cache_1 + "\ncache 2: " + cache_2); Assert.assertEquals(20, cache_1.size(), "cache 1: " + cache_1 + "\ncache 2: " + cache_2); Util.close(c2,c1); } private void flush(Channel channel, long timeout) { if(channel.flushSupported()) { boolean success=Util.startFlush(channel); channel.stopFlush(); log.debug("startFlush(): " + success); assertTrue(success); } else Util.sleep(timeout); } private class Cache extends ExtendedReceiverAdapter { protected final Map data; Channel ch; String name; public Cache(Channel ch,String name) { this.data=new HashMap(); this.ch=ch; this.name=name; this.ch.setReceiver(this); } protected Object get(Object key) { synchronized(data) { return data.get(key); } } protected void put(Object key, Object val) throws Exception { Object[] buf=new Object[2]; buf[0]=key; buf[1]=val; Message msg=new Message(null, null, buf); ch.send(msg); } protected int size() { synchronized(data) { return data.size(); } } public void receive(Message msg) { Object[] modification=(Object[])msg.getObject(); Object key=modification[0]; Object val=modification[1]; synchronized(data) { // System.out.println("****** [" + name + "] received PUT(" + // key + ", " + val + ") " + " from " + msg.getSrc() + " // *******"); data.put(key, val); } } public byte[] getState() { byte[] state=null; synchronized(data) { try { state=Util.objectToByteBuffer(data); } catch(Exception e) { e.printStackTrace(); return null; } } return state; } public byte[] getState(String state_id) { return getState(); } @SuppressWarnings("unchecked") public void setState(byte[] state) { Map m; try { m=(Map)Util.objectFromByteBuffer(state); synchronized(data) { data.clear(); data.putAll(m); } } catch(Exception e) { e.printStackTrace(); } } public void setState(String state_id, byte[] state) { setState(state); } public void getState(OutputStream ostream) { ObjectOutputStream oos=null; try { oos=new ObjectOutputStream(ostream); synchronized(data) { oos.writeObject(data); } oos.flush(); } catch(IOException e) { } finally { try { if(oos != null) oos.close(); } catch(IOException e) { System.err.println(e); } } } public void getState(String state_id, OutputStream ostream) { getState(ostream); } @SuppressWarnings("unchecked") public void setState(InputStream istream) { ObjectInputStream ois=null; try { ois=new ObjectInputStream(istream); Map m=(Map)ois.readObject(); synchronized(data) { data.clear(); data.putAll(m); } } catch(Exception e) { } finally { try { if(ois != null) ois.close(); } catch(IOException e) { System.err.println(e); } } } public void setState(String state_id, InputStream istream) { setState(istream); } public void clear() { synchronized(data) { data.clear(); } } public void viewAccepted(View new_view) { log("view is " + new_view); } public String toString() { synchronized(data) { TreeMap map=new TreeMap(data); return map.keySet().toString(); } } private void log(String msg) { log.debug("-- [" + name + "] " + msg); } } }libjgroups-java-2.12.2.Final.orig/tests/junit/org/jgroups/tests/ReplicatedHashMapStartupTest.java0000644000175000017500000001102211647260573033074 0ustar moellermoellerpackage org.jgroups.tests; import static java.util.concurrent.TimeUnit.SECONDS; import org.jgroups.*; import org.jgroups.blocks.ReplicatedHashMap; import org.jgroups.protocols.pbcast.GMS; import org.jgroups.stack.ProtocolStack; import org.jgroups.util.Util; import org.testng.Assert; import org.testng.annotations.Test; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Vector; /** * Tests concurrent startup or replicated hashmap. * * @author vlada * belaban Exp $ */ @Test(groups= { Global.FLUSH }, sequential=true) public class ReplicatedHashMapStartupTest extends ChannelTestBase { public void testConcurrentStartup4Members() { List> channels=new ArrayList>(4); try { concurrentStartupHelper(channels, 4); } catch(Exception e) { log.warn("Exception while running testConcurrentStartup4Members", e); for(ReplicatedHashMap map:channels) { map.stop(); Util.sleep(1000); } } } public void testConcurrentStartup8Members() { List> channels=new ArrayList>(8); try { concurrentStartupHelper(channels, 8); } catch(Exception e) { log.warn("Exception while running testConcurrentStartup8Members", e); for(ReplicatedHashMap map:channels) { map.stop(); Util.sleep(1000); } } } protected void concurrentStartupHelper(List> channels, int channelCount) throws Exception { MyNotification n=new MyNotification(); JChannel first=null; for(int i=0;i < channelCount;i++) { JChannel c; if(i == 0) { c=createChannel(true, channelCount); modifyGMS(c); first=c; } else { c=createChannel(first); } ReplicatedHashMap map=new ReplicatedHashMap(c); channels.add(map); map.addNotifier(n); map.setBlockingUpdates(true); } //do a very concurrent startup for(ReplicatedHashMap map:channels) { map.getChannel().connect("ReplicatedHashMapStartupTest"); map.start(0); map.put(map.getChannel().getAddress(), new Integer(1)); Util.sleep(100); } boolean converged=false; for(int timeoutToConverge=120,counter=0;counter < timeoutToConverge && !converged;SECONDS.sleep(1),counter++) { for(ReplicatedHashMap map:channels) { converged=map.getChannel().getView().size() == channelCount; if(!converged) break; } } //verify all view are correct for(ReplicatedHashMap map:channels) { Assert.assertEquals(map.getChannel().getView().size(), channelCount, "Correct view"); } for(ReplicatedHashMap map:channels) { map.removeNotifier(n); } //verify all maps have all elements for(ReplicatedHashMap map:channels) { Assert.assertEquals(map.size(), channelCount, "Correct size"); } log.info("stopping replicated hash maps..."); for(ReplicatedHashMap map:channels) { map.stop(); Util.sleep(1000); } } private static void modifyGMS(JChannel c) { ProtocolStack stack=c.getProtocolStack(); GMS gms=(GMS)stack.findProtocol(GMS.class); if(gms != null) gms.setLogCollectMessages(false); } private class MyNotification implements org.jgroups.blocks.ReplicatedHashMap.Notification { public void contentsCleared() {} public void contentsSet(Map new_entries) {} public void entryRemoved(K key) {} public void entrySet(K key, V value) {} public void viewChange(View view, Vector
                    new_mbrs, Vector
                    old_mbrs) { log.info("Got view in ReplicatedHashMap notifier " + view); } } } libjgroups-java-2.12.2.Final.orig/tests/junit/org/jgroups/tests/ReplicatedHashMapTest.java0000644000175000017500000002225411647260573031522 0ustar moellermoellerpackage org.jgroups.tests; import org.jgroups.Global; import org.jgroups.JChannel; import org.jgroups.blocks.ReplicatedHashMap; import org.testng.Assert; import org.testng.annotations.AfterClass; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** * Test methods for ReplicatedHashMap * * @author Bela Ban * Exp $ */ @Test(groups=Global.STACK_DEPENDENT,sequential=true) public class ReplicatedHashMapTest extends ChannelTestBase { private ReplicatedHashMap map1; private ReplicatedHashMap map2; private ConcurrentHashMap wrap=new ConcurrentHashMap(); @BeforeClass protected void setUp() throws Exception { JChannel c1=createChannel(true, 2); this.map1=new ReplicatedHashMap(c1, false); map1.setBlockingUpdates(true); c1.connect("ReplicatedHashMapTest"); this.map1.start(5000); JChannel c2=createChannel(c1); this.map2=new ReplicatedHashMap(wrap, c2, false); map2.setBlockingUpdates(true); c2.connect("ReplicatedHashMapTest"); this.map2.start(5000); } @AfterMethod protected void clean() { map1.clear(); map2.clear(); } @AfterClass protected void tearDown() throws Exception { this.map1.stop(); this.map2.stop(); } public void testEqualsEtc() { map1.put("key1", "value1"); assertEquals(this.map1, this.map2); Assert.assertEquals(this.map1.hashCode(), this.map2.hashCode()); Assert.assertEquals(this.map1.toString(), this.map2.toString()); assertEquals(this.wrap, this.map1); } public void testSize() { Assert.assertEquals(0, this.map1.size()); Assert.assertEquals(this.map2.size(), this.map1.size()); this.map1.put("key1", "value1"); Assert.assertEquals(1, this.map1.size()); Assert.assertEquals(this.map2.size(), this.map1.size()); this.map2.put("key2", "value2"); Assert.assertEquals(2, this.map1.size()); Assert.assertEquals(this.map2.size(), this.map1.size()); } public void testIsEmpty() { assertTrue(this.map1.isEmpty()); assertTrue(this.map2.isEmpty()); this.map1.put("key", "value"); assertFalse(this.map1.isEmpty()); assertFalse(this.map2.isEmpty()); } public void testContainsKey() { assertFalse(this.map1.containsKey("key1")); assertFalse(this.map2.containsKey("key1")); this.map1.put("key1", "value"); assertTrue(this.map1.containsKey("key1")); assertTrue(this.map2.containsKey("key1")); this.map2.put("key2", "value"); assertTrue(this.map1.containsKey("key2")); assertTrue(this.map2.containsKey("key2")); } public void testContainsValue() { assertFalse(this.map1.containsValue("value1")); assertFalse(this.map2.containsValue("value1")); this.map1.put("key1", "value1"); assertTrue(this.map1.containsValue("value1")); assertTrue(this.map2.containsValue("value1")); this.map2.put("key2", "value2"); assertTrue(this.map1.containsValue("value2")); assertTrue(this.map2.containsValue("value2")); } public void testPutAndGet() { assert this.map1.get("key1") == null; assert this.map2.get("key1") == null; this.map1.put("key1", "value1"); assertNotNull(this.map1.get("key1")); assertNotNull(this.map2.get("key1")); this.map2.put("key2", "value2"); assertNotNull(this.map1.get("key2")); assertNotNull(this.map2.get("key2")); } public void testPutIfAbsent() { String retval=map1.putIfAbsent("name", "Bela"); assert retval == null; retval=map1.putIfAbsent("name", "Michelle"); assertNotNull(retval); Assert.assertEquals("Bela", retval); Assert.assertEquals("Bela", map1.get("name")); Assert.assertEquals("Bela", map2.get("name")); } public void testRemove() { assert this.map1.get("key1") == null; assert this.map2.get("key1") == null; this.map1.put("key1", "value1"); this.map2.put("key2", "value2"); assertNotNull(this.map1.get("key1")); assertNotNull(this.map2.get("key1")); assertNotNull(this.map1.get("key2")); assertNotNull(this.map2.get("key2")); this.map1.remove("key1"); assert this.map1.get("key1") == null; assert this.map2.get("key1") == null; assertNotNull(this.map1.get("key2")); assertNotNull(this.map2.get("key2")); this.map2.remove("key2"); assert this.map1.get("key2") == null; assert this.map2.get("key2") == null; } public void testRemove2() { map1.put("name", "Bela"); map1.put("id", "322649"); System.out.println("map1: " + map1); boolean removed=map1.remove("id", "322000"); assertFalse(removed); assertTrue(map1.containsKey("id")); removed=map1.remove("id", "322649"); System.out.println("map1: " + map1); assertTrue(removed); assertFalse(map1.containsKey("id")); Assert.assertEquals(1, map2.size()); } public void testReplace() { map1.put("name", "Bela"); map1.put("id", "322649"); System.out.println("map1: " + map1); String val=map1.replace("id2", "322000"); Assert.assertEquals(2, map1.size()); assert map1.get("id2") == null; System.out.println("map1: " + map1); assert val == null; val=map1.replace("id", "322000"); System.out.println("map1: " + map1); assertNotNull(val); Assert.assertEquals("322649", val); Assert.assertEquals("322000", map1.get("id")); Assert.assertEquals("322000", map2.get("id")); } public void testReplace2() { map1.put("name", "Bela"); map1.put("id", "322649"); System.out.println("map1: " + map1); boolean replaced=map1.replace("id", "322000", "1"); assertFalse(replaced); Assert.assertEquals("322649", map1.get("id")); replaced=map1.replace("id", "322649", "1"); assertTrue(replaced); Assert.assertEquals("1", map1.get("id")); } public void testPutAll() { Map all1=new HashMap(); all1.put("key1", "value1"); all1.put("key2", "value2"); Map all2=new HashMap(); all2.put("key3", "value3"); all2.put("key4", "value4"); this.map1.putAll(all1); Assert.assertEquals(2, this.map1.size()); Assert.assertEquals(2, this.map2.size()); this.map2.putAll(all2); Assert.assertEquals(4, this.map1.size()); Assert.assertEquals(4, this.map2.size()); assertTrue(this.map1.containsKey("key1")); assertTrue(this.map1.containsKey("key2")); assertTrue(this.map1.containsKey("key3")); assertTrue(this.map1.containsKey("key4")); assertTrue(this.map2.containsKey("key1")); assertTrue(this.map2.containsKey("key2")); assertTrue(this.map2.containsKey("key3")); assertTrue(this.map2.containsKey("key4")); } public void testClear() { assertTrue(this.map1.isEmpty()); assertTrue(this.map2.isEmpty()); this.map1.put("key", "value"); assertFalse(this.map1.isEmpty()); assertFalse(this.map2.isEmpty()); this.map1.clear(); assertTrue(this.map1.isEmpty()); assertTrue(this.map2.isEmpty()); this.map2.put("key", "value"); assertFalse(this.map1.isEmpty()); assertFalse(this.map2.isEmpty()); this.map2.clear(); assertTrue(this.map1.isEmpty()); assertTrue(this.map2.isEmpty()); } public void testKeySet() { Map all1=new HashMap(); all1.put("key1", "value1"); all1.put("key2", "value2"); Map all2=new HashMap(); all2.put("key3", "value3"); all2.put("key4", "value4"); this.map1.putAll(all1); assertEquals(all1.keySet(), this.map1.keySet()); assertEquals(all1.keySet(), this.map2.keySet()); this.map2.putAll(all2); all1.putAll(all2); assertEquals(all1.keySet(), this.map1.keySet()); assertEquals(all1.keySet(), this.map2.keySet()); } public void testValues() { Map all1=new HashMap(); all1.put("key1", "value1"); all1.put("key2", "value2"); Map all2=new HashMap(); all2.put("key3", "value3"); all2.put("key4", "value4"); this.map1.putAll(all1); assertTrue(this.map1.values().containsAll(all1.values())); assertTrue(this.map2.values().containsAll(all1.values())); this.map2.putAll(all2); all1.putAll(all2); assertTrue(this.map1.values().containsAll(all1.values())); assertTrue(this.map2.values().containsAll(all1.values())); } }libjgroups-java-2.12.2.Final.orig/tests/junit/org/jgroups/tests/SCOPE_Test.java0000644000175000017500000001546511647260573027222 0ustar moellermoellerpackage org.jgroups.tests; import org.jgroups.*; import org.jgroups.protocols.SCOPE; import org.jgroups.protocols.UNICAST; import org.jgroups.protocols.UNICAST2; import org.jgroups.stack.Protocol; import org.jgroups.stack.ProtocolStack; import org.jgroups.util.Util; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; /** * Tests the SCOPE protocol (https://jira.jboss.org/jira/browse/JGRP-822) * @author Bela Ban */ @Test(groups=Global.STACK_DEPENDENT,sequential=true) public class SCOPE_Test extends ChannelTestBase { JChannel c1, c2; static final int NUM_MSGS=5; static final long SLEEP_TIME=1000L; @BeforeMethod void setUp() throws Exception { c1=createChannel(true, 2); c1.setName("A"); c2=createChannel(c1); c2.setName("B"); } @AfterMethod void tearDown() throws Exception { Util.close(c2, c1); } public void testRegularMulticastMessages() throws Exception { sendMessages(null, false); } public void testScopedMulticastMessages() throws Exception { sendMessages(null, true); } public void testRegularUnicastMessages() throws Exception { sendMessages(c2.getAddress(), false); } public void testScopedUnicastMessages() throws Exception { sendMessages(c2.getAddress(), true); } public void testOrderWithScopedMulticasts() throws Exception { ProtocolStack stack=c2.getProtocolStack(); Protocol neighbor=stack.findProtocol(UNICAST.class, UNICAST2.class); SCOPE scope=new SCOPE(); stack.insertProtocolInStack(scope, neighbor, ProtocolStack.ABOVE); scope.init(); c1.connect("SCOPE_Test"); c2.connect("SCOPE_Test"); assert c2.getView().size() == 2 : "c2.view is " + c2.getView(); MyScopedReceiver receiver=new MyScopedReceiver(); c2.setReceiver(receiver); Short[] scopes=new Short[]{'X', 'Y', 'Z'}; for(short scope_id: scopes) { for(long i=1; i <=5; i++) { Message msg=new Message(null, null, i); msg.setScope(scope_id); System.out.println("-- sending message " + (char)scope_id + "#" + i); c1.send(msg); } } long target_time=System.currentTimeMillis() + NUM_MSGS * SLEEP_TIME * 2, start=System.currentTimeMillis(); do { if(receiver.size() >= NUM_MSGS * scopes.length) break; Util.sleep(100); System.out.print("."); } while(target_time > System.currentTimeMillis()); long time=System.currentTimeMillis() - start; ConcurrentMap> msgs=receiver.getMsgs(); System.out.println("seqnos:"); for(Map.Entry> entry: msgs.entrySet()) { short tmp=entry.getKey(); System.out.println((char)tmp + ": " + entry.getValue()); } System.out.println(receiver.size() + " msgs in " + time + " ms"); assert receiver.size() == NUM_MSGS * scopes.length; assert time >= NUM_MSGS * SLEEP_TIME && time < NUM_MSGS *SLEEP_TIME * 2; System.out.println("checking order within the scopes:"); for(short scope_id: scopes) { List list=msgs.get(scope_id); for(int i=0; i < NUM_MSGS; i++) assert (i+1) == list.get(i); } System.out.println("OK, order is correct"); } private void sendMessages(Address dest, boolean use_scopes) throws Exception { if(use_scopes) { ProtocolStack stack=c2.getProtocolStack(); Protocol neighbor=stack.findProtocol(UNICAST.class, UNICAST2.class); SCOPE scope=new SCOPE(); stack.insertProtocolInStack(scope, neighbor, ProtocolStack.ABOVE); scope.init(); } c1.connect("SCOPE_Test"); c2.connect("SCOPE_Test"); assert c2.getView().size() == 2 : "c2.view is " + c2.getView(); MyReceiver receiver=new MyReceiver(); c2.setReceiver(receiver); for(long i=1; i <=5; i++) { Message msg=new Message(dest, null, i); if(use_scopes) msg.setScope((short)i); System.out.println("-- sending message #" + i); c1.send(msg); } long target_time=System.currentTimeMillis() + NUM_MSGS * SLEEP_TIME * 2, start=System.currentTimeMillis(); do { if(receiver.size() >= NUM_MSGS) break; Util.sleep(100); System.out.print("."); } while(target_time > System.currentTimeMillis()); long time=System.currentTimeMillis() - start; List seqnos=receiver.getSeqnos(); System.out.println("\nsequence numbers: " + seqnos + " in " + time + " ms"); assert seqnos.size() == NUM_MSGS; if(use_scopes) { assert time > SLEEP_TIME * 1 && time < NUM_MSGS * SLEEP_TIME; for(int i=0; i < NUM_MSGS; i++) assert seqnos.contains((long)i+1); } else { assert time >= NUM_MSGS * SLEEP_TIME; for(int i=0; i < NUM_MSGS; i++) assert (i+1) == seqnos.get(i); } } public static class MyReceiver extends ReceiverAdapter { /** List of unicast sequence numbers */ final List seqnos=Collections.synchronizedList(new LinkedList()); public MyReceiver() { } public List getSeqnos() { return seqnos; } public void receive(Message msg) { Util.sleep(SLEEP_TIME); Long num=(Long)msg.getObject(); seqnos.add(num); } public int size() {return seqnos.size();} } public static class MyScopedReceiver extends ReceiverAdapter { final ConcurrentMap> msgs=new ConcurrentHashMap>(); public void receive(Message msg) { Util.sleep(SLEEP_TIME); Short scope=msg.getScope(); if(scope.shortValue() > 0) { List list=msgs.get(scope); if(list == null) { list=new ArrayList(5); List tmp=msgs.putIfAbsent(scope, list); if(tmp != null) list=tmp; } list.add((Long)msg.getObject()); } } public ConcurrentMap> getMsgs() { return msgs; } public int size() { int retval=0; for(List list: msgs.values()) retval+=list.size(); return retval; } } }libjgroups-java-2.12.2.Final.orig/tests/junit/org/jgroups/tests/SendAndReceiveTest.java0000644000175000017500000001224711647260573031024 0ustar moellermoeller package org.jgroups.tests; import org.jgroups.JChannel; import org.jgroups.Message; import org.jgroups.Global; import org.testng.annotations.AfterMethod; import org.testng.annotations.Test; /** * Tests sending and receiving of messages within the same VM. Sends N messages * and expects reception of N messages within a given time. Fails otherwise. * @author Bela Ban */ @Test(groups=Global.STACK_INDEPENDENT,sequential=true) public class SendAndReceiveTest { JChannel channel; static final int NUM_MSGS=1000; static final long TIMEOUT=30000; String props1="UDP(loopback=true;mcast_port=27000;ip_ttl=1;" + "mcast_send_buf_size=64000;mcast_recv_buf_size=64000):" + //"PIGGYBACK(max_wait_time=100;max_size=32000):" + "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=600,1200,2400,4800):" + "pbcast.STABLE(desired_avg_gossip=20000):" + "FRAG(frag_size=8096):" + "pbcast.GMS(join_timeout=5000;" + "print_local_addr=true)"; String props2="UDP(loopback=false;mcast_port=27000;ip_ttl=1;" + "mcast_send_buf_size=64000;mcast_recv_buf_size=64000):" + //"PIGGYBACK(max_wait_time=100;max_size=32000):" + "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=600,1200,2400,4800):" + "pbcast.STABLE(desired_avg_gossip=20000):" + "FRAG(frag_size=8096):" + "pbcast.GMS(join_timeout=5000;" + "print_local_addr=true)"; String props3="SHARED_LOOPBACK:" + "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=600,1200,2400,4800):" + "pbcast.STABLE(desired_avg_gossip=20000):" + "FRAG(frag_size=8096):" + "pbcast.GMS(join_timeout=5000;" + "print_local_addr=true)"; private void setUp(String props) { try { channel=new JChannel(props); channel.connect("test1"); } catch(Throwable t) { t.printStackTrace(System.err); assert false : "channel could not be created"; } } @AfterMethod public void tearDown() { if(channel != null) { channel.close(); channel=null; } } /** * Sends NUM messages and expects NUM messages to be received. If * NUM messages have not been received after 20 seconds, the test failed. */ @Test public void testSendAndReceiveWithDefaultUDP_Loopback() { setUp(props1); sendMessages(NUM_MSGS); int received_msgs=receiveMessages(NUM_MSGS, TIMEOUT); assert received_msgs >= NUM_MSGS; } @Test public void testSendAndReceiveWithDefaultUDP_NoLoopback() { setUp(props2); sendMessages(NUM_MSGS); int received_msgs=receiveMessages(NUM_MSGS, TIMEOUT); assert received_msgs >= NUM_MSGS; } @Test public void testSendAndReceiveWithLoopback() { setUp(props3); sendMessages(NUM_MSGS); int received_msgs=receiveMessages(NUM_MSGS, TIMEOUT); assert received_msgs >= NUM_MSGS; } private void sendMessages(int num) { Message msg; for(int i=0; i < num; i++) { try { msg=new Message(); channel.send(msg); System.out.print(i + " "); } catch(Throwable t) { assert false : "could not send message #" + i; } } } /** * Receive at least num messages. Total time should not exceed timeout * @param num * @param timeout Must be > 0 * @return */ private int receiveMessages(int num, long timeout) { int received=0; Object msg; if(timeout <= 0) timeout=5000; long start=System.currentTimeMillis(), current, wait_time; while(true) { current=System.currentTimeMillis(); wait_time=timeout - (current - start); if(wait_time <= 0) break; try { msg=channel.receive(wait_time); if(msg instanceof Message) { received++; System.out.print("+" + received + ' '); } if(received >= num) break; } catch(Throwable t) { assert false : "failed receiving message"; } } return received; } } libjgroups-java-2.12.2.Final.orig/tests/junit/org/jgroups/tests/SequencerFailoverTest.java0000644000175000017500000001142711647260573031626 0ustar moellermoeller package org.jgroups.tests; import org.jgroups.*; import org.jgroups.protocols.pbcast.GMS; import org.jgroups.util.Util; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.LinkedList; import java.util.List; /** * Tests a SEQUENCER based stack: A, B and C. B starts multicasting messages with a monotonically increasing * number. Then A is crashed. C and B should receive *all* numbers *without* a gap. * @author Bela Ban */ @Test(groups=Global.STACK_INDEPENDENT,sequential=true) public class SequencerFailoverTest { JChannel a, b, c; // A is the coordinator static final String GROUP="SequencerFailoverTest"; static final int NUM_MSGS=50; static final String props="sequencer.xml"; @BeforeMethod public void setUp() throws Exception { a=new JChannel(props); a.setName("A"); a.connect(GROUP); b=new JChannel(props); b.setName("B"); b.connect(GROUP); c=new JChannel(props); c.setName("C"); c.connect(GROUP); } @AfterMethod public void tearDown() throws Exception { Util.close(c, b, a); } @Test public void testBroadcastSequence() throws Exception { MyReceiver rb=new MyReceiver("B"), rc=new MyReceiver("C"); b.setReceiver(rb); c.setReceiver(rc); View v2=b.getView(), v3=c.getView(); System.out.println("ch2's view: " + v2 + "\nch3's view: " + v3); assert v2.equals(v3); new Thread() { public void run() { Util.sleep(3000); System.out.println("** killing A"); try { Util.shutdown(a); } catch(Exception e) { System.err.println("failed shutting down channel " + a.getAddress() + ", exception=" + e); } System.out.println("** A killed"); injectSuspectEvent(a.getAddress(), b, c); a=null; } }.start(); for(int i=1; i <= NUM_MSGS; i++) { Util.sleep(300); b.send(new Message(null, null, new Integer(i))); System.out.print("-- messages sent: " + i + "/" + NUM_MSGS + "\r"); } System.out.println(""); v2=b.getView(); v3=c.getView(); System.out.println("B's view: " + v2 + "\nC's view: " + v3); assert v2.equals(v3); assert v2.size() == 2; int s2, s3; for(int i=15000; i > 0; i-=1000) { s2=rb.size(); s3=rc.size(); if(s2 >= NUM_MSGS && s3 >= NUM_MSGS) { System.out.print("B: " + s2 + " msgs, C: " + s3 + " msgs\r"); break; } Util.sleep(1000); System.out.print("sleeping for " + (i/1000) + " seconds (B: " + s2 + " msgs, C: " + s3 + " msgs)\r"); } System.out.println("-- verifying messages on B and C"); List list_b=rb.getList(), list_c=rc.getList(); System.out.println("B: " + list_b + "\nC: " + list_c); assert list_b.size() == list_c.size(); System.out.println("OK: both B and C have the same number of messages (" + list_b.size() + ")"); assert list_b.size() == NUM_MSGS && list_c.size() == NUM_MSGS; System.out.println("OK: both B and C have the expected number (" + NUM_MSGS + ") of messages"); System.out.println("verifying B and C have the same order"); for(int i=0; i < list_b.size(); i++) { Integer el_b=list_b.get(i), el_c=list_c.get(i); assert el_b.equals(el_c) : "element at index=" + i + " in B (" + el_b + ") is different from element " + i + " in C (" + el_c + ")"; } System.out.println("OK: B and C's message are in the same order"); } /** Injects SUSPECT event(suspected_mbr) into channels */ private static void injectSuspectEvent(Address suspected_mbr, JChannel ... channels) { Event evt=new Event(Event.SUSPECT, suspected_mbr); for(JChannel ch: channels) { GMS gms=(GMS)ch.getProtocolStack().findProtocol(GMS.class); if(gms != null) gms.up(evt); } } private static class MyReceiver extends ReceiverAdapter { private final List list=new LinkedList(); private final String name; public MyReceiver(String name) { this.name=name; } public List getList() {return list;} public int size() {return list.size();} public void receive(Message msg) { Integer val=(Integer)msg.getObject(); synchronized(list) { list.add(val); } } void clear() {list.clear();} } } libjgroups-java-2.12.2.Final.orig/tests/junit/org/jgroups/tests/SequencerOrderTest.java0000644000175000017500000001427711647260573031140 0ustar moellermoeller package org.jgroups.tests; import org.jgroups.Global; import org.jgroups.JChannel; import org.jgroups.Message; import org.jgroups.ReceiverAdapter; import org.jgroups.protocols.SHUFFLE; import org.jgroups.protocols.pbcast.NAKACK; import org.jgroups.stack.ProtocolStack; import org.jgroups.util.Util; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.LinkedList; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; /** * Tests a SEQUENCER based stack: demonstrates race condition where thread#1 * gets seqno, thread#2 gets seqno, thread#2 sends, thread#1 tries to send but * is out of order. * * In order to test total ordering, make sure that messages are sent from * concurrent senders; using one sender will cause NAKACK to FIFO order * the messages and the assertions in this test will still hold true, whether * SEQUENCER is present or not. */ @Test(groups=Global.STACK_INDEPENDENT,sequential=true) public class SequencerOrderTest { private JChannel c1, c2, c3; private MyReceiver r1, r2, r3; static final String GROUP="SequencerOrderTest"; static final int NUM_MSGS=50; // messages per thread static final int NUM_THREADS=10; static final int EXPECTED_MSGS=NUM_MSGS * NUM_THREADS; static final String props="sequencer.xml"; private Sender[] senders=new Sender[NUM_THREADS]; @BeforeMethod void setUp() throws Exception { c1=new JChannel(props); c1.setName("A"); c1.connect(GROUP); r1=new MyReceiver("A"); c1.setReceiver(r1); c2=new JChannel(props); c2.setName("B"); c2.connect(GROUP); r2=new MyReceiver("B"); c2.setReceiver(r2); c3=new JChannel(props); c3.setName("C"); c3.connect(GROUP); r3=new MyReceiver("C"); c3.setReceiver(r3); AtomicInteger num=new AtomicInteger(1); for(int i=0; i < senders.length; i++) { senders[i]=new Sender(NUM_MSGS, num, c1, c2, c3); } } @AfterMethod void tearDown() throws Exception { Util.close(c3, c2, c1); } @Test @SuppressWarnings("unchecked") public void testBroadcastSequence() throws Exception { insertShuffle(c1, c2, c3); // use concurrent senders to send messages to the group System.out.println("Starting " + senders.length + " sender threads (each sends " + NUM_MSGS + " messages)"); for(Sender sender: senders) sender.start(); for(Sender sender: senders) sender.join(20000); System.out.println("Ok, senders have completed"); final List l1=r1.getMsgs(); final List l2=r2.getMsgs(); final List l3=r3.getMsgs(); System.out.println("-- verifying messages on A and B"); verifyNumberOfMessages(EXPECTED_MSGS, l1, l2, l3); verifySameOrder(EXPECTED_MSGS, l1, l2, l3); } private static void insertShuffle(JChannel... channels) throws Exception { for(JChannel ch: channels) { SHUFFLE shuffle=new SHUFFLE(); shuffle.setDown(false); shuffle.setUp(true); shuffle.setMaxSize(10); shuffle.setMaxTime(1000); ch.getProtocolStack().insertProtocol(shuffle, ProtocolStack.BELOW, NAKACK.class); shuffle.init(); // gets the timer } } private static void verifyNumberOfMessages(int num_msgs, List ... lists) throws Exception { long end_time=System.currentTimeMillis() + 10000; while(System.currentTimeMillis() < end_time) { boolean all_correct=true; for(List list: lists) { if(list.size() != num_msgs) { all_correct=false; break; } } if(all_correct) break; Util.sleep(1000); } for(int i=0; i < lists.length; i++) System.out.println("list #" + (i+1) + ": " + lists[i]); for(int i=0; i < lists.length; i++) assert lists[i].size() == num_msgs : "list #" + (i+1) + " should have " + num_msgs + " elements"; System.out.println("OK, all lists have the same size (" + num_msgs + ")\n"); } private static void verifySameOrder(int expected_msgs, List ... lists) throws Exception { for(int index=0; index < expected_msgs; index++) { String val=null; for(List list: lists) { if(val == null) val=list.get(index); else { String val2=list.get(index); assert val.equals(val2) : "found different values at index " + index + ": " + val + " != " + val2; } } } System.out.println("OK, all lists have the same order"); } private static class Sender extends Thread { final int num_msgs; final JChannel[] channels; final AtomicInteger num; public Sender(int num_msgs, AtomicInteger num, JChannel ... channels) { this.num_msgs=num_msgs; this.num=num; this.channels=channels; } public void run() { for(int i=1; i <= num_msgs; i++) { try { JChannel ch=(JChannel)Util.pickRandomElement(channels); String channel_name=ch.getName(); ch.send(null, null, channel_name + ":" + num.getAndIncrement()); } catch(Exception e) { } } } } private static class MyReceiver extends ReceiverAdapter { final String name; final List msgs=new LinkedList(); private MyReceiver(String name) { this.name=name; } public List getMsgs() { return msgs; } public void receive(Message msg) { String val=(String)msg.getObject(); if(val != null) { synchronized(msgs) { msgs.add(val); } } } } } libjgroups-java-2.12.2.Final.orig/tests/junit/org/jgroups/tests/SharedTransportTest.java0000644000175000017500000004412011647260573031323 0ustar moellermoellerpackage org.jgroups.tests; import org.jgroups.*; import org.jgroups.conf.ConfiguratorFactory; import org.jgroups.conf.ProtocolConfiguration; import org.jgroups.conf.ProtocolStackConfigurator; import org.jgroups.protocols.BasicTCP; import org.jgroups.protocols.TCPPING; import org.jgroups.protocols.TP; import org.jgroups.protocols.UDP; import org.jgroups.stack.IpAddress; import org.jgroups.stack.Protocol; import org.jgroups.stack.ProtocolStack; import org.jgroups.util.ResourceManager; import org.jgroups.util.TimeScheduler; import org.jgroups.util.Util; import org.testng.AssertJUnit; import org.testng.annotations.AfterMethod; import org.testng.annotations.Test; import java.net.InetAddress; import java.util.LinkedList; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; /** * Tests which test the shared transport * @author Bela Ban */ @Test(groups=Global.STACK_DEPENDENT,sequential=true) public class SharedTransportTest extends ChannelTestBase { private JChannel a, b, c; private MyReceiver r1, r2, r3; static final String SINGLETON_1="singleton-1", SINGLETON_2="singleton-2"; @AfterMethod protected void tearDown() throws Exception { Util.close(c,b,a); r1=r2=r3=null; } public void testCreationNonSharedTransport() throws Exception { a=createChannel(true); a.connect("SharedTransportTest.testCreationNonSharedTransport"); View view=a.getView(); System.out.println("view = " + view); assert view.size() == 1; } public void testCreationOfDuplicateCluster() throws Exception { a=createSharedChannel(SINGLETON_1); // makeUnique(a, 2); b=createSharedChannel(SINGLETON_1); a.connect("x"); try { b.connect("x"); assert false : "b should not be able to join cluster 'x' as a has already joined it"; } catch(Exception ex) { System.out.println("b was not able to join the same cluster (\"x\") as expected"); } } public void testView() throws Exception { a=createSharedChannel(SINGLETON_1); b=createSharedChannel(SINGLETON_2); a.setReceiver(new MyReceiver(SINGLETON_1)); b.setReceiver(new MyReceiver(SINGLETON_2)); a.connect("x"); b.connect("x"); View view=a.getView(); assert view.size() == 2; view=b.getView(); assert view.size() == 2; } public void testView2() throws Exception { a=createSharedChannel(SINGLETON_1); b=createSharedChannel(SINGLETON_1); a.setReceiver(new MyReceiver("first-channel")); b.setReceiver(new MyReceiver("second-channel")); a.connect("x"); b.connect("y"); View view=a.getView(); assert view.size() == 1; view=b.getView(); assert view.size() == 1; } /** * Tests http://jira.jboss.com/jira/browse/JGRP-689: with TCP or UDP.ip_mcast=false, the transport iterates through * the 'members' instance variable to send a group message. However, the 'members' var is the value of the last * view change received. If we receive multiple view changes, this leads to incorrect membership. * @throws Exception */ public void testView3() throws Exception { a=createSharedChannel(SINGLETON_1); b=createSharedChannel(SINGLETON_1); c=createSharedChannel(SINGLETON_2); r1=new MyReceiver("A::" + SINGLETON_1); r2=new MyReceiver("B::" + SINGLETON_1); r3=new MyReceiver("C::" + SINGLETON_2); a.setReceiver(r1); b.setReceiver(r2); c.setReceiver(r3); a.connect("cluster-1"); c.connect("cluster-1"); View view=a.getView(); assert view.size() == 2; view=c.getView(); assert view.size() == 2; a.send(new Message(null, null, "msg-1")); c.send(new Message(null, null, "msg-2")); Util.sleep(1000); // async sending - wait a little List list=r1.getList(); assert list.size() == 2; list=r3.getList(); assert list.size() == 2; r1.clear(); r2.clear(); r3.clear(); b.connect("cluster-2"); a.send(new Message(null, null, "msg-3")); b.send(new Message(null, null, "msg-4")); c.send(new Message(null, null, "msg-5")); Util.sleep(1000); // async sending - wait a little // printLists(r1, r2, r3); list=r1.getList(); assert list.size() == 2; list=r2.getList(); assert list.size() == 1; list=r3.getList(); assert list.size() == 2; } public void testView4() throws Exception { a=createSharedChannel(SINGLETON_1); r1=new MyReceiver("A::" + SINGLETON_1); a.setReceiver(r1); a.connect("cluster-X"); a.send(new Message(null, null, "msg-1")); Util.sleep(1000); // async sending - wait a little List list=r1.getList(); assert list.size() == 1; a.send(new Message(null, null, "msg-2")); a.send(new Message(null, null, "msg-3")); a.send(new Message(null, null, "msg-4")); Util.sleep(1000); // async sending - wait a little list=r1.getList(); assert list.size() == 4; } public void testSharedTransportAndNonsharedTransport() throws Exception { a=createSharedChannel(SINGLETON_1); b=createChannel(); a.setReceiver(new MyReceiver("first-channel")); b.setReceiver(new MyReceiver("second-channel")); a.connect("x"); b.connect("x"); View view=a.getView(); assert view.size() == 2; view=b.getView(); assert view.size() == 2; } public void testCreationOfDifferentCluster() throws Exception { a=createSharedChannel(SINGLETON_1); b=createSharedChannel(SINGLETON_2); a.connect("x"); b.connect("x"); View view=b.getView(); System.out.println("b's view is " + view); assert view.size() == 2; } public void testReferenceCounting() throws ChannelException { a=createSharedChannel(SINGLETON_1); r1=new MyReceiver("a"); a.setReceiver(r1); b=createSharedChannel(SINGLETON_1); r2=new MyReceiver("b"); b.setReceiver(r2); c=createSharedChannel(SINGLETON_1); r3=new MyReceiver("c"); c.setReceiver(r3); a.connect("A"); b.connect("B"); c.connect("C"); a.send(null, null, "message from a"); b.send(null, null, "message from b"); c.send(null, null, "message from c"); Util.sleep(500); assert r1.size() == 1; assert r2.size() == 1; assert r3.size() == 1; r1.clear(); r2.clear(); r3.clear(); b.disconnect(); System.out.println("\n"); a.send(null, null, "message from a"); c.send(null, null, "message from c"); Util.sleep(500); assert r1.size() == 1 : "size should be 1 but is " + r1.size(); assert r3.size() == 1 : "size should be 1 but is " + r3.size(); r1.clear(); r3.clear(); c.disconnect(); System.out.println("\n"); a.send(null, null, "message from a"); Util.sleep(500); assert r1.size() == 1; } /** * Tests that a second channel with the same group name can be * created and connected once the first channel is disconnected. * @throws Exception */ public void testSimpleReCreation() throws Exception { a=createSharedChannel(SINGLETON_1); a.setReceiver(new MyReceiver("A")); a.connect("A"); a.disconnect(); b=createSharedChannel(SINGLETON_1); b.setReceiver(new MyReceiver("A'")); b.connect("A"); } /** * Tests that a second channel with the same group name can be * created and connected once the first channel is disconnected even * if 3rd channel with a different group name is still using the shared * transport. * @throws Exception */ public void testCreationFollowedByDeletion() throws Exception { a=createSharedChannel(SINGLETON_1); a.setReceiver(new MyReceiver("A")); a.connect("A"); b=createSharedChannel(SINGLETON_1); b.setReceiver(new MyReceiver("B")); b.connect("B"); b.close(); a.close(); } public void test2ChannelsCreationFollowedByDeletion() throws Exception { a=createSharedChannel(SINGLETON_1); a.setReceiver(new MyReceiver("A")); a.connect("A"); b=createSharedChannel(SINGLETON_2); b.setReceiver(new MyReceiver("B")); b.connect("A"); c=createSharedChannel(SINGLETON_2); c.setReceiver(new MyReceiver("C")); c.connect("B"); c.send(null, null, "hello world from C"); } public void testReCreationWithSurvivingChannel() throws Exception { // Create 2 channels sharing a transport System.out.println("-- creating A"); a=createSharedChannel(SINGLETON_1); a.setReceiver(new MyReceiver("A")); a.connect("A"); System.out.println("-- creating B"); b=createSharedChannel(SINGLETON_1); b.setReceiver(new MyReceiver("B")); b.connect("B"); System.out.println("-- disconnecting A"); a.disconnect(); // a is disconnected so we should be able to create a new channel with group "A" System.out.println("-- creating A'"); c=createSharedChannel(SINGLETON_1); c.setReceiver(new MyReceiver("A'")); c.connect("A"); } /** * Tests http://jira.jboss.com/jira/browse/JGRP-737 * @throws Exception */ public void testShutdownOfTimer() throws Exception { a=createSharedChannel(SINGLETON_1); b=createSharedChannel(SINGLETON_1); a.connect("x"); b.connect("y"); TimeScheduler timer1=a.getProtocolStack().getTransport().getTimer(); TimeScheduler timer2=b.getProtocolStack().getTransport().getTimer(); assert timer1 == timer2; assert !timer1.isShutdown(); assert !timer2.isShutdown(); Util.sleep(500); b.close(); assert !timer2.isShutdown(); assert !timer1.isShutdown(); a.close(); // now, reference counting reaches 0, so the timer thread pool is stopped assert timer2.isShutdown(); assert timer1.isShutdown(); } /** Create channels A, B and C. Close A. This will close the timer and transports threads (!), so B will * not be able to send messages anymore, so C will not receive any messages * Tests http://jira.jboss.com/jira/browse/JGRP-737 */ public void testSendingOfMessagesAfterChannelClose() throws ChannelException { MyReceiver rec_a=new MyReceiver("A"), rec_b=new MyReceiver("B"), rec_c=new MyReceiver("C"); System.out.println("-- creating A"); a=createSharedChannel(SINGLETON_1); a.setReceiver(rec_a); a.connect("A"); System.out.println("-- creating B"); b=createSharedChannel(SINGLETON_1); b.setReceiver(rec_b); b.connect("B"); System.out.println("-- creating C"); c=createSharedChannel(SINGLETON_2); c.setReceiver(rec_c); c.connect("B"); b.send(null, null, "first"); Util.sleep(500); // msg delivery is asynchronous, so give members some time to receive the msg (incl retransmission) assertSize(1, rec_b, rec_c); assertSize(0, rec_a); a.close(); b.send(null, null, "second"); Util.sleep(500); assertSize(0, rec_a); assertSize(2, rec_b, rec_c); } /** * Use a CountDownLatch to concurrently connect 3 channels; confirms * the channels connect * * @throws ChannelException * @throws InterruptedException */ public void testConcurrentCreation() throws ChannelException, InterruptedException { a=createSharedChannel(SINGLETON_1); r1=new MyReceiver("a"); a.setReceiver(r1); b=createSharedChannel(SINGLETON_1); r2=new MyReceiver("b"); b.setReceiver(r2); c=createSharedChannel(SINGLETON_1); r3=new MyReceiver("c"); c.setReceiver(r3); CountDownLatch startLatch = new CountDownLatch(1); CountDownLatch finishLatch = new CountDownLatch(3); ConnectTask connectA = new ConnectTask(a, "a", startLatch, finishLatch); Thread threadA = new Thread(connectA); threadA.setDaemon(true); threadA.start(); ConnectTask connectB = new ConnectTask(b, "b", startLatch, finishLatch); Thread threadB = new Thread(connectB); threadB.setDaemon(true); threadB.start(); ConnectTask connectC = new ConnectTask(c, "c", startLatch, finishLatch); Thread threadC = new Thread(connectC); threadC.setDaemon(true); threadC.start(); startLatch.countDown(); try { boolean finished = finishLatch.await(20, TimeUnit.SECONDS); if (connectA.exception != null) { AssertJUnit.fail("connectA threw exception " + connectA.exception); } if (connectB.exception != null) { AssertJUnit.fail("connectB threw exception " + connectB.exception); } if (connectC.exception != null) { AssertJUnit.fail("connectC threw exception " + connectC.exception); } if (!finished) { if (threadA.isAlive()) AssertJUnit.fail("threadA did not finish"); if (threadB.isAlive()) AssertJUnit.fail("threadB did not finish"); if (threadC.isAlive()) AssertJUnit.fail("threadC did not finish"); } } finally { if (threadA.isAlive()) threadA.interrupt(); if (threadB.isAlive()) threadB.interrupt(); if (threadC.isAlive()) threadC.interrupt(); } } private static void assertSize(int expected, MyReceiver... receivers) { for(MyReceiver recv: receivers) { assertEquals(expected, recv.size()); } } private JChannel createSharedChannel(String singleton_name) throws ChannelException { ProtocolStackConfigurator config=ConfiguratorFactory.getStackConfigurator(channel_conf); List protocols=config.getProtocolStack(); ProtocolConfiguration transport=protocols.get(0); transport.getProperties().put(Global.SINGLETON_NAME, singleton_name); return new JChannel(config); } protected static void makeUnique(Channel channel, int num) throws Exception { ProtocolStack stack=channel.getProtocolStack(); TP transport=stack.getTransport(); InetAddress bind_addr=transport.getBindAddressAsInetAddress(); if(transport instanceof UDP) { String mcast_addr=ResourceManager.getNextMulticastAddress(); short mcast_port=ResourceManager.getNextMulticastPort(bind_addr); ((UDP)transport).setMulticastAddress(InetAddress.getByName(mcast_addr)); ((UDP)transport).setMulticastPort(mcast_port); } else if(transport instanceof BasicTCP) { List ports=ResourceManager.getNextTcpPorts(bind_addr, num); transport.setBindPort(ports.get(0)); transport.setPortRange(num); Protocol ping=stack.findProtocol(TCPPING.class); if(ping == null) throw new IllegalStateException("TCP stack must consist of TCP:TCPPING - other config are not supported"); List initial_hosts=new LinkedList(); for(short port: ports) { initial_hosts.add(bind_addr + "[" + port + "]"); } String tmp=Util.printListWithDelimiter(initial_hosts, ","); List init_hosts = Util.parseCommaDelimitedHosts(tmp, 1) ; ((TCPPING)ping).setInitialHosts(init_hosts) ; } else { throw new IllegalStateException("Only UDP and TCP are supported as transport protocols"); } } private static class MyReceiver extends ReceiverAdapter { final List list=new LinkedList(); final String name; private MyReceiver(String name) { this.name=name; } public List getList() { return list; } public int size() { return list.size(); } public void clear() { list.clear(); } public void receive(Message msg) { System.out.println("[" + name + "]: received message from " + msg.getSrc() + ": " + msg.getObject()); list.add(msg); } public void viewAccepted(View new_view) { StringBuilder sb=new StringBuilder(); sb.append("[" + name + "]: view = " + new_view); System.out.println(sb); } public String toString() { return super.toString() + " (size=" + list.size() + ")"; } } private static class ConnectTask implements Runnable { private final Channel channel; private final String clusterName; private final CountDownLatch startLatch; private final CountDownLatch finishLatch; private Exception exception; ConnectTask(Channel channel, String clusterName, CountDownLatch startLatch, CountDownLatch finishLatch) { this.channel = channel; this.clusterName = clusterName; this.startLatch = startLatch; this.finishLatch = finishLatch; } public void run() { try { startLatch.await(); channel.connect(clusterName); } catch (Exception e) { e.printStackTrace(System.out); this.exception = e; } finally { finishLatch.countDown(); } } } } libjgroups-java-2.12.2.Final.orig/tests/junit/org/jgroups/tests/StateTransferTest.java0000644000175000017500000002173711647260573030776 0ustar moellermoellerpackage org.jgroups.tests; import org.jgroups.*; import org.jgroups.util.Util; import org.testng.annotations.Test; import java.io.*; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; /** * Tests correct state transfer while other members continue sending messages to the group * @author Bela Ban */ @Test(groups=Global.STACK_DEPENDENT,sequential=true) public class StateTransferTest extends ChannelTestBase { static final int MSG_SEND_COUNT=5000; static final String[] names= { "A", "B", "C", "D"}; static final int APP_COUNT=names.length; public void testStateTransferFromSelfWithRegularChannel() throws Exception { Channel ch=createChannel(true); ch.connect("StateTransferTest"); try { Address self=ch.getAddress(); assert self != null; boolean rc=ch.getState(self, 20000); assert !rc : "getState() on self should return false"; } finally { ch.close(); } } public void testStateTransferWhileSending() throws Exception { StateTransferApplication[] apps=new StateTransferApplication[APP_COUNT]; try { Semaphore semaphore=new Semaphore(APP_COUNT); semaphore.acquire(APP_COUNT); int from=0, to=MSG_SEND_COUNT; for(int i=0;i < apps.length;i++) { if(i == 0) apps[i]=new StateTransferApplication(semaphore, names[i], from, to); else apps[i]=new StateTransferApplication((JChannel)apps[0].getChannel(), semaphore, names[i], from, to); from+=MSG_SEND_COUNT; to+=MSG_SEND_COUNT; } for(int i=0;i < apps.length;i++) { StateTransferApplication app=apps[i]; app.start(); semaphore.release(); //avoid merge Util.sleep(3000); } // Make sure everyone is in sync Channel[] tmp=new Channel[apps.length]; for(int i=0; i < apps.length; i++) tmp[i]=apps[i].getChannel(); Util.blockUntilViewsReceived(60000, 1000, tmp); // Reacquire the semaphore tickets; when we have them all // we know the threads are done boolean acquired=semaphore.tryAcquire(apps.length, 30, TimeUnit.SECONDS); if(!acquired) { log.warn("Most likely a bug, analyse the stack below:"); log.warn(Util.dumpThreads()); } // Sleep to ensure async messages arrive System.out.println("Waiting for all channels to have received the " + MSG_SEND_COUNT * APP_COUNT + " messages:"); long end_time=System.currentTimeMillis() + 40000L; while(System.currentTimeMillis() < end_time) { boolean terminate=true; for(StateTransferApplication app: apps) { Map map=app.getMap(); if(map.size() != MSG_SEND_COUNT * APP_COUNT) { terminate=false; break; } } if(terminate) break; else Util.sleep(500); } // have we received all and the correct messages? System.out.println("++++++++++++++++++++++++++++++++++++++"); for(int i=0;i < apps.length;i++) { StateTransferApplication w=apps[i]; Map m=w.getMap(); log.info("map has " + m.size() + " elements"); assert m.size() == MSG_SEND_COUNT * APP_COUNT; } System.out.println("++++++++++++++++++++++++++++++++++++++"); Set keys=apps[0].getMap().keySet(); for(int i=0;i < apps.length;i++) { StateTransferApplication app=apps[i]; Map m=app.getMap(); Set s=m.keySet(); assert keys.equals(s); } } finally { for(StateTransferApplication app: apps) app.getChannel().setReceiver(null); for(StateTransferApplication app: apps) app.cleanup(); } } protected class StateTransferApplication extends PushChannelApplicationWithSemaphore { private final Map map=new HashMap(MSG_SEND_COUNT * APP_COUNT); private int from, to; public StateTransferApplication(Semaphore semaphore, String name, int from, int to) throws Exception { super(name, semaphore); this.from=from; this.to=to; } public StateTransferApplication(JChannel copySource,Semaphore semaphore, String name, int from, int to) throws Exception { super(copySource,name, semaphore); this.from=from; this.to=to; } public Map getMap() { synchronized(map) { return Collections.unmodifiableMap(map); } } public void receive(Message msg) { Object[] data=(Object[])msg.getObject(); int num_received=0; boolean changed=false; synchronized(map) { int tmp_size=map.size(); map.put(data[0], data[1]); num_received=map.size(); changed=tmp_size != num_received; } if(changed && num_received % 1000 == 0) log.info(channel.getAddress() + ": received " + num_received); // are we done? if(num_received >= MSG_SEND_COUNT * APP_COUNT) semaphore.release(); } public byte[] getState() { synchronized(map) { try { return Util.objectToByteBuffer(map); } catch(Exception e) { e.printStackTrace(); } } return null; } @SuppressWarnings("unchecked") public void setState(byte[] state) { synchronized(map) { try { Map tmp=(Map)Util.objectFromByteBuffer(state); map.putAll(tmp); log.info(channel.getAddress() + ": received state, map has " + map.size() + " elements"); } catch(Exception e) { e.printStackTrace(); } } } public void getState(OutputStream ostream) { synchronized(map) { try { ObjectOutputStream out=new ObjectOutputStream(ostream); out.writeObject(map); out.close(); } catch(IOException e) { e.printStackTrace(); } } } @SuppressWarnings("unchecked") public void setState(InputStream istream) { synchronized(map) { try { ObjectInputStream in=new ObjectInputStream(istream); Map tmp=(Map)in.readObject(); Util.close(in); map.putAll(tmp); log.info(channel.getAddress() + ": received state, map has " + map.size() + " elements"); } catch(Exception e) { e.printStackTrace(); } } } public void run() { boolean acquired=false; try { acquired=semaphore.tryAcquire(60000L, TimeUnit.MILLISECONDS); if(!acquired) { throw new Exception(channel.getAddress() + " cannot acquire semaphore"); } useChannel(); } catch(Exception e) { log.error(channel.getAddress() + ": " + e.getLocalizedMessage(), e); exception=e; } } protected void useChannel() throws Exception { System.out.println(channel.getName() + ": connecting and fetching the state"); channel.connect("StateTransferTest", null, null, 30000); System.out.println(channel.getName() + ": state transfer is done"); Object[] data=new Object[2]; for(int i=from; i < to; i++) { data[0]=new Integer(i); data[1]="Value #" + i; try { channel.send(null, null, data); if(i % 100 == 0) Util.sleep(50); if(i % 1000 == 0) log.info(channel.getAddress() + ": sent " + i); } catch(Exception e) { e.printStackTrace(); break; } } } } }libjgroups-java-2.12.2.Final.orig/tests/junit/org/jgroups/tests/TCPGOSSIP_Test.java0000644000175000017500000001351411647260573027715 0ustar moellermoellerpackage org.jgroups.tests; import org.jgroups.Global; import org.jgroups.JChannel; import org.jgroups.View; import org.jgroups.protocols.TCPGOSSIP; import org.jgroups.stack.GossipRouter; import org.jgroups.stack.Protocol; import org.jgroups.util.StackType; import org.jgroups.util.Util; import org.testng.annotations.AfterClass; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; /** * Tests TCPGOSSIP protocol * * @author Vladimir Blagojevic * **/ @Test(groups = { Global.STACK_INDEPENDENT, Global.GOSSIP_ROUTER }, sequential = true) public class TCPGOSSIP_Test extends ChannelTestBase { private JChannel channel, coordinator; private final static String GROUP = "TCPGOSSIP_Test"; private GossipRouter gossipRouter; private static final String props = "tcpgossip.xml"; @BeforeClass void startRouter() throws Exception { String bind_addr = getRouterBindAddress(); gossipRouter = new GossipRouter(12001, bind_addr); gossipRouter.start(); } private String getRouterBindAddress() { String bind_addr = Util.getProperty(Global.BIND_ADDR); if (bind_addr == null) { StackType type = Util.getIpStackType(); if (type == StackType.IPv6) bind_addr = "::1"; else bind_addr = "127.0.0.1"; } return bind_addr; } @AfterClass(alwaysRun = true) void stopRouter() throws Exception { gossipRouter.stop(); } @AfterMethod(alwaysRun = true) void tearDown() throws Exception { Util.close(channel, coordinator); } /** * Tests connect-disconnect-connect sequence for a group with two members (using default * configuration). **/ public void testDisconnectConnectTwo() throws Exception { coordinator = new JChannel(props); channel = new JChannel(props); coordinator.connect(GROUP); channel.connect("DisconnectTest.testgroup-1"); channel.disconnect(); channel.connect(GROUP); View view = channel.getView(); assert view.size() == 2; assert view.containsMember(channel.getAddress()); assert view.containsMember(coordinator.getAddress()); } public void testAddInitialHosts() throws Exception { coordinator = new JChannel(props); channel = new JChannel(props); coordinator.connect(GROUP); channel.connect(GROUP); TCPGOSSIP p = (TCPGOSSIP) channel.getProtocolStack().findProtocol(TCPGOSSIP.class); String bind_addr = getRouterBindAddress(); assert p.removeInitialHost(bind_addr, 12001); p.addInitialHost(bind_addr, 12001); View view = channel.getView(); assert view.size() == 2; assert view.containsMember(channel.getAddress()); assert view.containsMember(coordinator.getAddress()); } public void testConnectThree() throws Exception { JChannel third = null; try { coordinator = new JChannel(props); channel = new JChannel(props); coordinator.connect(GROUP); channel.connect(GROUP); third = new JChannel(props); third.connect(GROUP); View view = channel.getView(); assert channel.getView().size() == 3; assert third.getView().size() == 3; assert view.containsMember(channel.getAddress()); assert view.containsMember(coordinator.getAddress()); } finally { Util.close(third); } } public void testConnectThreeChannelsWithGRDown() throws Exception { JChannel third = null; try { coordinator = new JChannel(props); channel = new JChannel(props); coordinator.connect("testConnectThreeChannelsWithGRDown"); channel.connect("testConnectThreeChannelsWithGRDown"); // kill router gossipRouter.stop(); // cannot discover others since GR is down third = new JChannel(props); third.connect("testConnectThreeChannelsWithGRDown"); // restart and.... gossipRouter.start(); Util.blockUntilViewsReceived(60000, 500, coordinator, channel, third); // confirm they found each other View view = channel.getView(); assert channel.getView().size() == 3; assert third.getView().size() == 3; assert view.containsMember(channel.getLocalAddress()); assert view.containsMember(coordinator.getLocalAddress()); } finally { Util.close(third); } } public void testConnectThreeChannelsWithGRAlreadyDown() throws Exception { JChannel third = null; try { coordinator = new JChannel(props); channel = new JChannel(props); // kill router gossipRouter.stop(); // cannot discover others since GR is down coordinator.connect("testConnectThreeChannelsWithGRAlreadyDown"); channel.connect("testConnectThreeChannelsWithGRAlreadyDown"); third = new JChannel(props); third.connect("testConnectThreeChannelsWithGRAlreadyDown"); // restart and.... gossipRouter.start(); Util.blockUntilViewsReceived(60000, 500, coordinator, channel, third); // confirm they found each other View view = channel.getView(); assert channel.getView().size() == 3; assert third.getView().size() == 3; assert view.containsMember(channel.getLocalAddress()); assert view.containsMember(coordinator.getLocalAddress()); } finally { Util.close(third); } } }libjgroups-java-2.12.2.Final.orig/tests/junit/org/jgroups/tests/TUNNELDeadLockTest.java0000644000175000017500000001124711647260573030600 0ustar moellermoellerpackage org.jgroups.tests; import org.jgroups.Global; import org.jgroups.JChannel; import org.jgroups.Message; import org.jgroups.ReceiverAdapter; import org.jgroups.stack.GossipRouter; import org.jgroups.util.Promise; import org.jgroups.util.StackType; import org.jgroups.util.Util; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; /** * Test designed to make sure the TUNNEL doesn't lock the client and the GossipRouter * under heavy load. * * @author Ovidiu Feodorov * @see TUNNELDeadLockTest#testStress */ @Test(groups={Global.STACK_INDEPENDENT, Global.GOSSIP_ROUTER},sequential=true) public class TUNNELDeadLockTest extends ChannelTestBase { private JChannel channel; private Promise promise; private int receivedCnt; // the total number of the messages pumped down the channel private int msgCount=20000; // the message payload size (in bytes); private int payloadSize=32; // the time (in ms) the main thread waits for all the messages to arrive, // before declaring the test failed. private int mainTimeout=60000; GossipRouter gossipRouter; @BeforeMethod void setUp() throws Exception { String bind_addr=Util.getProperty(Global.BIND_ADDR); if(bind_addr == null) { StackType type=Util.getIpStackType(); if(type == StackType.IPv6) bind_addr="::1"; else bind_addr="127.0.0.1"; } promise=new Promise(); gossipRouter=new GossipRouter(GossipRouter.PORT,bind_addr); gossipRouter.start(); } @AfterMethod(alwaysRun=true) void tearDown() throws Exception { // I prefer to close down the channel inside the test itself, for the // reason that the channel might be brought in an uncloseable state by // the test. // TO_DO: no elegant way to stop the Router threads and clean-up // resources. Use the Router administrative interface, when available. channel.close(); promise.reset(); promise=null; gossipRouter.stop(); System.out.println("Router stopped"); } /** * Pushes messages down the channel as fast as possible. Sometimes this * manages to bring the channel and the Router into deadlock. On the * machine I run it usually happens after 700 - 1000 messages and I * suspect that this number it is related to the socket buffer size. * (the comments are written when I didn't solve the bug yet).
                    *

                    * The number of messages sent can be controlled with msgCount. * The time (in ms) the main threads wait for the all messages to come can * be controlled with mainTimeout. If this time passes and the test * doesn't see all the messages, it declares itself failed. */ @Test public void testStress() throws Exception { channel=new JChannel("tunnel.xml"); channel.connect("agroup"); channel.setReceiver(new ReceiverAdapter() { @Override public void receive(Message msg) { receivedCnt++; if(receivedCnt % 2000 == 0) System.out.println("-- received " + receivedCnt); if(receivedCnt == msgCount) { // let the main thread know I got all msgs promise.setResult(Boolean.TRUE); } } }); // stress send messages - the sender thread new Thread(new Runnable() { public void run() { try { for(int i=0; i < msgCount; i++) { channel.send(null, null, new byte[payloadSize]); if(i % 2000 == 0) System.out.println("-- sent " + i); } } catch(Exception e) { System.err.println("Error sending data over ..."); e.printStackTrace(); } } }).start(); // wait for all the messages to come; if I don't see all of them in // mainTimeout ms, I fail the test Boolean result=promise.getResult(mainTimeout); if(result == null) { String msg= "The channel has failed to send/receive " + msgCount + " messages " + "possibly because of the channel deadlock or too short " + "timeout (currently " + mainTimeout + " ms). " + receivedCnt + " messages received so far."; assert false : msg; } } } libjgroups-java-2.12.2.Final.orig/tests/junit/org/jgroups/tests/TUNNEL_Test.java0000644000175000017500000002273311647260573027352 0ustar moellermoellerpackage org.jgroups.tests; import org.jgroups.Global; import org.jgroups.JChannel; import org.jgroups.Message; import org.jgroups.ReceiverAdapter; import org.jgroups.View; import org.jgroups.protocols.MERGE2; import org.jgroups.protocols.FD; import org.jgroups.protocols.FD_ALL; import org.jgroups.protocols.pbcast.GMS; import org.jgroups.stack.GossipRouter; import org.jgroups.stack.ProtocolStack; import org.jgroups.util.Promise; import org.jgroups.util.StackType; import org.jgroups.util.Util; import org.testng.annotations.AfterClass; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; /** * Ensures that a disconnected channel reconnects correctly, for different * stack configurations. * * @author Ovidiu Feodorov * @author Bela Ban belaban@yahoo.com **/ @Test(groups={Global.STACK_INDEPENDENT, Global.GOSSIP_ROUTER},sequential=true) public class TUNNEL_Test extends ChannelTestBase{ private JChannel channel, coordinator; private final static String GROUP="TUNNEL_Test"; private GossipRouter gossipRouter; private static final String props="tunnel.xml"; @BeforeClass void startRouter() throws Exception { String bind_addr=Util.getProperty(Global.BIND_ADDR); if(bind_addr == null) { StackType type=Util.getIpStackType(); if(type == StackType.IPv6) bind_addr="::1"; else bind_addr="127.0.0.1"; } gossipRouter=new GossipRouter(12001, bind_addr); gossipRouter.start(); } @AfterClass(alwaysRun=true) void stopRouter() throws Exception { gossipRouter.stop(); } @AfterMethod(alwaysRun=true) void tearDown() throws Exception { Util.close(channel, coordinator); } /** * Tests if the channel has a null local address after disconnect (using TUNNEL). **/ public void testNullLocalAddress_TUNNEL() throws Exception { channel = new JChannel(props); setProps(channel); channel.connect(GROUP); assert channel.getAddress() != null; channel.disconnect(); assert channel.getAddress() == null; } /** * Tests connect-disconnect-connect sequence for a group with one member * (using default configuration). **/ public void testDisconnectConnectOne_Default() throws Exception { channel=new JChannel(props); setProps(channel); channel.connect("DisconnectTest.testgroup-1"); channel.disconnect(); channel.connect("DisconnectTest.testgroup-2"); View view=channel.getView(); assert view.size() == 1; assert view.containsMember(channel.getAddress()); } /** * Tests connect-disconnect-connect sequence for a group with two members * (using default configuration). **/ public void testDisconnectConnectTwo_Default() throws Exception { coordinator=new JChannel(props); setProps(coordinator); channel=new JChannel(props); setProps(channel); coordinator.connect(GROUP); channel.connect("DisconnectTest.testgroup-1"); channel.disconnect(); channel.connect(GROUP); View view=channel.getView(); assert view.size() == 2; assert view.containsMember(channel.getAddress()); assert view.containsMember(coordinator.getAddress()); } /** * Tests connect-disconnect-connect-send sequence for a group with two * members, using the default stack configuration. Assumes that default * configuration includes pbcast.NAKACK. Test case introduced before fixing * pbcast.NAKACK bug, which used to leave pbcast.NAKACK in a broken state * after DISCONNECT. Because of this problem, the channel couldn't be used * to multicast messages. **/ public void testDisconnectConnectSendTwo_Default() throws Exception { final Promise msgPromise=new Promise(); coordinator=new JChannel(props); setProps(coordinator); coordinator.connect(GROUP); coordinator.setReceiver(new PromisedMessageListener(msgPromise)); channel=new JChannel(props); setProps(channel); channel.connect("DisconnectTest.testgroup-1"); channel.disconnect(); channel.connect(GROUP); channel.send(new Message(null, null, "payload")); Message msg=msgPromise.getResult(20000); assert msg != null; assert "payload".equals(msg.getObject()); } /** * Tests connect-disconnect-connect sequence for a group with one member * (using TUNNEL). **/ public void testDisconnectConnectOne_TUNNEL() throws Exception { channel=new JChannel(props); setProps(channel); channel.connect("DisconnectTest.testgroup-1"); channel.disconnect(); channel.connect("DisconnectTest.testgroup-2"); View view=channel.getView(); assert view.size() == 1; assert view.containsMember(channel.getAddress()); } public void testFailureDetection() throws Exception { coordinator=new JChannel(props); coordinator.setName("coord"); setProps(coordinator); coordinator.connect(GROUP); channel=new JChannel(props); channel.setName("participant"); setProps(channel); channel.connect(GROUP); System.out.println("shutting down the participant channel"); Util.shutdown(channel); GMS coord_gms=(GMS)coordinator.getProtocolStack().findProtocol(GMS.class); if(coord_gms != null) coord_gms.setLevel("trace"); View view; long end_time=System.currentTimeMillis() + 10000; while(System.currentTimeMillis() < end_time) { view=coordinator.getView(); if(view.size() == 1) break; Util.sleep(500); } view=coordinator.getView(); assert view.size() == 1 : "coordinator's view is " + view + ", but we expected a view of 1 member"; if(coord_gms != null) coord_gms.setLevel("warn"); } public void testConnectThree() throws Exception { coordinator=new JChannel(props); setProps(coordinator); channel=new JChannel(props); setProps(channel); coordinator.connect(GROUP); channel.connect(GROUP); JChannel third = new JChannel (props); third.connect(GROUP); View view=channel.getView(); assert channel.getView().size() == 3; assert third.getView().size() == 3; assert view.containsMember(channel.getAddress()); assert view.containsMember(coordinator.getAddress()); Util.close(third); } /** * Tests connect-disconnect-connect sequence for a group with two members * (using TUNNEL). **/ public void testDisconnectConnectTwo_TUNNEL() throws Exception { coordinator=new JChannel(props); setProps(coordinator); coordinator.connect(GROUP); channel=new JChannel(props); setProps(channel); channel.connect("DisconnectTest.testgroup-1"); channel.disconnect(); channel.connect(GROUP); Thread.sleep(1000); View view=channel.getView(); assert view.size() == 2; assert view.containsMember(channel.getAddress()); assert view.containsMember(coordinator.getAddress()); } /** * Tests connect-disconnect-connect-send sequence for a group with two * members, using TUNNEL. Test case introduced before fixing pbcast.NAKACK * bug, which used to leave pbcast.NAKACK in a broken state after * DISCONNECT. Because of this problem, the channel couldn't be used to * multicast messages. **/ public void testDisconnectConnectSendTwo_TUNNEL() throws Exception { final Promise msgPromise=new Promise(); coordinator=new JChannel(props); setProps(coordinator); coordinator.connect(GROUP); coordinator.setReceiver(new PromisedMessageListener(msgPromise)); channel=new JChannel(props); setProps(channel); channel.connect("DisconnectTest.testgroup-1"); channel.disconnect(); channel.connect(GROUP); channel.send(new Message(null, null, "payload")); Message msg=msgPromise.getResult(20000); assert msg != null; assert "payload".equals(msg.getObject()); } private static void setProps(JChannel channel) { ProtocolStack stack=channel.getProtocolStack(); MERGE2 merge=(MERGE2)stack.findProtocol(MERGE2.class); if(merge != null) { merge.setMinInterval(1000); merge.setMaxInterval(3000); } FD fd=(FD)stack.findProtocol(FD.class); if(fd != null) { fd.setTimeout(1000); fd.setMaxTries(2); } FD_ALL fd_all=(FD_ALL)stack.findProtocol(FD_ALL.class); if(fd_all != null) { fd_all.setTimeout(2000); fd_all.setInterval(600); } } private static class PromisedMessageListener extends ReceiverAdapter { private final Promise promise; public PromisedMessageListener(Promise promise) { this.promise=promise; } public void receive(Message msg) { promise.setResult(msg); } } }libjgroups-java-2.12.2.Final.orig/tests/junit/org/jgroups/tests/TUNNEL_Test2.java0000644000175000017500000002604611647260573027435 0ustar moellermoellerpackage org.jgroups.tests; import org.jgroups.Global; import org.jgroups.JChannel; import org.jgroups.Message; import org.jgroups.ReceiverAdapter; import org.jgroups.View; import org.jgroups.protocols.TUNNEL; import org.jgroups.stack.GossipRouter; import org.jgroups.stack.ProtocolStack; import org.jgroups.util.Promise; import org.jgroups.util.Util; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; /** * Ensures that a disconnected channel reconnects correctly, for different stack * configurations. * * **/ @Test(groups = {Global.STACK_INDEPENDENT,"known-failures",Global.GOSSIP_ROUTER}, sequential = true) public class TUNNEL_Test2 extends ChannelTestBase { private JChannel channel, coordinator; private GossipRouter gr1, gr2; private static final String props ="tunnel.xml"; private static String bindAddress = "127.0.0.1"; static { try { bindAddress = Util.getBindAddress(null).getHostAddress(); } catch (Exception e) { } } @BeforeMethod void startRouter() throws Exception { gr1 = new GossipRouter(12003,bindAddress); gr1.start(); gr2 = new GossipRouter(12004,bindAddress); gr2.start(); } @AfterMethod void tearDown() throws Exception { Util.close(channel, coordinator); Util.sleep(1000); gr1.stop(); gr2.stop(); } private void modifyChannel(JChannel... channels) throws Exception { for (JChannel c : channels) { ProtocolStack stack = c.getProtocolStack(); TUNNEL t = (TUNNEL) stack.getBottomProtocol(); String s = bindAddress + "[" + gr1.getPort() + "],"; s+=bindAddress+"[" + gr2.getPort() + "]"; t.setGossipRouterHosts(s); t.init(); } } public void testSimpleConnect() throws Exception { channel = new JChannel(props); modifyChannel(channel); channel.connect("testSimpleConnect"); assert channel.getLocalAddress() != null; assert channel.getView().size() == 1; channel.disconnect(); assert channel.getLocalAddress() == null; assert channel.getView() == null; } /** * Tests connect with two members * **/ public void testConnectTwoChannels() throws Exception { coordinator = new JChannel(props); channel = new JChannel(props); modifyChannel(channel,coordinator); coordinator.connect("testConnectTwoChannels"); channel.connect("testConnectTwoChannels"); View view = channel.getView(); assert view.size() == 2; assert view.containsMember(channel.getLocalAddress()); assert view.containsMember(coordinator.getLocalAddress()); channel.disconnect(); Util.sleep(1000); view = coordinator.getView(); assert view.size() == 1; assert view.containsMember(coordinator.getLocalAddress()); } /** * Tests connect with two members but when both GR fail and restart * **/ public void testConnectTwoChannelsBothGRDownReconnect() throws Exception { coordinator = new JChannel(props); channel = new JChannel(props); modifyChannel(channel,coordinator); coordinator.connect("testConnectTwoChannelsBothGRDownReconnect"); channel.connect("testConnectTwoChannelsBothGRDownReconnect"); Util.sleep(1000); gr1.stop(); gr2.stop(); // give time to reconnect Util.sleep(3000); gr1.start(); gr2.start(); // give time to reconnect Util.sleep(3000); View view = coordinator.getView(); assert view.size() == 2; assert view.containsMember(coordinator.getLocalAddress()); assert view.containsMember(channel.getLocalAddress()); view = channel.getView(); assert view.size() == 2; assert view.containsMember(coordinator.getLocalAddress()); assert view.containsMember(channel.getLocalAddress()); } public void testConnectThreeChannelsWithGRDown() throws Exception { JChannel third = null; coordinator = new JChannel(props); channel = new JChannel(props); modifyChannel(channel,coordinator); coordinator.connect("testConnectThreeChannelsWithGRDown"); channel.connect("testConnectThreeChannelsWithGRDown"); third = new JChannel(props); modifyChannel(third); third.connect("testConnectThreeChannelsWithGRDown"); Util.sleep(1000); View view = channel.getView(); assert channel.getView().size() == 3; assert third.getView().size() == 3; assert view.containsMember(channel.getLocalAddress()); assert view.containsMember(coordinator.getLocalAddress()); // kill router and recheck views gr2.stop(); Util.sleep(1000); view = channel.getView(); assert channel.getView().size() == 3; assert third.getView().size() == 3; assert third.getView().containsMember(channel.getLocalAddress()); assert third.getView().containsMember(coordinator.getLocalAddress()); } /** * **/ public void testConnectSendMessage() throws Exception { final Promise msgPromise = new Promise(); coordinator = new JChannel(props); modifyChannel(coordinator); coordinator.connect("testConnectSendMessage"); coordinator.setReceiver(new PromisedMessageListener(msgPromise)); channel = new JChannel(props); modifyChannel(channel); channel.connect("testConnectSendMessage"); channel.send(new Message(null, null, "payload")); Message msg = msgPromise.getResult(20000); assert msg != null; assert "payload".equals(msg.getObject()); } /** * **/ public void testConnectSendMessageSecondGRDown() throws Exception { final Promise msgPromise = new Promise(); coordinator = new JChannel(props); modifyChannel(coordinator); coordinator.connect("testConnectSendMessageSecondGRDown"); coordinator.setReceiver(new PromisedMessageListener(msgPromise)); channel = new JChannel(props); modifyChannel(channel); channel.connect("testConnectSendMessageSecondGRDown"); Util.sleep(1000); gr2.stop(); channel.send(new Message(null, null, "payload")); View view = coordinator.getView(); assert view.size() == 2; assert view.containsMember(coordinator.getLocalAddress()); assert view.containsMember(channel.getLocalAddress()); view = channel.getView(); assert view.size() == 2; assert view.containsMember(coordinator.getLocalAddress()); assert view.containsMember(channel.getLocalAddress()); Message msg = msgPromise.getResult(20000); assert msg != null; assert "payload".equals(msg.getObject()); } /** * **/ public void testConnectSendMessageBothGRDown() throws Exception { final Promise msgPromise = new Promise(); coordinator = new JChannel(props); modifyChannel(coordinator); coordinator.connect("testConnectSendMessageBothGRDown"); coordinator.setReceiver(new PromisedMessageListener(msgPromise)); channel = new JChannel(props); modifyChannel(channel); channel.connect("testConnectSendMessageBothGRDown"); Util.sleep(1000); gr1.stop(); gr2.stop(); // give time to reconnect Util.sleep(3000); gr1.start(); gr2.start(); // give time to reconnect Util.sleep(3000); channel.send(new Message(null, null, "payload")); View view = coordinator.getView(); assert view.size() == 2; assert view.containsMember(coordinator.getLocalAddress()); assert view.containsMember(channel.getLocalAddress()); view = channel.getView(); assert view.size() == 2; assert view.containsMember(coordinator.getLocalAddress()); assert view.containsMember(channel.getLocalAddress()); Message msg = msgPromise.getResult(20000); assert msg != null; assert "payload".equals(msg.getObject()); } /** * **/ public void testConnectSendMessageBothGRDownOnlyOneUp() throws Exception { final Promise msgPromise = new Promise(); coordinator = new JChannel(props); modifyChannel(coordinator); coordinator.connect("testConnectSendMessageBothGRDownOnlyOneUp"); coordinator.setReceiver(new PromisedMessageListener(msgPromise)); channel = new JChannel(props); modifyChannel(channel); channel.connect("testConnectSendMessageBothGRDownOnlyOneUp"); Util.sleep(1000); gr1.stop(); gr2.stop(); gr1.start(); // give time to reconnect Util.sleep(6000); channel.send(new Message(null, null, "payload")); View view = coordinator.getView(); assert view.size() == 2; assert view.containsMember(coordinator.getLocalAddress()); assert view.containsMember(channel.getLocalAddress()); view = channel.getView(); assert view.size() == 2; assert view.containsMember(coordinator.getLocalAddress()); assert view.containsMember(channel.getLocalAddress()); Message msg = msgPromise.getResult(20000); assert msg != null; assert "payload".equals(msg.getObject()); } public void testConnectSendMessageFirstGRDown() throws Exception { final Promise msgPromise = new Promise(); coordinator = new JChannel(props); modifyChannel(coordinator); coordinator.connect("testConnectSendMessageFirstGRDown"); coordinator.setReceiver(new PromisedMessageListener(msgPromise)); channel = new JChannel(props); modifyChannel(channel); channel.connect("testConnectSendMessageFirstGRDown"); Util.sleep(1000); gr1.stop(); channel.send(new Message(null, null, "payload")); View view = coordinator.getView(); assert view.size() == 2; assert view.containsMember(coordinator.getLocalAddress()); assert view.containsMember(channel.getLocalAddress()); view = channel.getView(); assert view.size() == 2; assert view.containsMember(coordinator.getLocalAddress()); assert view.containsMember(channel.getLocalAddress()); Message msg = msgPromise.getResult(20000); assert msg != null; assert "payload".equals(msg.getObject()); } private static class PromisedMessageListener extends ReceiverAdapter { private final Promise promise; public PromisedMessageListener(Promise promise) { this.promise = promise; } public void receive(Message msg) { promise.setResult(msg); } } } libjgroups-java-2.12.2.Final.orig/tests/junit/org/jgroups/tests/ThreadFactoryTest.java0000644000175000017500000000407411647260573030743 0ustar moellermoellerpackage org.jgroups.tests; import org.jgroups.util.DefaultThreadFactory; import org.jgroups.Global; import org.testng.annotations.Test; /** * @author Bela Ban */ @Test(groups=Global.FUNCTIONAL,sequential=true) public class ThreadFactoryTest { private DefaultThreadFactory factory; private static final String BASE="base"; private static final String ADDR="192.168.1.5:12345"; private static final String CLUSTER="MyCluster"; public void testNoNumbering() { factory=new DefaultThreadFactory(null, BASE, true, false); Thread thread=factory.newThread(new MyRunnable(), BASE); String name=thread.getName(); System.out.println("name = " + name); assert name.equals(BASE); } public void testNumbering() { factory=new DefaultThreadFactory(null, BASE, true, true); Thread thread=factory.newThread(new MyRunnable(), BASE); String name=thread.getName(); System.out.println("name = " + name); assert name.equals("base-1"); thread=factory.newThread(new MyRunnable(), BASE); name=thread.getName(); System.out.println("name = " + name); assert name.equals("base-2"); } public void testPatterns() { factory=new DefaultThreadFactory(null, BASE, true, false); factory.setAddress(ADDR); Thread thread=factory.newThread(new MyRunnable(), BASE); String name=thread.getName(); System.out.println("name = " + name); assert name.equals(BASE); factory.setPattern("l"); thread=factory.newThread(new MyRunnable(), BASE); name=thread.getName(); System.out.println("name = " + name); assert name.equals(BASE + "," + ADDR); factory.setPattern("cl"); factory.setClusterName(CLUSTER); thread=factory.newThread(new MyRunnable(), BASE); name=thread.getName(); System.out.println("name = " + name); assert name.equals(BASE + "," + CLUSTER + "," + ADDR); } static class MyRunnable implements Runnable { public void run() {} } } libjgroups-java-2.12.2.Final.orig/tests/junit/org/jgroups/tests/TransportThreadPoolTest.java0000644000175000017500000000575311647260573032167 0ustar moellermoellerpackage org.jgroups.tests; import org.jgroups.Global; import org.jgroups.JChannel; import org.jgroups.Message; import org.jgroups.ReceiverAdapter; import org.jgroups.protocols.TP; import org.jgroups.util.Util; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.LinkedList; import java.util.List; import java.util.Collection; import java.util.concurrent.*; /** * @author Bela Ban */ @Test(groups=Global.STACK_DEPENDENT,sequential=true) public class TransportThreadPoolTest extends ChannelTestBase { JChannel c1, c2; @BeforeMethod protected void setUp() throws Exception { c1=createChannel(true, 2); c2=createChannel(c1); } @AfterMethod protected void tearDown() throws Exception { Util.close(c2, c1); } @Test public void testThreadPoolReplacement() throws Exception { Receiver r1=new Receiver(), r2=new Receiver(); c1.setReceiver(r1); c2.setReceiver(r2); c1.connect("TransportThreadPoolTest"); c2.connect("TransportThreadPoolTest"); Util.blockUntilViewsReceived(5000, 500, c1, c2); assert c2.getView().size() == 2 : "view is " + c2.getView() + ", but should have had a size of 2"; TP transport=c1.getProtocolStack().getTransport(); ExecutorService thread_pool=Executors.newFixedThreadPool(2); transport.setDefaultThreadPool(thread_pool); transport=c2.getProtocolStack().getTransport(); thread_pool=Executors.newFixedThreadPool(2); transport.setDefaultThreadPool(thread_pool); c1.send(null, null, "hello world"); c2.send(null, null, "bela"); c1.send(null, null, "message 3"); c2.send(null, null, "message 4"); long start=System.currentTimeMillis(); r1.getLatch().await(3000, TimeUnit.MILLISECONDS); r2.getLatch().await(3000, TimeUnit.MILLISECONDS); long diff=System.currentTimeMillis() - start; System.out.println("messages c1: " + print(r1.getMsgs()) + "\nmessages c2: " + print(r2.getMsgs()) + "\ntook " + diff + " ms"); assert r1.getMsgs().size() == 4; assert r2.getMsgs().size() == 4; } private static String print(Collection msgs) { StringBuilder sb=new StringBuilder(); for(Message msg: msgs) { sb.append("\"" + msg.getObject() + "\"").append(" "); } return sb.toString(); } private static class Receiver extends ReceiverAdapter { Collection msgs=new ConcurrentLinkedQueue(); final CountDownLatch latch = new CountDownLatch(4); public Collection getMsgs() { return msgs; } public CountDownLatch getLatch(){ return latch; } public void receive(Message msg) { msgs.add(msg); latch.countDown(); } } } libjgroups-java-2.12.2.Final.orig/tests/junit/org/jgroups/tests/UNICAST_OOB_Test.java0000644000175000017500000000771711647260573030157 0ustar moellermoellerpackage org.jgroups.tests; import org.jgroups.Address; import org.jgroups.Global; import org.jgroups.JChannel; import org.jgroups.Message; import org.jgroups.ReceiverAdapter; import org.jgroups.protocols.DISCARD_PAYLOAD; import org.jgroups.protocols.UNICAST; import org.jgroups.protocols.UNICAST2; import org.jgroups.stack.ProtocolStack; import org.jgroups.stack.Protocol; import org.jgroups.util.Util; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.Collections; import java.util.LinkedList; import java.util.List; /** * Tests the UNICAST protocol for OOB msgs, tests http://jira.jboss.com/jira/browse/JGRP-377 * @author Bela Ban */ @Test(groups=Global.STACK_DEPENDENT,sequential=true) public class UNICAST_OOB_Test extends ChannelTestBase { JChannel c1, c2; @BeforeMethod void setUp() throws Exception { c1=createChannel(true, 2); c2=createChannel(c1); } @AfterMethod void tearDown() throws Exception { Util.close(c2, c1); } public void testRegularMessages() throws Exception { sendMessages(false); } public void testOutOfBandMessages() throws Exception { sendMessages(true); } /** * Check that 4 is received before 3 */ private void sendMessages(boolean oob) throws Exception { DISCARD_PAYLOAD prot1=new DISCARD_PAYLOAD(); MyReceiver receiver=new MyReceiver(); c2.setReceiver(receiver); // the first channel will discard the unicast messages with seqno #3 two times, the let them pass down ProtocolStack stack=c1.getProtocolStack(); Protocol neighbor=stack.findProtocol(UNICAST.class, UNICAST2.class); System.out.println("Found unicast protocol " + neighbor.getClass().getSimpleName()); stack.insertProtocolInStack(prot1, neighbor, ProtocolStack.BELOW); c1.connect("UNICAST_OOB_Test"); c2.connect("UNICAST_OOB_Test"); assert c2.getView().size() == 2 : "ch2.view is " + c2.getView(); Address dest=c2.getAddress(); for(int i=1; i <=5; i++) { Message msg=new Message(dest, null, new Long(i)); if(i == 4 && oob) msg.setFlag(Message.OOB); System.out.println("-- sending message #" + i); c1.send(msg); Util.sleep(100); } // wait until retransmission of seqno #3 happens, so that 4 and 5 are received as well long target_time=System.currentTimeMillis() + 5000; do { if(receiver.size() >= 5) break; Util.sleep(500); } while(target_time > System.currentTimeMillis()); List seqnos=receiver.getSeqnos(); System.out.println("sequence numbers: " + seqnos); if(!oob) { for(int i=0; i < 5; i++) assert seqnos.get(i) == i+1 : " seqno is " + seqnos.get(i) + ", but expected " + i+1; } else { // 4 needs to be received before 3. Reason: 4 is sent OOB, does *not* wait until 3 has been retransmitted ! int index_3=-1, index_4=-1; for(int i=0; i < 5; i++) { if(seqnos.get(i) == 3) index_3=i; if(seqnos.get(i) == 4) index_4=i; } assert index_4 < index_3 : "4 must come before 3 in list " + seqnos; } } public static class MyReceiver extends ReceiverAdapter { /** List of unicast sequence numbers */ List seqnos=Collections.synchronizedList(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 int size() {return seqnos.size();} } } libjgroups-java-2.12.2.Final.orig/tests/junit/org/jgroups/tests/UUIDCacheClearTest.java0000644000175000017500000000700511647260573030642 0ustar moellermoellerpackage org.jgroups.tests; import org.jgroups.*; import org.jgroups.util.Util; import org.testng.annotations.Test; import java.util.ArrayList; import java.util.List; /** * Test whether physical addresses are fetched correctly after the UUID-physical address cache has been cleared * @author Bela Ban */ @Test(groups=Global.STACK_DEPENDENT,sequential=true) public class UUIDCacheClearTest extends ChannelTestBase { @Test public void testCacheClear() throws Exception { JChannel c1=null, c2=null; Address c1_addr, c2_addr; MyReceiver r1=new MyReceiver(), r2=new MyReceiver(); try { c1=createChannel(true, 2); c1.setReceiver(r1); c1.connect("testCacheClear"); c2=createChannel(c1); c2.setReceiver(r2); c2.connect("testCacheClear"); assert c2.getView().size() == 2 : "view is " + c2.getView(); c1_addr=c1.getAddress(); c2_addr=c2.getAddress(); // send one unicast message from c1 to c2 and vice versa c1.send(c2_addr, null, "one"); c2.send(c1_addr, null, "one"); List c1_list=r1.getList(); List c2_list=r2.getList(); for(int i=0; i < 10; i++) { // poor man's way of waiting until we have 1 message in each receiver... :-) if(!c1_list.isEmpty() && !c2_list.isEmpty()) break; Util.sleep(500); } assert c1_list.size() == 1 && c2_list.size() == 1; // now clear the caches and send message "two" printCaches(c1, c2); System.out.println("clearing the caches"); clearCache(c1,c2); printCaches(c1, c2); r1.clear(); r2.clear(); // send one unicast message from c1 to c2 and vice versa c1.send(c2_addr, null, "two"); c2.send(c1_addr, null, "two"); for(int i=0; i < 10; i++) { // poor man's way of waiting until we have 1 message in each receiver... :-) if(!c1_list.isEmpty() && !c2_list.isEmpty()) break; Util.sleep(1000); } assert c1_list.size() == 1 && c2_list.size() == 1; Message msg_from_1=c2_list.get(0); Message msg_from_2=c1_list.get(0); assert msg_from_1.getSrc().equals(c1_addr); assert msg_from_1.getObject().equals("two"); assert msg_from_2.getSrc().equals(c2_addr); assert msg_from_2.getObject().equals("two"); } finally { Util.close(c2, c1); } } private static void clearCache(JChannel ... channels) { for(JChannel ch: channels) { ch.getProtocolStack().getTransport().clearLogicalAddressCache(); ch.down(new Event(Event.SET_LOCAL_ADDRESS, ch.getAddress())); } } private static void printCaches(JChannel ... channels) { System.out.println("chaches:\n"); for(JChannel ch: channels) { System.out.println(ch.getAddress() + ":\n" + ch.getProtocolStack().getTransport().printLogicalAddressCache()); } } private static class MyReceiver extends ReceiverAdapter { private final List msgs=new ArrayList(4); public void receive(Message msg) { msgs.add(msg); } public void clear() {msgs.clear();} public List getList() {return msgs;} } }libjgroups-java-2.12.2.Final.orig/tests/junit/org/jgroups/tests/UnicastEnableToTest.java0000644000175000017500000000715411647260573031226 0ustar moellermoellerpackage org.jgroups.tests; import org.jgroups.*; import org.jgroups.stack.Protocol; import org.jgroups.protocols.UNICAST; import org.jgroups.protocols.UNICAST2; import org.jgroups.util.UUID; import org.jgroups.util.Util; import org.jgroups.util.AgeOutCache; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.Collections; import java.util.LinkedList; import java.util.List; /** * Tests sending of unicasts to members not in the group (http://jira.jboss.com/jira/browse/JGRP-357) * @author Bela Ban */ @Test(groups=Global.STACK_DEPENDENT,sequential=true) public class UnicastEnableToTest extends ChannelTestBase { private JChannel c1=null, c2=null; private static final String GROUP="UnicastEnableToTest"; AgeOutCache cache; @BeforeMethod protected void setUp() throws Exception { c1=createChannel(true); c1.connect(GROUP); Protocol prot=c1.getProtocolStack().findProtocol(UNICAST.class, UNICAST2.class); if(prot instanceof UNICAST) cache=((UNICAST)prot).getAgeOutCache(); else if(prot instanceof UNICAST2) cache=((UNICAST2)prot).getAgeOutCache(); else throw new Exception("Neither UNICAST nor UNICAST2 are present in the stack"); if(cache != null) cache.setTimeout(1000); } @AfterMethod protected void tearDown() throws Exception { Util.close(c2, c1); } public void testUnicastMessageToUnknownMember() throws Exception { Address addr=UUID.randomUUID(); System.out.println("sending message to non-existing destination " + addr); c1.send(new Message(addr, null, "Hello world")); if(cache != null) { System.out.println("age out cache:\n" + cache); assert cache.size() == 1; } Util.sleep(1500); if(cache != null) { assert cache.size() == 0; } } public void testUnicastMessageToExistingMember() throws Exception { c2=createChannel(c1); c2.connect(GROUP); assert 2 == c2.getView().size() : " view=" + c2.getView(); MyReceiver receiver=new MyReceiver(); c2.setReceiver(receiver); Address dest=c2.getAddress(); c1.send(new Message(dest, null, "hello")); if(cache != null) { System.out.println("age out cache:\n" + cache); assert cache.size() == 0; } Util.sleep(500); List list=receiver.getMsgs(); System.out.println("channel2 received the following msgs: " + list); assert 1 == list.size(); receiver.reset(); } public void testUnicastMessageToLeftMember() throws Exception { c2=createChannel(c1); c2.connect(GROUP); assert 2 == c2.getView().size() : "view=" + c2.getView(); Address dest=c2.getAddress(); c2.close(); Util.sleep(100); c1.send(new Message(dest, null, "hello")); if(cache != null) { System.out.println("age out cache:\n" + cache); assert cache.size() == 1; } Util.sleep(1500); if(cache != null) assert cache.size() == 0 : "cache size is " + cache.size(); } private static class MyReceiver extends ExtendedReceiverAdapter { List msgs=Collections.synchronizedList(new LinkedList()); public void receive(Message msg) { msgs.add(msg); } List getMsgs() { return msgs; } void reset() { msgs.clear(); } } } libjgroups-java-2.12.2.Final.orig/tests/junit/org/jgroups/tests/UnicastLoopbackTest.java0000644000175000017500000001122211647260573031256 0ustar moellermoellerpackage org.jgroups.tests; import org.jgroups.*; import org.jgroups.protocols.TP; import org.jgroups.util.Promise; import org.jgroups.util.Util; import org.testng.Assert; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; /** * Tests unicasts to self (loopback of transport protocol) * @author Richard Achmatowicz 12 May 2008 * @author Bela Ban Dec 31 2003 */ @Test(groups=Global.STACK_DEPENDENT,sequential=true) public class UnicastLoopbackTest extends ChannelTestBase { JChannel channel=null; @BeforeMethod protected void setUp() throws Exception { channel=createChannel(true, 1); } @AfterMethod protected void tearDown() throws Exception { Util.close(channel); } /** * Tests that when UNICAST messages are sent with TP.loopback == true, the following * conditions hold: * (i) no messages touch the network * (ii) all messages are correctly received * * @throws ChannelException * @throws ChannelClosedException * @throws ChannelNotConnectedException * @throws TimeoutException * @throws Exception */ public void testUnicastMsgsWithLoopback() throws Exception { final long TIMEOUT = 2 * 1000 ; final int NUM=1000; long num_msgs_sent_before = 0 ; long num_msgs_sent_after = 0 ; Promise p = new Promise() ; MyReceiver receiver = new MyReceiver(NUM, p) ; channel.setReceiver(receiver) ; channel.connect("demo-group") ; Address local_addr=channel.getAddress(); // set the loopback property on transport setLoopbackProperty(channel, true) ; num_msgs_sent_before = getNumMessagesSentViaNetwork(channel) ; // send NUM UNICAST messages to ourself for(int i=1; i <= NUM; i++) { channel.send(new Message(local_addr, null, new Integer(i))); if(i % 100 == 0) System.out.println("-- sent " + i); } num_msgs_sent_after = getNumMessagesSentViaNetwork(channel) ; // when loopback == true, messages should not touch the network System.out.println("num msgs before: " + num_msgs_sent_before + ", num msgs after: " + num_msgs_sent_after); assert num_msgs_sent_before <= num_msgs_sent_after; assert num_msgs_sent_after < NUM/10; try { // wait for all messages to be received p.getResultWithTimeout(TIMEOUT) ; } catch(TimeoutException te) { // timeout exception occurred Assert.fail("Test timed out before all messages were received") ; } } /** * Returns the number of messages sent across the network. * * @param ch * @return the number of messages sent across the network * @throws Exception */ private static long getNumMessagesSentViaNetwork(JChannel ch) throws Exception { TP transport = ch.getProtocolStack().getTransport(); if (transport == null) { throw new Exception("transport layer is not present - check default stack configuration") ; } return transport.getNumMessagesSent(); } /** * Set the value of the loopback property on the transport layer. * * @param ch * @param loopback * @throws Exception */ private static void setLoopbackProperty(JChannel ch, boolean loopback) throws Exception { TP transport =ch.getProtocolStack().getTransport(); if (transport == null) { throw new Exception("transport layer is not present - check default stack configuration") ; } // check if already set correctly if ((loopback && transport.isLoopback()) || (!loopback && !transport.isLoopback())) return ; // otherwise, set it transport.setLoopback(loopback); } /** * A receiver which waits for all messages to be received and * then sets a promise. */ private static class MyReceiver extends ReceiverAdapter { private final int numExpected ; private int numReceived; private final Promise p ; public MyReceiver(int numExpected, Promise p) { this.numExpected = numExpected ; this.numReceived = 0 ; this.p = p ; } // when we receive a Message, we update the count of messages received public void receive(Message msg) { Integer num=(Integer)msg.getObject(); numReceived++; if(num != null && num.intValue() % 100 == 0) System.out.println("-- received " + num); // if we have received NUM messages, set the result if (numReceived >= numExpected) p.setResult(Boolean.TRUE) ; } public int getNumMsgsReceived() { return numReceived ; } } } libjgroups-java-2.12.2.Final.orig/tests/junit/org/jgroups/tests/UnicastUnitTest.java0000644000175000017500000000444211647260573030451 0ustar moellermoellerpackage org.jgroups.tests; import org.jgroups.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.LinkedList; import java.util.List; /** * Tests unicast functionality * @author Bela Ban */ @Test(groups=Global.STACK_DEPENDENT,sequential=false) public class UnicastUnitTest extends ChannelTestBase { JChannel ch1, ch2=null; @BeforeMethod protected void setUp() throws Exception { ch1=createChannel(true,2); ch2=createChannel(ch1); } @AfterMethod protected void tearDown() throws Exception { if(ch2 != null) ch2.close(); if(ch1 != null) ch1.close(); } @Test public void testUnicastMessageInCallbackExistingMember() throws Exception { ch1.connect("UnicastUnitTest"); MyReceiver receiver=new MyReceiver(ch1); ch1.setReceiver(receiver); ch2.connect("UnicastUnitTest"); Exception ex=receiver.getEx(); if(ex != null) throw ex; ch1.setReceiver(null); } private static class MyReceiver extends ReceiverAdapter { Channel channel; Exception ex; public MyReceiver(Channel channel) { this.channel=channel; } public Exception getEx() { return ex; } public void viewAccepted(View new_view) { Address local_addr=channel.getAddress(); assertNotNull(local_addr); System.out.println("[" + local_addr + "]: " + new_view); List

                    members=new LinkedList
                    (new_view.getMembers()); assertEquals("members=" + members + ", local_addr=" + local_addr, 2, members.size()); members.remove(local_addr); assertEquals(1, members.size()); Address dest=members.get(0); Message unicast_msg=new Message(dest, null, null); try { // uncomment line below for workaround // channel.down(new Event(Event.ENABLE_UNICASTS_TO, dest)); channel.send(unicast_msg); } catch(Exception e) { ex=e; e.printStackTrace(); throw new RuntimeException(e); } } } }libjgroups-java-2.12.2.Final.orig/tests/junit/org/jgroups/tests/VirtualSynchronyTest.java0000644000175000017500000001531311647260573031545 0ustar moellermoellerpackage org.jgroups.tests; import org.jgroups.*; import org.jgroups.util.Util; import org.testng.annotations.Test; import java.io.Serializable; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Random; /** * Virtual Synchrony guarantees that views in a group are observed * in the same order by all group members and that views are totally ordered * with respect to all regular messages in a group. * * Therefore, in virtually synchronous group communication model, all members * that observe the same two consecutive views, receive the same set of regular * multicast messages between those two views. * * VirtualSynchronyTest verifies virtual synchrony as follows. Each surviving member P * from a view Vm that receives a new view Vn sends a message M to group coordinator Q * containing number of messages received in view Vm. Each member P upon receiving * a new view sends a random number of messages to everyone in the group. * * Group coordinator Q upon receiving each message M from a member P verifies * if virtual synchrony is satisifed. * * * @author Vladimir Blagojevic * */ @Test(groups=Global.STACK_INDEPENDENT,sequential=true) public class VirtualSynchronyTest { private final static String CHANNEL_PROPS="flush-udp.xml"; private final static int INITIAL_NUMBER_OF_MEMBERS=5; private int runningTime = 1000*50; // 50 secs private Random r = new Random(); public void testVSynch() throws Exception { long start = System.currentTimeMillis(); boolean running=true; List members=new ArrayList(); //first spawn and join for(int i =0;i1) { Util.sleep(getRandomDelayInSeconds(3,8)*1000); GroupMemberThread unluckyBastard = (GroupMemberThread) members.get(r.nextInt(members.size())); members.remove(unluckyBastard); unluckyBastard.setRunning(false); } running =System.currentTimeMillis() - start <= runningTime; System.out.println("Running time " + ((System.currentTimeMillis()-start)/1000) + " secs"); } System.out.println("Done, Virtual Synchrony satisfied in all tests "); } protected int getRandomDelayInSeconds(int from,int to) { return from + r.nextInt(to-from); } private static class GroupMemberThread extends Thread { JChannel ch = null; int numberOfMessagesInView = 0; View currentView; View prevView; List payloads; VSynchPayload payload; volatile boolean running = true; Random r; int messagesSentPerView = 0; public GroupMemberThread(String name) { super(name); payloads = new ArrayList(); r = new Random(); messagesSentPerView = r.nextInt(25); } public String getAddress() { if(ch!=null && ch.isConnected()) { return ch.getAddress().toString(); } else { return "disconnected " + getName(); } } public void setRunning(boolean b) { running=b; System.out.println("Disconnect " + getAddress()); if(ch!=null)ch.close(); } public void run() { try { ch = new JChannel(CHANNEL_PROPS); ch.connect("vsynchtest"); } catch (Exception e) { e.printStackTrace(); } while (running) { Object msgReceived = null; try { msgReceived = ch.receive(0); if (!running) { // I am not a group member anymore so // I will discard any transient message I // receive } else { if (msgReceived instanceof View) { gotView(msgReceived); } if (msgReceived instanceof Message) { gotMessage(msgReceived); } if (msgReceived instanceof BlockEvent){ ch.blockOk(); } } } catch (TimeoutException e) { } catch (Exception e) { ch.close(); running = false; } } } private void gotMessage(Object msgReceived) { Message msg = (Message) msgReceived; Object m = msg.getObject(); if (m instanceof VSynchPayload) { VSynchPayload pay = (VSynchPayload) m; if (prevView != null && prevView.getVid().equals(pay.viewId)) { payloads.add(pay); boolean receivedAllPayloads = ((payloads.size() == prevView .getMembers().size()) || (payloads.size() == currentView .getMembers().size())); if (receivedAllPayloads) { VSynchPayload first=(VSynchPayload) payloads.get(0); for (Iterator i = payloads.listIterator(1); i.hasNext();) { VSynchPayload p = (VSynchPayload) i.next(); assert first.msgViewCount == p.msgViewCount : "Member " + p + " and " + first + " failed VS"; } System.out.println("VS ok, all " + payloads.size() + " members in " + prevView.getVid() + " view have received " + first.msgViewCount + " messages.\nAll messages sent in " + prevView.getVid() + " were delivered in " + prevView.getVid()); } } } else if (m instanceof String) { assert currentView.getVid().getId() == Long.parseLong((String)m) : ch.getAddress() + " received message from the wrong view. Message sender was " + msg.getSrc(); numberOfMessagesInView++; } } private void gotView(Object msg) throws ChannelNotConnectedException, ChannelClosedException { View tmpView = (View) msg; if (currentView != null) { payload = new VSynchPayload(currentView.getVid(), numberOfMessagesInView,ch.getAddress()); ch.send(tmpView.getMembers().get(0), null, payload); } numberOfMessagesInView = 0; payloads.clear(); prevView = currentView; currentView = tmpView; // send our allotment of messages for (int i = 0; i < messagesSentPerView; i++) { ch.send(null, null, Long.toString(currentView.getVid().getId())); } } } private static class VSynchPayload implements Serializable { public ViewId viewId; public int msgViewCount; public Address member; private static final long serialVersionUID=-3684761509882737012L; public VSynchPayload(ViewId viewId, int numbreOfMessagesInView,Address a) { super(); this.viewId = viewId; this.msgViewCount = numbreOfMessagesInView; this.member=a; } public String toString() { return "[member=" +member + ",viewId=" + viewId.getId() + ",msgCount=" + msgViewCount + "]"; } } } libjgroups-java-2.12.2.Final.orig/tests/other/0000755000175000017500000000000011647260573021042 5ustar moellermoellerlibjgroups-java-2.12.2.Final.orig/tests/other/org/0000755000175000017500000000000011647260573021631 5ustar moellermoellerlibjgroups-java-2.12.2.Final.orig/tests/other/org/jgroups/0000755000175000017500000000000011647260573023322 5ustar moellermoellerlibjgroups-java-2.12.2.Final.orig/tests/other/org/jgroups/tests/0000755000175000017500000000000011647260573024464 5ustar moellermoellerlibjgroups-java-2.12.2.Final.orig/tests/other/org/jgroups/tests/Bsh.java0000644000175000017500000000460511647260573026050 0ustar moellermoeller package org.jgroups.tests; import org.jgroups.util.Util; import java.io.*; import java.net.Socket; /** * Interactive program to test a unicast channel * @author Bela Ban March 16 2003 */ public class Bsh { String host="localhost"; int port=0; long timeout=0; public void start(String[] args) throws Exception { for(int i=0; i < args.length; i++) { String tmp=args[i]; if("-host".equals(tmp)) { host=args[++i]; continue; } if("-port".equals(tmp)) { port=Integer.parseInt(args[++i]); continue; } if("-timeout".equals(tmp)) { timeout=Long.parseLong(args[++i]); continue; } help(); return; } runClient(); } void runClient() throws Exception { final Socket sock=new Socket(host, port); final InputStream in=sock.getInputStream(); final OutputStream out=sock.getOutputStream(); final BufferedReader reader= new BufferedReader(new InputStreamReader(System.in)); new Thread() { public void run() { byte[] buf=new byte[1024]; while(!sock.isClosed()) { try { int num=in.read(buf, 0, buf.length); String str=new String(buf, 0, num); System.out.println(str); } catch(IOException e) { e.printStackTrace(); break; } } } }.start(); while(true) { System.out.print("> "); String line=reader.readLine(); if(line.startsWith("quit") || line.startsWith("exit")) { Util.close(sock); return; } line=line + "\n"; byte[] buf=line.getBytes(); out.write(buf, 0, buf.length); out.flush(); } } static void help() { System.out.println("Bsh [-help] [-host ] [-port ] [-timeout ]"); } public static void main(String[] args) { try { new Bsh().start(args); } catch(Exception ex) { ex.printStackTrace(); } } } libjgroups-java-2.12.2.Final.orig/tests/other/org/jgroups/tests/BundlerTest.java0000644000175000017500000001220711647260573027564 0ustar moellermoellerpackage org.jgroups.tests; import org.jgroups.Global; import org.jgroups.JChannel; import org.jgroups.Message; import org.jgroups.ReceiverAdapter; import org.jgroups.util.Util; import org.testng.annotations.Test; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicInteger; /** * Tests contention in TP.Bundler * @author Bela Ban */ @Test(groups=Global.STACK_INDEPENDENT,sequential=true) public class BundlerTest { static final String props="SHARED_LOOPBACK(thread_pool.queue_max_size=5000;" + "thread_pool.rejection_policy=run;thread_pool.min_threads=20;thread_pool.max_threads=20;" + "oob_thread_pool.rejection_policy=run;enable_bundling=true)"; static final int NUM_THREADS=200; static final int NUM_MSGS=1000; static final int SIZE=1000; // default size of a message in bytes public static void testSimpleMessageReception() throws Exception { JChannel c1=new JChannel(props); JChannel c2=new JChannel(props); MyReceiver r1=new MyReceiver("c1"), r2=new MyReceiver("c2"); c1.setReceiver(r1); c2.setReceiver(r2); c1.connect("testSimpleMessageReception"); c2.connect("testSimpleMessageReception"); int NUM=100; for(int i=1; i <= NUM; i++) { c1.send(null, null, "bla"); c2.send(null, null, "bla"); } for(int i=0; i < 10; i++) { if(r1.getNum() == NUM * 2 && r2.getNum() == NUM * 2) break; Util.sleep(500); } System.out.println("c1 received " + r1.getNum() + " msgs"); System.out.println("c2 received " + r2.getNum() + " msgs"); Util.close(c2, c1); assert r1.getNum() == NUM * 2: "expected " + NUM *2 + ", but got " + r1.getNum(); assert r2.getNum() == NUM * 2: "expected " + NUM *2 + ", but got " + r2.getNum(); } /** * Multiple threads (NUM_THREADS) send messages (NUM_MSGS) * @throws Exception */ public static void testMessageReceptionUnderHighLoad() throws Exception { CountDownLatch latch=new CountDownLatch(1); JChannel c1=new JChannel(props); JChannel c2=new JChannel(props); MyReceiver r1=new MyReceiver("c1"), r2=new MyReceiver("c2"); c1.setReceiver(r1); c2.setReceiver(r2); c1.connect("testSimpleMessageReception"); c2.connect("testSimpleMessageReception"); System.out.println("starting to send messages"); MySender[] c1_senders=new MySender[NUM_THREADS]; for(int i=0; i < c1_senders.length; i++) { c1_senders[i]=new MySender(c1, latch); c1_senders[i].start(); } MySender[] c2_senders=new MySender[NUM_THREADS]; for(int i=0; i < c2_senders.length; i++) { c2_senders[i]=new MySender(c2, latch); c2_senders[i].start(); } Util.sleep(500); long start=System.currentTimeMillis(); latch.countDown(); // starts all threads long NUM_EXPECTED_MSGS=NUM_THREADS * NUM_MSGS * 2; for(int i=0; i < 1000; i++) { if(r1.getNum() >= NUM_EXPECTED_MSGS && r2.getNum() >= NUM_EXPECTED_MSGS) break; Util.sleep(2000); } System.out.println("c1 received " + r1.getNum() + " msgs"); System.out.println("c2 received " + r2.getNum() + " msgs"); long diff=System.currentTimeMillis() - start; Util.close(c2, c1); assert r1.getNum() == NUM_EXPECTED_MSGS : "expected " + NUM_EXPECTED_MSGS + ", but got " + r1.getNum(); assert r2.getNum() == NUM_EXPECTED_MSGS : "expected " + NUM_EXPECTED_MSGS + ", but got " + r2.getNum(); System.out.println("sending and receiving of " + NUM_EXPECTED_MSGS + " took " + diff + " ms"); } private static class MySender extends Thread { private final JChannel ch; private final CountDownLatch latch; private final byte[] buf=new byte[SIZE]; public MySender(JChannel ch, CountDownLatch latch) { this.ch=ch; this.latch=latch; } public void run() { try { latch.await(); } catch(InterruptedException e) { e.printStackTrace(); } for(int i=0; i < NUM_MSGS; i++) { try { Message msg=new Message(null, null, buf); ch.send(msg); } catch(Exception e) { e.printStackTrace(); } } } } private static class MyReceiver extends ReceiverAdapter { final String name; final AtomicInteger num=new AtomicInteger(0); static final long MOD=NUM_MSGS * NUM_THREADS / 10; public MyReceiver(String name) { this.name=name; } public void receive(Message msg) { int count=num.getAndIncrement(); if(count > 0 && count % MOD == 0) { System.out.println("[" + name + "] received " + count + " msgs"); } } public int getNum() { return num.get(); } } }libjgroups-java-2.12.2.Final.orig/tests/other/org/jgroups/tests/ChannelCallbackTest.java0000644000175000017500000000316211647260573031156 0ustar moellermoellerpackage org.jgroups.tests; import org.jgroups.*; import org.jgroups.util.Util; /** * @author Bela Ban */ public class ChannelCallbackTest extends ReceiverAdapter implements ChannelListener { JChannel channel; public static void main(String[] args) { try { new ChannelCallbackTest().start(); } catch(ChannelException e) { e.printStackTrace(); } } private void start() throws ChannelException { channel=new JChannel(); channel.setReceiver(this); channel.addChannelListener(this); channel.connect("bla"); channel.send(null, null, "hello world"); Util.sleep(3000); channel.close(); } public void receive(Message msg) { System.out.println("-- MSG: " + msg); try { Address dst=msg.getDest(); if(dst == null || dst.isMulticastAddress()) channel.send(msg.getSrc(), null, "this is a response"); } catch(Exception e) { e.printStackTrace(); } } public void viewAccepted(View new_view) { System.out.println("-- VIEW: " + new_view); } public void channelConnected(Channel channel) { System.out.println("-- channel connected: " + channel); } public void channelDisconnected(Channel channel) { System.out.println("-- channel disconnected: " + channel); } public void channelClosed(Channel channel) { System.out.println("-- channel closed: " + channel); } public void channelShunned() { } public void channelReconnected(Address addr) { } } libjgroups-java-2.12.2.Final.orig/tests/other/org/jgroups/tests/CheckGaps.java0000644000175000017500000000153211647260573027160 0ustar moellermoellerpackage org.jgroups.tests; import java.util.SortedSet; import java.util.TreeSet; import java.util.Set; import java.util.HashSet; /** Reads a sequence of numbers from stdin and verifies that they are in order * @author Bela Ban */ public class CheckGaps { public static void main(String[] args) { SortedSet set=new TreeSet(); for(int i=0; i < args.length; i++) { set.add(Integer.parseInt(args[i])); } int low=set.first(), high=set.last(); System.out.println("input has " + set.size() + " numbers, low=" + low + ", high=" + high); Set correct_set=new HashSet(); for(int i=low; i < high; i++) { correct_set.add(i); } correct_set.removeAll(set); System.out.println("missing seqnos: " + correct_set); } } ././@LongLink0000000000000000000000000000015100000000000011562 Lustar rootrootlibjgroups-java-2.12.2.Final.orig/tests/other/org/jgroups/tests/CheckMonotonicallyIncreasingNumbers.javalibjgroups-java-2.12.2.Final.orig/tests/other/org/jgroups/tests/CheckMonotonicallyIncreasingNumbers.0000644000175000017500000000373411647260573033620 0ustar moellermoellerpackage org.jgroups.tests; import java.io.*; /** * Checks whether numbers in a stream are monotonically increasing, e.g. *
                     * 1
                     * 2
                     * 3
                     * 4
                     * 
                    * @author Bela Ban */ public class CheckMonotonicallyIncreasingNumbers { static int check(InputStream in) throws IOException { Reader r = new BufferedReader(new InputStreamReader(in)); StreamTokenizer st = new StreamTokenizer(r); int i, cnt=0, num=0, tmp, incorrect=0; boolean first_read=false; while(true) { i=st.nextToken(); if(i == StreamTokenizer.TT_EOF) break; tmp=(int)st.nval; if(!first_read) { first_read=true; } else { if(tmp != num +1) { System.err.println("Number read: " + tmp + ", previous number: " + num + " (lineno: " + st.lineno() + ")"); incorrect++; } } num=tmp; cnt++; if(cnt > 0 && cnt % 1000 == 0) System.out.println("read " + cnt + " numbers"); } return incorrect; } public static void main(String[] args) throws IOException { String file=null; for(int i=0; i < args.length; i++) { if(args[i].equals("-file")) { file=args[++i]; continue; } help(); return; } FileInputStream fis=new FileInputStream(file); int incorrect=CheckMonotonicallyIncreasingNumbers.check(fis); if(incorrect == 0) { System.out.println("OK, all numbers are monotonically increasing"); } else { System.err.println("Failure: there are " + incorrect + " incorrect numbers"); } fis.close(); } private static void help() { System.out.println("CheckMonotonicallyIncreasingNumbers [-help] [-file ]"); } } libjgroups-java-2.12.2.Final.orig/tests/other/org/jgroups/tests/ConnectionMapDemo.java0000644000175000017500000001110611647260573030670 0ustar moellermoeller package org.jgroups.tests; import org.jgroups.Address; import org.jgroups.blocks.AbstractConnectionMap; import org.jgroups.blocks.TCPConnectionMap; import org.jgroups.stack.IpAddress; import org.jgroups.util.DefaultThreadFactory; import org.jgroups.util.Util; import java.io.BufferedReader; import java.io.InputStreamReader; public class ConnectionMapDemo implements TCPConnectionMap.Receiver, AbstractConnectionMap.ConnectionMapListener { TCPConnectionMap ct=null; String dst_host=null; int dst_port=0; public void receive(Address sender, byte[] data, int offset, int length) { String s=new String(data, offset, length); System.out.println("<-- " + s + " (from " + sender + ')'); } public void connectionOpened(Address peer_addr, TCPConnectionMap.TCPConnection conn) { System.out.println("** Connection to " + peer_addr + " opened"); } public void connectionClosed(Address peer_addr) { System.out.println("** Connection to " + peer_addr + " closed"); } public void start(int local_port, String dst_host, int dst_port, long reaper_interval, long conn_expire_time) throws Exception { BufferedReader in; String line; Address dest; byte[] data; if(reaper_interval > 0 || conn_expire_time > 0) ct=new TCPConnectionMap("TCPConnectionMap", new DefaultThreadFactory(Util.getGlobalThreadGroup(), "test", true), this, null, null, local_port, local_port, reaper_interval, conn_expire_time); else ct=new TCPConnectionMap("TCPConnectionMap", new DefaultThreadFactory(Util.getGlobalThreadGroup(), "test", true), null, this, null, null, local_port, local_port); ct.addConnectionMapListener(this); ct.start(); this.dst_host=dst_host; this.dst_port=dst_port; in=new BufferedReader(new InputStreamReader(System.in)); while(true) { try { System.out.print("> "); System.out.flush(); line=in.readLine(); if(line.startsWith("quit".toLowerCase()) || line.startsWith("exit".toLowerCase())) break; if(line.startsWith("conns")) { System.out.println(ct); continue; } dest=new IpAddress(dst_host, dst_port); data=line.getBytes(); ct.send(dest, data, 0, data.length); } catch(Exception e) { System.err.println(e); } } ct.stop(); } public static void main(String[] args) { String host="localhost", tmp; int port=6666, local_port=5555; long reaper_interval=0; long conn_expire_time=0; for(int i=0; i < args.length; i++) { tmp=args[i]; if("-local_port".equals(tmp)) { local_port=Integer.parseInt(args[++i]); continue; } if("-remote_host".equals(tmp)) { host=args[++i]; continue; } if("-remote_port".equals(tmp)) { port=Integer.parseInt(args[++i]); continue; } if("-reaper_interval".equals(tmp)) { reaper_interval=Long.parseLong(args[++i]); continue; } if("-conn_expire_time".equals(tmp)) { conn_expire_time=Long.parseLong(args[++i]); continue; } help(); return; } try { if(reaper_interval > 0 || conn_expire_time > 0) { if(reaper_interval <= 0) reaper_interval=60000; if(conn_expire_time <= 0) conn_expire_time=300000; new ConnectionMapDemo().start(local_port, host, port, reaper_interval, conn_expire_time); } else { new ConnectionMapDemo().start(local_port, host, port, 0, 0); } } catch(Exception ex) { System.err.println("ConnectionMapDemo.main(): " + ex); } } static void help() { System.out.println("ConnectionMapDemo [-help] [-local_port ] [-remote_host ] " + "[-remote_port ] [-reaper_interval ] " + "[-conn_expire_time