This is an automated email from the ASF dual-hosted git repository.

rmaucher pushed a commit to branch 9.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git


The following commit(s) were added to refs/heads/9.0.x by this push:
     new c478fa4bf5 Various minor fixes and improvements from code review
c478fa4bf5 is described below

commit c478fa4bf50244464454956b0c8edc6e6d541ab1
Author: remm <[email protected]>
AuthorDate: Wed Jul 8 15:47:30 2026 +0200

    Various minor fixes and improvements from code review
    
    hashCode seemed odd in many places (generated from four bytes), probably
    trying to optimize it. Replace with a more traditional way. If there is
    an actual performance impact, I will revert it.
---
 java/org/apache/catalina/tribes/ByteMessage.java   |  4 +-
 java/org/apache/catalina/tribes/Channel.java       | 56 +++++++---------------
 .../apache/catalina/tribes/ChannelException.java   |  5 +-
 .../apache/catalina/tribes/ChannelInterceptor.java |  2 +-
 .../apache/catalina/tribes/ChannelReceiver.java    |  8 ++--
 .../apache/catalina/tribes/MembershipService.java  |  3 +-
 .../catalina/tribes/group/ChannelCoordinator.java  |  4 +-
 .../apache/catalina/tribes/group/RpcChannel.java   |  4 +-
 .../interceptors/FragmentationInterceptor.java     |  2 +-
 .../tribes/group/interceptors/GzipInterceptor.java | 23 ++++-----
 .../interceptors/MessageDispatchInterceptor.java   |  9 +++-
 .../group/interceptors/NonBlockingCoordinator.java | 17 ++-----
 .../group/interceptors/TcpPingInterceptor.java     | 10 +---
 .../interceptors/TcpPingInterceptorMBean.java      |  7 +++
 .../group/interceptors/ThroughputInterceptor.java  |  8 +++-
 .../interceptors/ThroughputInterceptorMBean.java   |  2 +-
 .../interceptors/TwoPhaseCommitInterceptor.java    |  2 +-
 .../org/apache/catalina/tribes/io/ChannelData.java | 19 ++++----
 .../org/apache/catalina/tribes/io/XByteBuffer.java |  8 +++-
 .../tribes/membership/LocalStrings.properties      |  2 +-
 .../catalina/tribes/membership/McastService.java   |  3 ++
 .../tribes/membership/McastServiceImpl.java        | 32 ++++---------
 .../catalina/tribes/membership/MemberImpl.java     | 15 +++++-
 .../tribes/membership/MembershipProviderBase.java  |  4 +-
 .../membership/cloud/CloudMembershipProvider.java  |  1 +
 .../membership/cloud/DNSMembershipProvider.java    |  3 +-
 .../cloud/KubernetesMembershipProvider.java        | 11 ++---
 .../membership/cloud/LocalStrings.properties       |  1 +
 .../catalina/tribes/transport/ReceiverBase.java    |  5 +-
 29 files changed, 129 insertions(+), 141 deletions(-)

diff --git a/java/org/apache/catalina/tribes/ByteMessage.java 
b/java/org/apache/catalina/tribes/ByteMessage.java
index 4793ef105f..27e4a6d3ad 100644
--- a/java/org/apache/catalina/tribes/ByteMessage.java
+++ b/java/org/apache/catalina/tribes/ByteMessage.java
@@ -50,9 +50,9 @@ public class ByteMessage implements Externalizable {
     }
 
     /**
-     * Creates a byte message with
+     * Creates a byte message with the specified contents.
      *
-     * @param data byte[] - the message contents
+     * @param data the message contents
      */
     public ByteMessage(byte[] data) {
         message = data;
diff --git a/java/org/apache/catalina/tribes/Channel.java 
b/java/org/apache/catalina/tribes/Channel.java
index 8c71351059..b7323c64ec 100644
--- a/java/org/apache/catalina/tribes/Channel.java
+++ b/java/org/apache/catalina/tribes/Channel.java
@@ -139,12 +139,7 @@ public interface Channel {
     int MBR_TX_SEQ = 8;
 
     /**
-     * Send options, when a message is sent, it can have an option flag to 
trigger certain behavior. Most flags are used
-     * to trigger channel interceptors as the message passes through the 
channel stack.
-     * <p>
-     * However, there are five default flags that every channel implementation 
must implement.
-     * <p>
-     * SEND_OPTIONS_BYTE_MESSAGE - The message is a pure byte message and no 
marshaling or unmarshalling will be
+     * Send options. The message is a pure byte message and no marshaling or 
unmarshalling will be
      * performed.
      *
      * @see #send(Member[], Serializable , int)
@@ -153,12 +148,7 @@ public interface Channel {
     int SEND_OPTIONS_BYTE_MESSAGE = 0x0001;
 
     /**
-     * Send options, when a message is sent, it can have an option flag to 
trigger certain behavior. Most flags are used
-     * to trigger channel interceptors as the message passes through the 
channel stack.
-     * <p>
-     * However, there are five default flags that every channel implementation 
must implement
-     * <p>
-     * SEND_OPTIONS_USE_ACK - Message is sent and an ACK is received when the 
message has been received by the
+     * Send options. Message is sent and an ACK is received when the message 
has been received by the
      * recipient. If no ack is received, the message is not considered 
successful.
      *
      * @see #send(Member[], Serializable , int)
@@ -167,12 +157,7 @@ public interface Channel {
     int SEND_OPTIONS_USE_ACK = 0x0002;
 
     /**
-     * Send options, when a message is sent, it can have an option flag to 
trigger certain behavior. Most flags are used
-     * to trigger channel interceptors as the message passes through the 
channel stack.
-     * <p>
-     * However, there are five default flags that every channel implementation 
must implement
-     * <p>
-     * SEND_OPTIONS_SYNCHRONIZED_ACK - Message is sent and an ACK is received 
when the message has been received and
+     * Send options. Message is sent and an ACK is received when the message 
has been received and
      * processed by the recipient. If no ack is received, the message is not 
considered successful
      *
      * @see #send(Member[], Serializable , int)
@@ -181,12 +166,7 @@ public interface Channel {
     int SEND_OPTIONS_SYNCHRONIZED_ACK = 0x0004;
 
     /**
-     * Send options, when a message is sent, it can have an option flag to 
trigger certain behavior. Most flags are used
-     * to trigger channel interceptors as the message passes through the 
channel stack.
-     * <p>
-     * However, there are five default flags that every channel implementation 
must implement
-     * <p>
-     * SEND_OPTIONS_ASYNCHRONOUS - Message will be placed on a queue and sent 
by a separate thread. If the queue is
+     * Send options. Message will be placed on a queue and sent by a separate 
thread. If the queue is
      * full, behaviour depends on {@link 
MessageDispatchInterceptor#isAlwaysSend()}
      *
      * @see #send(Member[], Serializable , int)
@@ -195,12 +175,7 @@ public interface Channel {
     int SEND_OPTIONS_ASYNCHRONOUS = 0x0008;
 
     /**
-     * Send options, when a message is sent, it can have an option flag to 
trigger certain behavior. Most flags are used
-     * to trigger channel interceptors as the message passes through the 
channel stack.
-     * <p>
-     * However, there are five default flags that every channel implementation 
must implement
-     * <p>
-     * SEND_OPTIONS_SECURE - Message is sent over an encrypted channel
+     * Send options. Message is sent over an encrypted channel
      *
      * @see #send(Member[], Serializable , int)
      * @see #send(Member[], Serializable, int, ErrorHandler)
@@ -225,10 +200,10 @@ public interface Channel {
     int SEND_OPTIONS_MULTICAST = 0x0040;
 
     /**
-     * Send options, when a message is sent, it can have an option flag to 
trigger certain behavior. Most flags are used
+     * When a message is sent, it can have an option flag to trigger certain 
behavior. Most flags are used
      * to trigger channel interceptors as the message passes through the 
channel stack.
      * <p>
-     * However, there are five default flags that every channel implementation 
must implement
+     * However, there are seven default flags that every channel 
implementation must implement
      * <p>
      * SEND_OPTIONS_DEFAULT - the default sending options, just a helper 
variable. The default is
      * <code>SEND_OPTIONS_USE_ACK</code>
@@ -291,16 +266,16 @@ public interface Channel {
      *                <li>SND_RX_SEQ - stops the replication receiver</li>
      *                </ul>
      *
-     * @throws ChannelException if a startup error occurs or the service is 
already stopped or an error occurs.
+     * @throws ChannelException if a shutdown error occurs or the service is 
already stopped or an error occurs.
      */
     void stop(int svc) throws ChannelException;
 
     /**
      * Send a message to one or more members in the cluster
      *
-     * @param destination Member[] - the destinations, cannot be null or zero 
length, the reason for that is that a
-     *                        membership change can occur and at that time the 
application is uncertain what group the
-     *                        message actually got sent to.
+     * @param destination Member[] - the destinations, null or zero length 
means all members. Note that a membership
+     *                        change can occur between obtaining the member 
list and sending the message, so the
+     *                        application is uncertain what group the message 
actually got sent to.
      * @param msg         Serializable - the message to send, has to be 
serializable, or a <code>ByteMessage</code> to
      *                        send a pure byte array
      * @param options     int - sender options, see class documentation for 
each interceptor that is configured in order
@@ -320,7 +295,9 @@ public interface Channel {
     /**
      * Send a message to one or more members in the cluster
      *
-     * @param destination Member[] - the destinations, null or zero length 
means all
+     * @param destination Member[] - the destinations, null or zero length 
means all members. Note that a membership
+     *                        change can occur between obtaining the member 
list and sending the message, so the
+     *                        application is uncertain what group the message 
actually got sent to.
      * @param msg         ClusterMessage - the message to send
      * @param options     int - sender options, see class documentation
      * @param handler     ErrorHandler - handle errors through a callback, 
rather than throw it
@@ -510,6 +487,9 @@ public interface Channel {
      * @return a bitwise ORd value of the passed option names
      */
     static int parseSendOptions(String input) {
+        if (input == null || input.isEmpty()) {
+            return 0;
+        }
 
         try {
             return Integer.parseInt(input);
@@ -537,7 +517,7 @@ public interface Channel {
      *
      * @param input the int value of SendOptions
      *
-     * @return the human-friendly string representation in a reverse order 
(i.e. the last option will be shown first)
+     * @return the human-friendly comma-separated string representation of the 
set options
      */
     static String getSendOptionsAsString(int input) {
 
diff --git a/java/org/apache/catalina/tribes/ChannelException.java 
b/java/org/apache/catalina/tribes/ChannelException.java
index 740120ff55..b90424bb19 100644
--- a/java/org/apache/catalina/tribes/ChannelException.java
+++ b/java/org/apache/catalina/tribes/ChannelException.java
@@ -16,6 +16,7 @@
  */
 package org.apache.catalina.tribes;
 
+import java.io.Serializable;
 import java.util.ArrayList;
 
 /**
@@ -165,7 +166,9 @@ public class ChannelException extends Exception {
     /**
      * Represent a failure to a specific member when a message was sent to 
more than one member
      */
-    public static class FaultyMember {
+    public static class FaultyMember implements Serializable {
+        private static final long serialVersionUID = 1L;
+
         /**
          * The cause of the failure.
          */
diff --git a/java/org/apache/catalina/tribes/ChannelInterceptor.java 
b/java/org/apache/catalina/tribes/ChannelInterceptor.java
index 7c478579cb..ed9acdb23e 100644
--- a/java/org/apache/catalina/tribes/ChannelInterceptor.java
+++ b/java/org/apache/catalina/tribes/ChannelInterceptor.java
@@ -194,7 +194,7 @@ public interface ChannelInterceptor extends 
MembershipListener, Heartbeat {
      *                <li>Channel.SND_RX_SEQ - stops the replication 
receiver</li>
      *                </ul>
      *
-     * @throws ChannelException if a startup error occurs or the service is 
already started.
+     * @throws ChannelException if a shutdown error occurs or the service is 
already stopped.
      *
      * @see Channel
      */
diff --git a/java/org/apache/catalina/tribes/ChannelReceiver.java 
b/java/org/apache/catalina/tribes/ChannelReceiver.java
index e14797f390..24105d6e67 100644
--- a/java/org/apache/catalina/tribes/ChannelReceiver.java
+++ b/java/org/apache/catalina/tribes/ChannelReceiver.java
@@ -20,8 +20,8 @@ import java.io.IOException;
 
 /**
  * The <code>ChannelReceiver</code> interface is the data receiver component 
at the bottom layer, the IO layer (for
- * layers see the {@link Channel} interface). This class may optionally 
implement a thread pool for parallel processing
- * of incoming messages.
+ * layers see the {@link Channel} interface). An implementation of this 
interface may optionally implement a thread
+ * pool for parallel processing of incoming messages.
  */
 public interface ChannelReceiver extends Heartbeat {
     /**
@@ -71,11 +71,9 @@ public interface ChannelReceiver extends Heartbeat {
     int getUdpPort();
 
     /**
-     * Sets the message listener to receive notification of incoming
+     * Sets the message listener to receive notification of incoming messages.
      *
      * @param listener MessageListener
-     *
-     * @see MessageListener
      */
     void setMessageListener(MessageListener listener);
 
diff --git a/java/org/apache/catalina/tribes/MembershipService.java 
b/java/org/apache/catalina/tribes/MembershipService.java
index 7227188d27..b029ded6ec 100644
--- a/java/org/apache/catalina/tribes/MembershipService.java
+++ b/java/org/apache/catalina/tribes/MembershipService.java
@@ -68,8 +68,7 @@ public interface MembershipService {
 
 
     /**
-     * Stops the membership service. If a membership listeners is added the 
listener will start to receive membership
-     * events.
+     * Stops the membership service. If a membership listener is added the 
listener will stop receiving membership events.
      *
      * @param level - level MBR_RX stops listening for members, level MBR_TX 
stops broadcasting the server
      *
diff --git a/java/org/apache/catalina/tribes/group/ChannelCoordinator.java 
b/java/org/apache/catalina/tribes/group/ChannelCoordinator.java
index 49ca602a97..f5e950ee09 100644
--- a/java/org/apache/catalina/tribes/group/ChannelCoordinator.java
+++ b/java/org/apache/catalina/tribes/group/ChannelCoordinator.java
@@ -93,7 +93,7 @@ public class ChannelCoordinator extends 
ChannelInterceptorBase implements Messag
      *
      * @param destination Member[] - the destinations, null or zero length 
means all
      * @param msg         ClusterMessage - the message to send
-     * @param payload     TBA
+     * @param payload     InterceptorPayload - optional payload carrying error 
handlers and metadata
      */
     @Override
     public void sendMessage(Member[] destination, ChannelMessage msg, 
InterceptorPayload payload)
@@ -219,7 +219,7 @@ public class ChannelCoordinator extends 
ChannelInterceptorBase implements Messag
      *                SND_TX_SEQ - stops the replication transmitter<BR>
      *                SND_RX_SEQ - stops the replication receiver<BR>
      *
-     * @throws ChannelException if a startup error occurs or the service is 
already started.
+     * @throws ChannelException if a shutdown error occurs or the service is 
already stopped.
      */
     protected synchronized void internalStop(int svc) throws ChannelException {
         try {
diff --git a/java/org/apache/catalina/tribes/group/RpcChannel.java 
b/java/org/apache/catalina/tribes/group/RpcChannel.java
index 482ddda1ef..a1ef2bd1a9 100644
--- a/java/org/apache/catalina/tribes/group/RpcChannel.java
+++ b/java/org/apache/catalina/tribes/group/RpcChannel.java
@@ -300,7 +300,7 @@ public class RpcChannel implements ChannelListener {
     }
 
     /**
-     * Class that holds all response.
+     * Class that holds all responses.
      */
     public static class RpcCollector {
         /**
@@ -414,7 +414,7 @@ public class RpcChannel implements ChannelListener {
 
         @Override
         public int hashCode() {
-            return id[0] + id[1] + id[2] + id[3];
+            return java.util.Arrays.hashCode(id);
         }
 
         @Override
diff --git 
a/java/org/apache/catalina/tribes/group/interceptors/FragmentationInterceptor.java
 
b/java/org/apache/catalina/tribes/group/interceptors/FragmentationInterceptor.java
index 51e0f093ef..a0d9e2eaee 100644
--- 
a/java/org/apache/catalina/tribes/group/interceptors/FragmentationInterceptor.java
+++ 
b/java/org/apache/catalina/tribes/group/interceptors/FragmentationInterceptor.java
@@ -322,7 +322,7 @@ public class FragmentationInterceptor extends 
ChannelInterceptorBase implements
 
         @Override
         public int hashCode() {
-            return XByteBuffer.toInt(uniqueId, 0);
+            return Arrays.hashCode(uniqueId);
         }
 
         @Override
diff --git 
a/java/org/apache/catalina/tribes/group/interceptors/GzipInterceptor.java 
b/java/org/apache/catalina/tribes/group/interceptors/GzipInterceptor.java
index ab0dc33c9d..0e7ecc7160 100644
--- a/java/org/apache/catalina/tribes/group/interceptors/GzipInterceptor.java
+++ b/java/org/apache/catalina/tribes/group/interceptors/GzipInterceptor.java
@@ -160,10 +160,10 @@ public class GzipInterceptor extends 
ChannelInterceptorBase implements GzipInter
      */
     public static byte[] compress(byte[] data) throws IOException {
         ByteArrayOutputStream bout = new ByteArrayOutputStream();
-        GZIPOutputStream gout = new GZIPOutputStream(bout);
-        gout.write(data);
-        gout.flush();
-        gout.close();
+        try (GZIPOutputStream gout = new GZIPOutputStream(bout)) {
+            gout.write(data);
+            gout.finish();
+        }
         return bout.toByteArray();
     }
 
@@ -177,13 +177,14 @@ public class GzipInterceptor extends 
ChannelInterceptorBase implements GzipInter
      */
     public static byte[] decompress(byte[] data) throws IOException {
         ByteArrayOutputStream bout = new 
ByteArrayOutputStream(DEFAULT_BUFFER_SIZE);
-        ByteArrayInputStream bin = new ByteArrayInputStream(data);
-        GZIPInputStream gin = new GZIPInputStream(bin);
-        byte[] tmp = new byte[DEFAULT_BUFFER_SIZE];
-        int length = gin.read(tmp);
-        while (length > -1) {
-            bout.write(tmp, 0, length);
-            length = gin.read(tmp);
+        try (ByteArrayInputStream bin = new ByteArrayInputStream(data);
+                GZIPInputStream gin = new GZIPInputStream(bin)) {
+            byte[] tmp = new byte[DEFAULT_BUFFER_SIZE];
+            int length = gin.read(tmp);
+            while (length > -1) {
+                bout.write(tmp, 0, length);
+                length = gin.read(tmp);
+            }
         }
         return bout.toByteArray();
     }
diff --git 
a/java/org/apache/catalina/tribes/group/interceptors/MessageDispatchInterceptor.java
 
b/java/org/apache/catalina/tribes/group/interceptors/MessageDispatchInterceptor.java
index b00df219dc..f3af235295 100644
--- 
a/java/org/apache/catalina/tribes/group/interceptors/MessageDispatchInterceptor.java
+++ 
b/java/org/apache/catalina/tribes/group/interceptors/MessageDispatchInterceptor.java
@@ -17,6 +17,7 @@
 package org.apache.catalina.tribes.group.interceptors;
 
 import java.util.concurrent.ExecutorService;
+import java.util.concurrent.RejectedExecutionException;
 import java.util.concurrent.ThreadPoolExecutor;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicLong;
@@ -131,8 +132,12 @@ public class MessageDispatchInterceptor extends 
ChannelInterceptorBase implement
      * @return true if added
      */
     public boolean addToQueue(final ChannelMessage msg, final Member[] 
destination, final InterceptorPayload payload) {
-        executor.execute(() -> sendAsyncData(msg, destination, payload));
-        return true;
+        try {
+            executor.execute(() -> sendAsyncData(msg, destination, payload));
+            return true;
+        } catch (RejectedExecutionException ree) {
+            return false;
+        }
     }
 
 
diff --git 
a/java/org/apache/catalina/tribes/group/interceptors/NonBlockingCoordinator.java
 
b/java/org/apache/catalina/tribes/group/interceptors/NonBlockingCoordinator.java
index 673d9cd030..9c126aa817 100644
--- 
a/java/org/apache/catalina/tribes/group/interceptors/NonBlockingCoordinator.java
+++ 
b/java/org/apache/catalina/tribes/group/interceptors/NonBlockingCoordinator.java
@@ -116,18 +116,6 @@ import org.apache.juli.logging.LogFactory;
  * <p>
  * Ideally, the interceptor below this one would be the TcpFailureDetector to 
ensure correct memberships
  * </p>
- * <p>
- * The example above, of course can be simplified with a finite 
statemachine:<br>
- * But I suck at writing state machines, my head gets all confused. One day I 
will document this algorithm though.<br>
- * Maybe I'll do a state diagram :)
- * </p>
- * <h2>State Diagrams</h2>
- * <a 
href="https://people.apache.org/~fhanik/tribes/docs/leader-election-initiate-election.jpg";>Initiate
 an
- * election</a><br>
- * <br>
- * <a 
href="https://people.apache.org/~fhanik/tribes/docs/leader-election-message-arrives.jpg";>Receive
 an election
- * message</a><br>
- * <br>
  */
 public class NonBlockingCoordinator extends ChannelInterceptorBase {
 
@@ -730,6 +718,9 @@ public class NonBlockingCoordinator extends 
ChannelInterceptorBase {
 
     @Override
     public void memberDisappeared(Member member) {
+        if (membership == null) {
+            setupMembership();
+        }
         membership.removeMember(member);
         super.memberDisappeared(member);
         try {
@@ -1012,7 +1003,7 @@ public class NonBlockingCoordinator extends 
ChannelInterceptorBase {
          * Writes the message to the buffer.
          */
         public void write() {
-            buf.reset();
+            buf.clear();
             // header
             buf.append(COORD_HEADER, 0, COORD_HEADER.length);
             // leader
diff --git 
a/java/org/apache/catalina/tribes/group/interceptors/TcpPingInterceptor.java 
b/java/org/apache/catalina/tribes/group/interceptors/TcpPingInterceptor.java
index 1c6bf6a449..28bca893de 100644
--- a/java/org/apache/catalina/tribes/group/interceptors/TcpPingInterceptor.java
+++ b/java/org/apache/catalina/tribes/group/interceptors/TcpPingInterceptor.java
@@ -180,11 +180,7 @@ public class TcpPingInterceptor extends 
ChannelInterceptorBase implements TcpPin
         return useThread;
     }
 
-    /**
-     * Returns whether static-only mode is enabled.
-     *
-     * @return true if only static members are pinged
-     */
+    @Override
     public boolean getStaticOnly() {
         return staticOnly;
     }
@@ -263,9 +259,7 @@ public class TcpPingInterceptor extends 
ChannelInterceptorBase implements TcpPin
                     sleep(interval);
                     sendPing();
                 } catch (InterruptedException ix) {
-                    // Ignore. Probably triggered by a call to stop().
-                    // In the highly unlikely event it was a different trigger,
-                    // simply ignore it and continue.
+                    // Expected during shutdown. The running flag is already 
set to false, so the loop will exit.
                 } catch (Exception e) {
                     
log.warn(sm.getString("tcpPingInterceptor.pingFailed.pingThread"), e);
                 }
diff --git 
a/java/org/apache/catalina/tribes/group/interceptors/TcpPingInterceptorMBean.java
 
b/java/org/apache/catalina/tribes/group/interceptors/TcpPingInterceptorMBean.java
index ef0f8bf2db..ea77d4a420 100644
--- 
a/java/org/apache/catalina/tribes/group/interceptors/TcpPingInterceptorMBean.java
+++ 
b/java/org/apache/catalina/tribes/group/interceptors/TcpPingInterceptorMBean.java
@@ -42,4 +42,11 @@ public interface TcpPingInterceptorMBean {
      */
     boolean getUseThread();
 
+    /**
+     * Returns whether static-only mode is enabled.
+     *
+     * @return true if only static members are pinged
+     */
+    boolean getStaticOnly();
+
 }
diff --git 
a/java/org/apache/catalina/tribes/group/interceptors/ThroughputInterceptor.java 
b/java/org/apache/catalina/tribes/group/interceptors/ThroughputInterceptor.java
index 8ca8cac6ce..f063b0d0be 100644
--- 
a/java/org/apache/catalina/tribes/group/interceptors/ThroughputInterceptor.java
+++ 
b/java/org/apache/catalina/tribes/group/interceptors/ThroughputInterceptor.java
@@ -111,9 +111,13 @@ public class ThroughputInterceptor extends 
ChannelInterceptorBase implements Thr
     @Override
     public void report(double timeTx) {
         if (log.isInfoEnabled()) {
+            double txRate = timeTx > 0 ? mbTx / timeTx : 0;
+            double appTxRate = timeTx > 0 ? mbAppTx / timeTx : 0;
+            double rxElapsed = (System.currentTimeMillis() - rxStart) / 1000d;
+            double rxRate = rxElapsed > 0 ? mbRx / rxElapsed : 0;
             log.info(sm.getString("throughputInterceptor.report", msgTxCnt, 
df.format(mbTx), df.format(mbAppTx),
-                    df.format(timeTx), df.format(mbTx / timeTx), 
df.format(mbAppTx / timeTx), msgTxErr, msgRxCnt,
-                    df.format(mbRx / ((System.currentTimeMillis() - rxStart) / 
(double) 1000)), df.format(mbRx)));
+                    df.format(timeTx), df.format(txRate), 
df.format(appTxRate), msgTxErr, msgRxCnt,
+                    df.format(rxRate), df.format(mbRx)));
         }
     }
 
diff --git 
a/java/org/apache/catalina/tribes/group/interceptors/ThroughputInterceptorMBean.java
 
b/java/org/apache/catalina/tribes/group/interceptors/ThroughputInterceptorMBean.java
index ee5d4cd2c3..94231e328c 100644
--- 
a/java/org/apache/catalina/tribes/group/interceptors/ThroughputInterceptorMBean.java
+++ 
b/java/org/apache/catalina/tribes/group/interceptors/ThroughputInterceptorMBean.java
@@ -24,7 +24,7 @@ import java.util.concurrent.atomic.AtomicLong;
 public interface ThroughputInterceptorMBean {
 
     /**
-     * Returns the socket option flag.
+     * Returns the option flag used to determine if this interceptor should 
process a message.
      * @return the option flag
      */
     int getOptionFlag();
diff --git 
a/java/org/apache/catalina/tribes/group/interceptors/TwoPhaseCommitInterceptor.java
 
b/java/org/apache/catalina/tribes/group/interceptors/TwoPhaseCommitInterceptor.java
index ce78bd7a7c..2344e0baef 100644
--- 
a/java/org/apache/catalina/tribes/group/interceptors/TwoPhaseCommitInterceptor.java
+++ 
b/java/org/apache/catalina/tribes/group/interceptors/TwoPhaseCommitInterceptor.java
@@ -82,7 +82,7 @@ public class TwoPhaseCommitInterceptor extends 
ChannelInterceptorBase {
             } else {
                 confirmation = (ChannelMessage) msg.clone();
             }
-            confirmation.getMessage().reset();
+            confirmation.getMessage().clear();
             UUIDGenerator.randomUUID(false, confirmation.getUniqueId(), 0);
             confirmation.getMessage().append(START_DATA, 0, START_DATA.length);
             confirmation.getMessage().append(msg.getUniqueId(), 0, 
msg.getUniqueId().length);
diff --git a/java/org/apache/catalina/tribes/io/ChannelData.java 
b/java/org/apache/catalina/tribes/io/ChannelData.java
index 99a26d5fe4..2f60b376f5 100644
--- a/java/org/apache/catalina/tribes/io/ChannelData.java
+++ b/java/org/apache/catalina/tribes/io/ChannelData.java
@@ -172,9 +172,9 @@ public class ChannelData implements ChannelMessage {
                 4 + // unique id length off=12
                 uniqueId.length + // id data off=12+uniqueId.length
                 4 + // addr length off=12+uniqueId.length+4
-                address.getDataLength() + // member data 
off=12+uniqueId.length+4+add.length
+                ((address != null) ? address.getDataLength() : 0) + // member 
data off=12+uniqueId.length+4+add.length
                 4 + // message length off=12+uniqueId.length+4+add.length+4
-                message.getLength();
+                ((message != null) ? message.getLength() : 0);
     }
 
     /**
@@ -198,7 +198,8 @@ public class ChannelData implements ChannelMessage {
      * @return the byte array
      */
     public byte[] getDataPackage(byte[] data, int offset) {
-        byte[] addr = address.getData(false);
+        byte[] addr = (address != null) ? address.getData(false) : new byte[0];
+        int msgLen = (message != null) ? message.getLength() : 0;
         XByteBuffer.toBytes(options, data, offset);
         offset += 4; // options
         XByteBuffer.toBytes(timestamp, data, offset);
@@ -211,9 +212,11 @@ public class ChannelData implements ChannelMessage {
         offset += 4; // addr.length
         System.arraycopy(addr, 0, data, offset, addr.length);
         offset += addr.length; // addr data
-        XByteBuffer.toBytes(message.getLength(), data, offset);
+        XByteBuffer.toBytes(msgLen, data, offset);
         offset += 4; // message.length
-        System.arraycopy(message.getBytesDirect(), 0, data, offset, 
message.getLength());
+        if (msgLen > 0) {
+            System.arraycopy(message.getBytesDirect(), 0, data, offset, 
msgLen);
+        }
         return data;
     }
 
@@ -278,7 +281,6 @@ public class ChannelData implements ChannelMessage {
         // data.message = new XByteBuffer(new byte[xsize],false);
         data.message = BufferPool.getBufferPool().getBuffer(xsize, false);
         offset += 4; // message length
-        System.arraycopy(b, offset, data.message.getBytesDirect(), 0, xsize);
         data.message.append(b, offset, xsize);
         offset += xsize; // message data
         return data;
@@ -286,7 +288,7 @@ public class ChannelData implements ChannelMessage {
 
     @Override
     public int hashCode() {
-        return XByteBuffer.toInt(getUniqueId(), 0);
+        return Arrays.hashCode(getUniqueId());
     }
 
     /**
@@ -308,7 +310,7 @@ public class ChannelData implements ChannelMessage {
     /**
      * Create a shallow clone, only the data gets recreated
      *
-     * @return ClusterData
+     * @return ChannelData clone
      */
     @Override
     public ChannelData clone() {
@@ -321,6 +323,7 @@ public class ChannelData implements ChannelMessage {
         }
         if (this.message != null) {
             clone.message = new XByteBuffer(this.message.getBytesDirect(), 
false);
+            clone.message.setLength(this.message.getLength());
         }
         return clone;
     }
diff --git a/java/org/apache/catalina/tribes/io/XByteBuffer.java 
b/java/org/apache/catalina/tribes/io/XByteBuffer.java
index 28762b9896..8df2165aa6 100644
--- a/java/org/apache/catalina/tribes/io/XByteBuffer.java
+++ b/java/org/apache/catalina/tribes/io/XByteBuffer.java
@@ -154,6 +154,7 @@ public class XByteBuffer implements Serializable {
     /**
      * Resets the buffer length to zero.
      */
+    @Deprecated
     public void reset() {
         bufSize = 0;
     }
@@ -179,7 +180,7 @@ public class XByteBuffer implements Serializable {
     }
 
     /**
-     * Resets the buffer
+     * Resets the buffer length to zero.
      */
     public void clear() {
         bufSize = 0;
@@ -196,6 +197,9 @@ public class XByteBuffer implements Serializable {
      *             something, or the length of data is 0
      */
     public boolean append(ByteBuffer b, int len) {
+        if (len == 0) {
+            return false;
+        }
         int newcount = bufSize + len;
         if (newcount > buf.length) {
             expand(newcount);
@@ -524,7 +528,7 @@ public class XByteBuffer implements Serializable {
     /**
      * Convert eight bytes to a long
      *
-     * @param b   - the byte array containing the four bytes
+     * @param b   - the byte array containing the eight bytes
      * @param off - the offset
      *
      * @return the long value constructed from the eight bytes
diff --git a/java/org/apache/catalina/tribes/membership/LocalStrings.properties 
b/java/org/apache/catalina/tribes/membership/LocalStrings.properties
index a4848fb09e..70c20154e9 100644
--- a/java/org/apache/catalina/tribes/membership/LocalStrings.properties
+++ b/java/org/apache/catalina/tribes/membership/LocalStrings.properties
@@ -39,7 +39,6 @@ mcastServiceImpl.memberDisappeared.failed=Unable to process 
member disappeared m
 mcastServiceImpl.memberExpire=Expire member [{0}]
 mcastServiceImpl.memberShutdown=Member [{0}] has shutdown
 mcastServiceImpl.messageError=Unable to decode message
-mcastServiceImpl.packet.tooLong=Multicast packet received was too long, 
dropping package:[{0}]
 mcastServiceImpl.receive.running=McastService.receive already running.
 mcastServiceImpl.recovery=Tribes membership running recovery thread. 
Multicasting is not functional.
 mcastServiceImpl.recovery.failed=Recovery attempt number [{0}] failed. Trying 
again in [{1}] seconds.
@@ -56,6 +55,7 @@ mcastServiceImpl.unableReceive.broadcastMessage=Unable to 
receive broadcast mess
 mcastServiceImpl.waitForMembers.done=Done sleeping, membership established, 
start level:[{0}]
 mcastServiceImpl.waitForMembers.start=Sleeping for [{0}] milliseconds to 
establish cluster membership, start level:[{1}]
 
+memberImpl.invalid.hostLength=Invalid host length [{0}]
 memberImpl.invalid.package.begin=Invalid package, should start with:[{0}]
 memberImpl.invalid.package.end=Invalid package, should end with:[{0}]
 memberImpl.large.payload=Payload is too large for tribes to handle.
diff --git a/java/org/apache/catalina/tribes/membership/McastService.java 
b/java/org/apache/catalina/tribes/membership/McastService.java
index c9a4b06c1c..d873139415 100644
--- a/java/org/apache/catalina/tribes/membership/McastService.java
+++ b/java/org/apache/catalina/tribes/membership/McastService.java
@@ -456,6 +456,7 @@ public class McastService extends MembershipServiceBase 
implements MessageListen
 
     @Override
     public void broadcast(ChannelMessage message) throws ChannelException {
+        McastServiceImpl impl = this.impl;
         if (impl == null || (impl.startLevel & Channel.MBR_TX_SEQ) != 
Channel.MBR_TX_SEQ) {
             throw new ChannelException(sm.getString("mcastService.noStart"));
         }
@@ -520,6 +521,7 @@ public class McastService extends MembershipServiceBase 
implements MessageListen
         if (localMember != null) {
             localMember.setPayload(payload);
             try {
+                McastServiceImpl impl = this.impl;
                 if (impl != null) {
                     impl.send(false);
                 }
@@ -535,6 +537,7 @@ public class McastService extends MembershipServiceBase 
implements MessageListen
         if (localMember != null) {
             localMember.setDomain(domain);
             try {
+                McastServiceImpl impl = this.impl;
                 if (impl != null) {
                     impl.send(false);
                 }
diff --git a/java/org/apache/catalina/tribes/membership/McastServiceImpl.java 
b/java/org/apache/catalina/tribes/membership/McastServiceImpl.java
index b6fcff0d5e..0a4a854a90 100644
--- a/java/org/apache/catalina/tribes/membership/McastServiceImpl.java
+++ b/java/org/apache/catalina/tribes/membership/McastServiceImpl.java
@@ -42,8 +42,7 @@ import org.apache.juli.logging.LogFactory;
  * A <b>membership</b> implementation using simple multicast. This is the 
representation of a multicast membership
  * service. This class is responsible for maintaining a list of active cluster 
nodes in the cluster. If a node fails to
  * send out a heartbeat, the node will be dismissed. This is the low level 
implementation that handles the multicasting
- * sockets. Need to fix this, could use java.nio and only need one thread to 
send and receive, or just use a timeout on
- * the receive
+ * sockets.
  */
 public class McastServiceImpl extends MembershipProviderBase {
 
@@ -94,10 +93,6 @@ public class McastServiceImpl extends MembershipProviderBase 
{
      * How often to we send out a broadcast saying we are alive, must be 
smaller than timeToExpiration
      */
     protected final long sendFrequency;
-    /**
-     * Reuse the sendPacket, no need to create a new one every time
-     */
-    protected DatagramPacket sendPacket;
     /**
      * Reuse the receivePacket, no need to create a new one every time
      */
@@ -196,9 +191,6 @@ public class McastServiceImpl extends 
MembershipProviderBase {
      */
     public void init() throws IOException {
         setupSocket();
-        sendPacket = new DatagramPacket(new byte[MAX_PACKET_SIZE], 
MAX_PACKET_SIZE);
-        sendPacket.setAddress(address);
-        sendPacket.setPort(port);
         receivePacket = new DatagramPacket(new byte[MAX_PACKET_SIZE], 
MAX_PACKET_SIZE);
         receivePacket.setAddress(address);
         receivePacket.setPort(port);
@@ -374,23 +366,19 @@ public class McastServiceImpl extends 
MembershipProviderBase {
     }
 
     /**
-     * Receive a datagram packet, locking wait
+     * Receive a datagram packet from the multicast socket with timeout.
      *
      * @throws IOException Received failed
      */
     public void receive() throws IOException {
         try {
             socket.receive(receivePacket);
-            if (receivePacket.getLength() > MAX_PACKET_SIZE) {
-                log.error(sm.getString("mcastServiceImpl.packet.tooLong", 
Integer.toString(receivePacket.getLength())));
+            byte[] data = new byte[receivePacket.getLength()];
+            System.arraycopy(receivePacket.getData(), 
receivePacket.getOffset(), data, 0, data.length);
+            if (XByteBuffer.firstIndexOf(data, 0, MemberImpl.TRIBES_MBR_BEGIN) 
== 0) {
+                memberDataReceived(data);
             } else {
-                byte[] data = new byte[receivePacket.getLength()];
-                System.arraycopy(receivePacket.getData(), 
receivePacket.getOffset(), data, 0, data.length);
-                if (XByteBuffer.firstIndexOf(data, 0, 
MemberImpl.TRIBES_MBR_BEGIN) == 0) {
-                    memberDataReceived(data);
-                } else {
-                    memberBroadcastsReceived(data);
-                }
+                memberBroadcastsReceived(data);
             }
         } catch (SocketTimeoutException ignore) {
             /*
@@ -555,10 +543,10 @@ public class McastServiceImpl extends 
MembershipProviderBase {
         } else if (log.isTraceEnabled()) {
             log.trace("Sending message broadcast " + packet.getLength() + " 
bytes from " + member);
         }
-        packet.setAddress(address);
-        packet.setPort(port);
         // TODO this operation is not thread safe
         synchronized (sendLock) {
+            packet.setAddress(address);
+            packet.setPort(port);
             socket.send(packet);
         }
         if (checkexpired) {
@@ -732,7 +720,7 @@ public class McastServiceImpl extends 
MembershipProviderBase {
     protected static class RecoveryThread extends Thread {
 
         /**
-         * Flag to track if recovery is running.
+         * Flag to track if recovery is running. Only one recovery can run at 
a time.
          */
         private static final AtomicBoolean running = new AtomicBoolean(false);
 
diff --git a/java/org/apache/catalina/tribes/membership/MemberImpl.java 
b/java/org/apache/catalina/tribes/membership/MemberImpl.java
index e93ce04e67..cc556fecfe 100644
--- a/java/org/apache/catalina/tribes/membership/MemberImpl.java
+++ b/java/org/apache/catalina/tribes/membership/MemberImpl.java
@@ -184,7 +184,7 @@ public class MemberImpl implements Member, 
java.io.Externalizable {
     /**
      * Create a data package to send over the wire representing this member. 
This is faster than serialization.
      *
-     * @return - the bytes for this member deserialized
+     * @return the serialized bytes for this member
      */
     public byte[] getData() {
         return getData(true);
@@ -281,6 +281,10 @@ public class MemberImpl implements Member, 
java.io.Externalizable {
         XByteBuffer.toBytes(udpPort, data, pos);
         pos += 4;
         // host length
+        if (host.length > 127) {
+            throw new 
IllegalArgumentException(sm.getString("memberImpl.invalid.hostLength",
+                    Integer.toString(host.length)));
+        }
         data[pos++] = (byte) host.length;
         // host
         System.arraycopy(host, 0, data, pos, host.length);
@@ -399,6 +403,10 @@ public class MemberImpl implements Member, 
java.io.Externalizable {
 
 
         byte hl = data[pos++];
+        if (hl < 0) {
+            throw new 
IllegalArgumentException(sm.getString("memberImpl.invalid.hostLength",
+                    Integer.toString(hl)));
+        }
         byte[] addr = new byte[hl];
         System.arraycopy(data, pos, addr, 0, hl);
         pos += hl;
@@ -611,7 +619,10 @@ public class MemberImpl implements Member, 
java.io.Externalizable {
 
     @Override
     public int hashCode() {
-        return getHost()[0] + getHost()[1] + getHost()[2] + getHost()[3];
+        int result = Arrays.hashCode(getHost());
+        result = 31 * result + getPort();
+        result = 31 * result + Arrays.hashCode(getUniqueId());
+        return result;
     }
 
     /**
diff --git 
a/java/org/apache/catalina/tribes/membership/MembershipProviderBase.java 
b/java/org/apache/catalina/tribes/membership/MembershipProviderBase.java
index ec185f4544..0cfa5026af 100644
--- a/java/org/apache/catalina/tribes/membership/MembershipProviderBase.java
+++ b/java/org/apache/catalina/tribes/membership/MembershipProviderBase.java
@@ -67,7 +67,7 @@ public abstract class MembershipProviderBase implements 
MembershipProvider {
 
     @Override
     public Member getMember(Member mbr) {
-        if (membership.getMembers() == null) {
+        if (membership == null || membership.getMembers() == null) {
             return null;
         }
         return membership.getMember(mbr);
@@ -75,7 +75,7 @@ public abstract class MembershipProviderBase implements 
MembershipProvider {
 
     @Override
     public Member[] getMembers() {
-        if (membership.getMembers() == null) {
+        if (membership == null || membership.getMembers() == null) {
             return Membership.EMPTY_MEMBERS;
         }
         return membership.getMembers();
diff --git 
a/java/org/apache/catalina/tribes/membership/cloud/CloudMembershipProvider.java 
b/java/org/apache/catalina/tribes/membership/cloud/CloudMembershipProvider.java
index 0c130aec6a..ee61f4f8c9 100644
--- 
a/java/org/apache/catalina/tribes/membership/cloud/CloudMembershipProvider.java
+++ 
b/java/org/apache/catalina/tribes/membership/cloud/CloudMembershipProvider.java
@@ -188,6 +188,7 @@ public abstract class CloudMembershipProvider extends 
MembershipProviderBase imp
 
     @Override
     public boolean stop(int level) throws Exception {
+        service.getChannel().removeChannelListener(this);
         return true;
     }
 
diff --git 
a/java/org/apache/catalina/tribes/membership/cloud/DNSMembershipProvider.java 
b/java/org/apache/catalina/tribes/membership/cloud/DNSMembershipProvider.java
index d9be130e9f..e43d66b3b2 100644
--- 
a/java/org/apache/catalina/tribes/membership/cloud/DNSMembershipProvider.java
+++ 
b/java/org/apache/catalina/tribes/membership/cloud/DNSMembershipProvider.java
@@ -185,7 +185,7 @@ public class DNSMembershipProvider extends 
CloudMembershipProvider {
                 try {
                     member = new MemberImpl(ip, port, aliveTime);
                 } catch (IOException ioe) {
-                    
log.error(sm.getString("kubernetesMembershipProvider.memberError"), ioe);
+                    
log.error(sm.getString("dnsMembershipProvider.memberError"), ioe);
                     continue;
                 }
                 member.setUniqueId(id);
@@ -227,6 +227,7 @@ public class DNSMembershipProvider extends 
CloudMembershipProvider {
             member.setMemberAliveTime(-1);
             updateMember(member, true);
         }
+        // We will not actually process the message
         return false;
     }
 }
diff --git 
a/java/org/apache/catalina/tribes/membership/cloud/KubernetesMembershipProvider.java
 
b/java/org/apache/catalina/tribes/membership/cloud/KubernetesMembershipProvider.java
index 83d6206ca8..410b103fbb 100644
--- 
a/java/org/apache/catalina/tribes/membership/cloud/KubernetesMembershipProvider.java
+++ 
b/java/org/apache/catalina/tribes/membership/cloud/KubernetesMembershipProvider.java
@@ -76,6 +76,9 @@ public class KubernetesMembershipProvider extends 
CloudMembershipProvider {
         }
 
         String protocol = getEnv(CUSTOM_ENV_PREFIX + "MASTER_PROTOCOL", 
"KUBERNETES_MASTER_PROTOCOL");
+        if (protocol == null) {
+            protocol = "https";
+        }
         String masterHost = getEnv(CUSTOM_ENV_PREFIX + "MASTER_HOST", 
"KUBERNETES_SERVICE_HOST");
         String masterPort = getEnv(CUSTOM_ENV_PREFIX + "MASTER_PORT", 
"KUBERNETES_SERVICE_PORT");
 
@@ -87,9 +90,6 @@ public class KubernetesMembershipProvider extends 
CloudMembershipProvider {
         }
 
         if (clientCertificateFile == null) {
-            if (protocol == null) {
-                protocol = "https";
-            }
             String saTokenFile = getEnv(CUSTOM_ENV_PREFIX + "SA_TOKEN_FILE", 
"SA_TOKEN_FILE");
             if (saTokenFile == null) {
                 saTokenFile = 
"/var/run/secrets/kubernetes.io/serviceaccount/token";
@@ -103,9 +103,6 @@ public class KubernetesMembershipProvider extends 
CloudMembershipProvider {
                 
log.error(sm.getString("kubernetesMembershipProvider.streamError"), ioe);
             }
         } else {
-            if (protocol == null) {
-                protocol = "http";
-            }
             String clientKeyFile = getEnv("KUBERNETES_CLIENT_KEY_FILE");
             if (clientKeyFile == null) {
                 log.error(sm.getString("kubernetesMembershipProvider.noKey"));
@@ -243,7 +240,7 @@ public class KubernetesMembershipProvider extends 
CloudMembershipProvider {
                 Object objectUid = metadata.get("uid");
                 Object creationTimestampObject = 
metadata.get("creationTimestamp");
                 if (creationTimestampObject == null) {
-                    
log.warn(sm.getString("kubernetesMembershipProvider.invalidPod", "uid"));
+                    
log.warn(sm.getString("kubernetesMembershipProvider.invalidPod", 
"creationTimestamp"));
                     continue;
                 }
                 // "status" contains "phase" (which must be "Running") and 
"podIP"
diff --git 
a/java/org/apache/catalina/tribes/membership/cloud/LocalStrings.properties 
b/java/org/apache/catalina/tribes/membership/cloud/LocalStrings.properties
index 632f49ccc6..0a0816cf02 100644
--- a/java/org/apache/catalina/tribes/membership/cloud/LocalStrings.properties
+++ b/java/org/apache/catalina/tribes/membership/cloud/LocalStrings.properties
@@ -32,6 +32,7 @@ cloudMembershipProvider.start=Namespace [{0}] set; clustering 
enabled
 cloudMembershipService.stopFail=Unable to stop the cloud membership service, 
level: [{0}]
 
 dnsMembershipProvider.dnsError=Error getting hosts address list for namespace 
[{0}]
+dnsMembershipProvider.memberError=Error creating member
 
 kubernetesMembershipProvider.invalidPod=Pod is missing some required 
attributes: [{0}]
 kubernetesMembershipProvider.invalidPodsList=Invalid pods list: [{0}]
diff --git a/java/org/apache/catalina/tribes/transport/ReceiverBase.java 
b/java/org/apache/catalina/tribes/transport/ReceiverBase.java
index 3f45ff7016..7f52950caf 100644
--- a/java/org/apache/catalina/tribes/transport/ReceiverBase.java
+++ b/java/org/apache/catalina/tribes/transport/ReceiverBase.java
@@ -139,10 +139,7 @@ public abstract class ReceiverBase implements 
ChannelReceiver, ListenCallback, R
         }
         executor = null;
         if (oname != null) {
-            JmxRegistry jmxRegistry = JmxRegistry.getRegistry(channel);
-            if (jmxRegistry != null) {
-                jmxRegistry.unregisterJmx(oname);
-            }
+            JmxRegistry.getRegistry(channel).unregisterJmx(oname);
             oname = null;
         }
         channel = null;


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]


Reply via email to