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

tabish121 pushed a commit to branch 1.x
in repository https://gitbox.apache.org/repos/asf/qpid-protonj2.git


The following commit(s) were added to refs/heads/1.x by this push:
     new d31f40ac PROTON-3002 Add thread safety measures to the internal 
registries
d31f40ac is described below

commit d31f40acd48a19970c7b12f6f3db34c6d5d770b5
Author: Timothy Bish <[email protected]>
AuthorDate: Wed Nov 4 14:21:22 2026 -0500

    PROTON-3002 Add thread safety measures to the internal registries
    
    Ensure that updates to the encoder and decoder described types
    registries are thread safe per the contract.
    
    (cherry picked from commit bb58aac424bf096b61a44f494cac118e71009e3d)
---
 .../java/org/apache/qpid/protonj2/codec/CodecFactory.java |  8 ++++----
 .../main/java/org/apache/qpid/protonj2/codec/Decoder.java |  4 ++++
 .../main/java/org/apache/qpid/protonj2/codec/Encoder.java |  4 ++++
 .../org/apache/qpid/protonj2/codec/StreamDecoder.java     |  4 ++++
 .../qpid/protonj2/codec/decoders/ProtonDecoder.java       | 15 +++++++++------
 .../qpid/protonj2/codec/decoders/ProtonStreamDecoder.java |  9 +++++----
 .../qpid/protonj2/codec/encoders/ProtonEncoder.java       |  8 +++++---
 7 files changed, 35 insertions(+), 17 deletions(-)

diff --git 
a/protonj2/src/main/java/org/apache/qpid/protonj2/codec/CodecFactory.java 
b/protonj2/src/main/java/org/apache/qpid/protonj2/codec/CodecFactory.java
index c0076dce..a19ba876 100644
--- a/protonj2/src/main/java/org/apache/qpid/protonj2/codec/CodecFactory.java
+++ b/protonj2/src/main/java/org/apache/qpid/protonj2/codec/CodecFactory.java
@@ -25,10 +25,10 @@ import 
org.apache.qpid.protonj2.codec.encoders.ProtonEncoderFactory;
  */
 public final class CodecFactory {
 
-    private static Encoder amqpTypeEncoder;
-    private static Encoder saslTypeEncoder;
-    private static Decoder amqpTypeDecoder;
-    private static Decoder saslTypeDecoder;
+    private static volatile Encoder amqpTypeEncoder;
+    private static volatile Encoder saslTypeEncoder;
+    private static volatile Decoder amqpTypeDecoder;
+    private static volatile Decoder saslTypeDecoder;
 
     private CodecFactory() {
     }
diff --git a/protonj2/src/main/java/org/apache/qpid/protonj2/codec/Decoder.java 
b/protonj2/src/main/java/org/apache/qpid/protonj2/codec/Decoder.java
index 137a082e..5dc86c7b 100644
--- a/protonj2/src/main/java/org/apache/qpid/protonj2/codec/Decoder.java
+++ b/protonj2/src/main/java/org/apache/qpid/protonj2/codec/Decoder.java
@@ -876,6 +876,10 @@ public interface Decoder {
     /**
      * Allows custom {@link DescribedTypeDecoder} instances to be registered 
with this {@link Decoder}
      * which will be used if the described type encoding is encountered during 
decode operations.
+     * <p>
+     * Implementations are not required to treat this method as thread safe 
and users should register
+     * type prior to putting a decoder into use in an environment where 
multiple threads can access the
+     * same decoder instance.
      *
      * @param <V> The type that the decoder reads.
      *
diff --git a/protonj2/src/main/java/org/apache/qpid/protonj2/codec/Encoder.java 
b/protonj2/src/main/java/org/apache/qpid/protonj2/codec/Encoder.java
index 82582150..2c3e5fff 100644
--- a/protonj2/src/main/java/org/apache/qpid/protonj2/codec/Encoder.java
+++ b/protonj2/src/main/java/org/apache/qpid/protonj2/codec/Encoder.java
@@ -986,6 +986,10 @@ public interface Encoder {
      * Register a {@link DescribedTypeEncoder} which can be used when writing 
custom types using this
      * encoder.  When an Object write is performed the type encoder registry 
will be consulted in order
      * to find the best match for the given {@link Object} instance.
+     * <p>
+     * Implementations are not required to treat this method as thread safe 
and users should register
+     * type prior to putting a encoder into use in an environment where 
multiple threads can access the
+     * same encoder instance.
      *
      * @param <V> The type that the encoder handles.
      *
diff --git 
a/protonj2/src/main/java/org/apache/qpid/protonj2/codec/StreamDecoder.java 
b/protonj2/src/main/java/org/apache/qpid/protonj2/codec/StreamDecoder.java
index 15b70a83..899b192b 100644
--- a/protonj2/src/main/java/org/apache/qpid/protonj2/codec/StreamDecoder.java
+++ b/protonj2/src/main/java/org/apache/qpid/protonj2/codec/StreamDecoder.java
@@ -879,6 +879,10 @@ public interface StreamDecoder {
     /**
      * Allows custom {@link StreamDescribedTypeDecoder} instances to be 
registered with this {@link StreamDecoder}
      * which will be used if the described type encoding is encountered during 
decode operations.
+     * <p>
+     * Implementations are not required to treat this method as thread safe 
and users should register
+     * type prior to putting a decoder into use in an environment where 
multiple threads can access the
+     * same decoder instance.
      *
      * @param <V> The type that the decoder reads.
      *
diff --git 
a/protonj2/src/main/java/org/apache/qpid/protonj2/codec/decoders/ProtonDecoder.java
 
b/protonj2/src/main/java/org/apache/qpid/protonj2/codec/decoders/ProtonDecoder.java
index 8f9a7155..f5b0a475 100644
--- 
a/protonj2/src/main/java/org/apache/qpid/protonj2/codec/decoders/ProtonDecoder.java
+++ 
b/protonj2/src/main/java/org/apache/qpid/protonj2/codec/decoders/ProtonDecoder.java
@@ -19,10 +19,10 @@ package org.apache.qpid.protonj2.codec.decoders;
 import java.lang.reflect.Array;
 import java.util.Arrays;
 import java.util.Collections;
-import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.function.BiFunction;
 
 import org.apache.qpid.protonj2.buffer.ProtonBuffer;
@@ -169,15 +169,18 @@ public class ProtonDecoder implements Decoder {
 
     // Registry of decoders for described types which can be updated with user 
defined
     // decoders as well as the default decoders.
-    private final Map<Object, DescribedTypeDecoder<?>> describedTypeDecoders = 
new HashMap<>();
+    private final Map<Object, DescribedTypeDecoder<?>> describedTypeDecoders = 
new ConcurrentHashMap<>();
 
     // Registry of decoders for described types which are not registered which 
can be updated with a
     // limited number of cached decoders to speed up processing
-    private final Map<Object, UnknownDescribedTypeDecoder> 
unknownDescribedTypeDecoders = new HashMap<>();
+    private final Map<Object, UnknownDescribedTypeDecoder> 
unknownDescribedTypeDecoders = new ConcurrentHashMap<>();
 
     // Quick access to decoders that handle AMQP types like Transfer, 
Properties etc.
     private final DescribedTypeDecoder<?>[] amqpTypeDecoders = new 
DescribedTypeDecoder[256];
 
+    // Provides a mechanism for users to register a decoder for reserved 
descriptors, the library
+    // does not allow this normally but some users may wish to decode reserved 
types for messages
+    // or other implementation specific use cases.
     private final BiFunction<ProtonBuffer, DecoderState, Object> 
reservedDescriptorDecoder;
 
     // Internal Decoders used to prevent user to access Proton specific 
decoding methods
@@ -1016,8 +1019,8 @@ public class ProtonDecoder implements Decoder {
 
         final boolean canCache;
 
-        if (descriptor instanceof Symbol && ((Symbol) descriptor).getLength() 
> UNKNOWN_DESCRIBED_TYPE_DESCRIPTOR_SIZE_LIMIT) {
-            canCache = false;
+        if (descriptor instanceof Symbol) {
+            canCache = ((Symbol) descriptor).getLength() <= 
UNKNOWN_DESCRIBED_TYPE_DESCRIPTOR_SIZE_LIMIT;
         } else {
             canCache = descriptor instanceof UnsignedLong;
         }
@@ -1031,7 +1034,7 @@ public class ProtonDecoder implements Decoder {
         };
 
         if (canCache && unknownDescribedTypeDecoders.size() < 
UNKNOWN_DESCRIBED_TYPES_CACHE_LIMIT) {
-            unknownDescribedTypeDecoders.put(descriptor, typeDecoder);
+            unknownDescribedTypeDecoders.putIfAbsent(descriptor, typeDecoder);
         }
 
         return typeDecoder;
diff --git 
a/protonj2/src/main/java/org/apache/qpid/protonj2/codec/decoders/ProtonStreamDecoder.java
 
b/protonj2/src/main/java/org/apache/qpid/protonj2/codec/decoders/ProtonStreamDecoder.java
index 426ccd5c..7dcc05fe 100644
--- 
a/protonj2/src/main/java/org/apache/qpid/protonj2/codec/decoders/ProtonStreamDecoder.java
+++ 
b/protonj2/src/main/java/org/apache/qpid/protonj2/codec/decoders/ProtonStreamDecoder.java
@@ -25,6 +25,7 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
 
 import org.apache.qpid.protonj2.buffer.ProtonBuffer;
 import org.apache.qpid.protonj2.codec.DecodeException;
@@ -175,7 +176,7 @@ public final class ProtonStreamDecoder implements 
StreamDecoder {
 
     // Registry of decoders for described types which are not registered which 
can be updated with a
     // limited number of cached decoders to speed up processing
-    private final Map<Object, UnknownDescribedTypeDecoder> 
unknownDescribedTypeDecoders = new HashMap<>();
+    private final Map<Object, UnknownDescribedTypeDecoder> 
unknownDescribedTypeDecoders = new ConcurrentHashMap<>();
 
     // Quick access to decoders that handle AMQP types like Transfer, 
Properties etc.
     private final StreamDescribedTypeDecoder<?>[] amqpTypeDecoders = new 
StreamDescribedTypeDecoder[256];
@@ -1001,8 +1002,8 @@ public final class ProtonStreamDecoder implements 
StreamDecoder {
 
         final boolean canCache;
 
-        if (descriptor instanceof Symbol && ((Symbol) descriptor).getLength() 
> UNKNOWN_DESCRIBED_TYPE_DESCRIPTOR_SIZE_LIMIT) {
-            canCache = false;
+        if (descriptor instanceof Symbol) {
+            canCache = ((Symbol) descriptor).getLength() <= 
UNKNOWN_DESCRIBED_TYPE_DESCRIPTOR_SIZE_LIMIT;
         } else {
             canCache = descriptor instanceof UnsignedLong;
         }
@@ -1016,7 +1017,7 @@ public final class ProtonStreamDecoder implements 
StreamDecoder {
         };
 
         if (canCache && unknownDescribedTypeDecoders.size() < 
UNKNOWN_DESCRIBED_TYPES_CACHE_LIMIT) {
-            unknownDescribedTypeDecoders.put(descriptor, streamTypeDecoder);
+            unknownDescribedTypeDecoders.putIfAbsent(descriptor, 
streamTypeDecoder);
         }
 
         return streamTypeDecoder;
diff --git 
a/protonj2/src/main/java/org/apache/qpid/protonj2/codec/encoders/ProtonEncoder.java
 
b/protonj2/src/main/java/org/apache/qpid/protonj2/codec/encoders/ProtonEncoder.java
index 02f9fa5c..8fecf306 100644
--- 
a/protonj2/src/main/java/org/apache/qpid/protonj2/codec/encoders/ProtonEncoder.java
+++ 
b/protonj2/src/main/java/org/apache/qpid/protonj2/codec/encoders/ProtonEncoder.java
@@ -17,10 +17,10 @@
 package org.apache.qpid.protonj2.codec.encoders;
 
 import java.util.Date;
-import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.function.Function;
 
 import org.apache.qpid.protonj2.buffer.ProtonBuffer;
@@ -108,7 +108,7 @@ public final class ProtonEncoder implements Encoder {
 
     private ProtonEncoderState singleThreadedState;
 
-    private final Map<Class<?>, TypeEncoder<?>> typeEncoders = new HashMap<>();
+    private final Map<Class<?>, TypeEncoder<?>> typeEncoders = new 
ConcurrentHashMap<>();
     {
         typeEncoders.put(arrayEncoder.getTypeClass(), arrayEncoder);
         typeEncoders.put(binaryEncoder.getTypeClass(), binaryEncoder);
@@ -791,7 +791,9 @@ public final class ProtonEncoder implements Encoder {
 
         // Ensure that next time we find the encoder immediately and don't 
need to
         // go through this process again.
-        typeEncoders.put(typeClass, encoder);
+        if (encoder != null) {
+            typeEncoders.put(typeClass, encoder);
+        }
 
         return encoder;
     }


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

Reply via email to