From 186240da5cd8ca9a53cf6e208ed91dc6245a3d3e Mon Sep 17 00:00:00 2001 From: "Patrick M. Niedzielski" Date: Tue, 2 Dec 2025 17:45:19 -0500 Subject: [PATCH 1/5] Chore: Bump supported versions of Java Signed-off-by: Patrick M. Niedzielski --- .github/workflows/build.yaml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 6081259..e884474 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -21,8 +21,9 @@ jobs: name: "Build & UTs: JDK ${{ matrix.Java }}" runs-on: ubuntu-latest strategy: + fail-fast: false matrix: - java: [ '17' ] + java: [ '17', '21', '22', '23', '24', '25' ] steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: @@ -39,7 +40,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - java: [ '17' ] + java: [ '17', '21', '25' ] steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: From ef8189c8efaf4af5f2730d25bc29e2b0cd127338 Mon Sep 17 00:00:00 2001 From: "Patrick M. Niedzielski" Date: Tue, 2 Dec 2025 18:11:12 -0500 Subject: [PATCH 2/5] Fix: Remove explicit JDK versioning from `SystemUtil` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As it stands, our SDK only supports three old LTS versions of Java. Any user who wants to use the SDK on non-LTS versions, or on reasonably new versions of Java, will be prevented. However, what the code really wants is to know whether we’re on a version of Java before or after Java 9, with which we determine which CRC32 implementation to use. This patch *removes* explicit JDK versioning from `SystemUtil`, which is a breaking change. Signed-off-by: Patrick M. Niedzielski --- .../bmq/impl/infr/util/SystemUtil.java | 37 ------------------- .../bmq/impl/infr/util/SystemUtilTest.java | 19 ---------- 2 files changed, 56 deletions(-) diff --git a/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/util/SystemUtil.java b/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/util/SystemUtil.java index 8d4feee..6b476d7 100644 --- a/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/util/SystemUtil.java +++ b/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/util/SystemUtil.java @@ -19,7 +19,6 @@ import java.lang.invoke.MethodHandles; import java.lang.management.ManagementFactory; import java.net.ServerSocket; -import java.util.Arrays; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -27,42 +26,6 @@ public class SystemUtil { static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); - public enum JavaVersion { - JAVA_UNSUPPORTED(""), - JAVA_8("1.8"), - JAVA_11("11"), - JAVA_17("17"); - - private final String major; - - JavaVersion(String major) { - this.major = major; - } - - public boolean isSupported() { - return !major.isEmpty(); - } - } - - public static JavaVersion getJavaVersion() { - JavaVersion result = JavaVersion.JAVA_UNSUPPORTED; - - try { - String version = getJavaVersionString(); - - result = - Arrays.stream(JavaVersion.values()) - .filter(JavaVersion::isSupported) - .filter(v -> version.startsWith(v.major)) - .findFirst() - .orElse(JavaVersion.JAVA_UNSUPPORTED); - } catch (Exception e) { - logger.info("Error while getting Java version: ", e); - } - - return result; - } - public static String getJavaVersionString() { return System.getProperty("java.version"); } diff --git a/bmq-sdk/src/test/java/com/bloomberg/bmq/impl/infr/util/SystemUtilTest.java b/bmq-sdk/src/test/java/com/bloomberg/bmq/impl/infr/util/SystemUtilTest.java index 4d2600b..f7a3482 100644 --- a/bmq-sdk/src/test/java/com/bloomberg/bmq/impl/infr/util/SystemUtilTest.java +++ b/bmq-sdk/src/test/java/com/bloomberg/bmq/impl/infr/util/SystemUtilTest.java @@ -15,7 +15,6 @@ */ package com.bloomberg.bmq.impl.infr.util; -import static org.junit.jupiter.api.Assertions.assertNotSame; import static org.junit.jupiter.api.Assertions.assertTrue; import java.lang.invoke.MethodHandles; @@ -27,24 +26,6 @@ class SystemUtilTest { static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); - @Test - void testJavaVersions() { - assertTrue( - SystemUtil.JavaVersion.JAVA_UNSUPPORTED.compareTo(SystemUtil.JavaVersion.JAVA_8) - < 0); - assertTrue(SystemUtil.JavaVersion.JAVA_8.compareTo(SystemUtil.JavaVersion.JAVA_11) < 0); - assertTrue(SystemUtil.JavaVersion.JAVA_11.compareTo(SystemUtil.JavaVersion.JAVA_17) < 0); - } - - @Test - void testVersion() { - SystemUtil.JavaVersion v = SystemUtil.getJavaVersion(); - - logger.info("JAVA ver.: {}", v); - - assertNotSame(SystemUtil.JavaVersion.JAVA_UNSUPPORTED, v); - } - @Test void testPid() { int p = SystemUtil.getProcessId(); From ce5fdee713b9b0c2cd7af1928c17c4c785032d93 Mon Sep 17 00:00:00 2001 From: "Patrick M. Niedzielski" Date: Wed, 3 Dec 2025 11:25:40 -0500 Subject: [PATCH 3/5] Fix: Avoid possible `this` escape before subclass is fully initialized MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since Java16, we get a number of warnings of the sort [WARNING] blazingmq-sdk-java/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/BoolMessageProperty.java:[27,25] possible 'this' escape before subclass is fully initialized These warnings are shown when a class that may serve as a superclass calls an overridable method in its constructor. If some other class inherits from that superclass and overrides that method, the overridden implementation will be called on an object that was not fully initialized. We can fix this in a few ways. On one hand, we can avoid calling methods in class constructors. This works nicely in cases like `ControlMessageChoice`, which directly forwards to an `init()` method to null out its member variables. Duplicating this code is not terrible. However, in more complicated cases, like `PutHeader` and the like, the methods we call abstract bit manupulation that’s easy to get wrong. It’s best not to inline that code in the constructors. On the other hand, we can prevent the methods we call from the class constructor from being overriden. In all cases we have this warning, there is a strong case to be made that we don’t want to allow inheritance. The message classes need to follow the protocol schema directly, and the protocol classes are also directly tied to the binary protocol that the BlazingMQ broker uses. We should not allow users to inject their own behaviors into this parsing. This patch fixes all “`this` escape” warnings that newer JDK versions give by marking the classes they’re shown for as `final`. Signed-off-by: Patrick M. Niedzielski --- .../impl/infr/msg/AuthenticationMessage.java | 16 ++--- .../impl/infr/msg/ControlMessageChoice.java | 70 +++++++++---------- .../infr/msg/NegotiationMessageChoice.java | 18 ++--- .../bmq/impl/infr/proto/AckEventImpl.java | 3 + .../bmq/impl/infr/proto/AckMessageImpl.java | 2 +- .../impl/infr/proto/AckMessageIterator.java | 2 + .../infr/proto/BinaryMessageProperty.java | 1 + .../impl/infr/proto/BoolMessageProperty.java | 1 + .../impl/infr/proto/ByteMessageProperty.java | 1 + .../bmq/impl/infr/proto/EventBuilder.java | 2 +- .../bmq/impl/infr/proto/EventHeader.java | 2 +- .../impl/infr/proto/Int32MessageProperty.java | 1 + .../impl/infr/proto/Int64MessageProperty.java | 1 + .../bmq/impl/infr/proto/MessageIterator.java | 6 +- .../infr/proto/MessagePropertiesHeader.java | 2 +- .../bmq/impl/infr/proto/MessageProperty.java | 2 +- .../bmq/impl/infr/proto/PushMessageImpl.java | 2 +- .../impl/infr/proto/PushMessageIterator.java | 1 + .../impl/infr/proto/ShortMessageProperty.java | 1 + .../infr/proto/StringMessageProperty.java | 1 + 20 files changed, 74 insertions(+), 61 deletions(-) diff --git a/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/msg/AuthenticationMessage.java b/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/msg/AuthenticationMessage.java index 3e23a12..b3d4d5d 100644 --- a/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/msg/AuthenticationMessage.java +++ b/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/msg/AuthenticationMessage.java @@ -15,7 +15,7 @@ */ package com.bloomberg.bmq.impl.infr.msg; -public class AuthenticationMessage { +public final class AuthenticationMessage { private AuthenticationRequest authenticationRequest; private AuthenticationResponse authenticationResponse; @@ -28,28 +28,28 @@ public Object createNewInstance() { return new AuthenticationMessage(); } - public final void reset() { + public void reset() { init(); } - public final void makeAuthenticationRequest(String mechanism, String data) { + public void makeAuthenticationRequest(String mechanism, String data) { reset(); authenticationRequest = new AuthenticationRequest(mechanism, data); } - public final boolean isAuthenticationRequestValue() { + public boolean isAuthenticationRequestValue() { return authenticationRequest != null; } - public final boolean isAuthenticationResponseValue() { + public boolean isAuthenticationResponseValue() { return authenticationResponse != null; } - public final AuthenticationRequest authenticationRequest() { + public AuthenticationRequest authenticationRequest() { return authenticationRequest; } - public final AuthenticationResponse authenticationResponse() { + public AuthenticationResponse authenticationResponse() { return authenticationResponse; } @@ -58,7 +58,7 @@ public void init() { authenticationResponse = null; } - public final void reset(AuthenticationMessage copied) { + public void reset(AuthenticationMessage copied) { authenticationRequest = copied.authenticationRequest; authenticationResponse = copied.authenticationResponse; } diff --git a/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/msg/ControlMessageChoice.java b/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/msg/ControlMessageChoice.java index b93095c..7fc4017 100644 --- a/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/msg/ControlMessageChoice.java +++ b/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/msg/ControlMessageChoice.java @@ -15,7 +15,7 @@ */ package com.bloomberg.bmq.impl.infr.msg; -public class ControlMessageChoice { +public final class ControlMessageChoice { private Integer rId; private Status status; @@ -40,150 +40,150 @@ public Object createNewInstance() { return new ControlMessageChoice(); } - public final void reset() { + public void reset() { init(); } - public final void makeStatus() { + public void makeStatus() { reset(); status = new Status(); } - public final void makeOpenQueue() { + public void makeOpenQueue() { reset(); openQueue = new OpenQueue(); } - public final void makeOpenQueueResponse() { + public void makeOpenQueueResponse() { reset(); openQueueResponse = new OpenQueueResponse(); } - public final void makeConfigureQueueStream() { + public void makeConfigureQueueStream() { reset(); configureQueueStream = new ConfigureQueueStream(); } - public final void makeConfigureQueueStreamResponse() { + public void makeConfigureQueueStreamResponse() { reset(); configureQueueStreamResponse = new ConfigureQueueStreamResponse(); } - public final void makeConfigureStream() { + public void makeConfigureStream() { reset(); configureStream = new ConfigureStream(); } - public final void makeConfigureStreamResponse() { + public void makeConfigureStreamResponse() { reset(); configureStreamResponse = new ConfigureStreamResponse(); } - public final void makeCloseQueue() { + public void makeCloseQueue() { reset(); closeQueue = new CloseQueue(); } - public final void makeCloseQueueResponse() { + public void makeCloseQueueResponse() { reset(); closeQueueResponse = new CloseQueueResponse(); } - public final void makeDisconnect() { + public void makeDisconnect() { reset(); disconnect = new Disconnect(); } - public final void makeDisconnectResponse() { + public void makeDisconnectResponse() { reset(); disconnectResponse = new DisconnectResponse(); } - public final boolean isStatusValue() { + public boolean isStatusValue() { return status != null; } - public final boolean isOpenQueueValue() { + public boolean isOpenQueueValue() { return openQueue != null; } - public final boolean isOpenQueueResponseValue() { + public boolean isOpenQueueResponseValue() { return openQueueResponse != null; } - public final boolean isDisconnectValue() { + public boolean isDisconnectValue() { return disconnect != null; } - public final boolean isDisconnectResponseValue() { + public boolean isDisconnectResponseValue() { return disconnectResponse != null; } - public final boolean isConfigureQueueStreamValue() { + public boolean isConfigureQueueStreamValue() { return configureQueueStream != null; } - public final boolean isConfigureQueueStreamResponseValue() { + public boolean isConfigureQueueStreamResponseValue() { return configureQueueStreamResponse != null; } - public final boolean isConfigureStreamValue() { + public boolean isConfigureStreamValue() { return configureStream != null; } - public final boolean isConfigureStreamResponseValue() { + public boolean isConfigureStreamResponseValue() { return configureStreamResponse != null; } - public final boolean isCloseQueueValue() { + public boolean isCloseQueueValue() { return closeQueue != null; } - public final boolean isCloseQueueResponseValue() { + public boolean isCloseQueueResponseValue() { return closeQueueResponse != null; } - public final Status status() { + public Status status() { return status; } - public final OpenQueue openQueue() { + public OpenQueue openQueue() { return openQueue; } - public final OpenQueueResponse openQueueResponse() { + public OpenQueueResponse openQueueResponse() { return openQueueResponse; } - public final ConfigureQueueStream configureQueueStream() { + public ConfigureQueueStream configureQueueStream() { return configureQueueStream; } - public final ConfigureQueueStreamResponse configureQueueStreamResponse() { + public ConfigureQueueStreamResponse configureQueueStreamResponse() { return configureQueueStreamResponse; } - public final ConfigureStream configureStream() { + public ConfigureStream configureStream() { return configureStream; } - public final ConfigureStreamResponse configureStreamResponse() { + public ConfigureStreamResponse configureStreamResponse() { return configureStreamResponse; } - public final CloseQueue closeQueue() { + public CloseQueue closeQueue() { return closeQueue; } - public final CloseQueueResponse closeQueueResponse() { + public CloseQueueResponse closeQueueResponse() { return closeQueueResponse; } - public final Disconnect disconnect() { + public Disconnect disconnect() { return disconnect; } - public final DisconnectResponse disconnectResponse() { + public DisconnectResponse disconnectResponse() { return disconnectResponse; } diff --git a/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/msg/NegotiationMessageChoice.java b/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/msg/NegotiationMessageChoice.java index 62a553b..7c93037 100644 --- a/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/msg/NegotiationMessageChoice.java +++ b/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/msg/NegotiationMessageChoice.java @@ -15,7 +15,7 @@ */ package com.bloomberg.bmq.impl.infr.msg; -public class NegotiationMessageChoice { +public final class NegotiationMessageChoice { private ClientIdentity clientIdentity; private BrokerResponse brokerResponse; @@ -28,33 +28,33 @@ public Object createNewInstance() { return new NegotiationMessageChoice(); } - public final void reset() { + public void reset() { init(); } - public final void makeClientIdentity() { + public void makeClientIdentity() { reset(); clientIdentity = new ClientIdentity(); } - public final void makeBrokerResponse() { + public void makeBrokerResponse() { reset(); brokerResponse = new BrokerResponse(); } - public final boolean isClientIdentityValue() { + public boolean isClientIdentityValue() { return clientIdentity != null; } - public final boolean isBrokerResponseValue() { + public boolean isBrokerResponseValue() { return brokerResponse != null; } - public final ClientIdentity clientIdentity() { + public ClientIdentity clientIdentity() { return clientIdentity; } - public final BrokerResponse brokerResponse() { + public BrokerResponse brokerResponse() { return brokerResponse; } @@ -63,7 +63,7 @@ public final void init() { brokerResponse = null; } - public final void reset(NegotiationMessageChoice copied) { + public void reset(NegotiationMessageChoice copied) { clientIdentity = copied.clientIdentity; brokerResponse = copied.brokerResponse; } diff --git a/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/AckEventImpl.java b/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/AckEventImpl.java index c059145..225bc20 100644 --- a/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/AckEventImpl.java +++ b/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/AckEventImpl.java @@ -26,6 +26,9 @@ public class AckEventImpl extends EventImpl { final AckHeader header; final Collection messages = new ArrayList<>(); + @SuppressWarnings( + "this-escape") // passing `this` to `AckMessageIterator` is necessary to fully construct + // `this` public AckEventImpl(ByteBuffer[] bbuf) { super(EventType.ACK, bbuf); AckMessageIterator it = new AckMessageIterator(this); diff --git a/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/AckMessageImpl.java b/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/AckMessageImpl.java index 9d04c49..c1997e0 100644 --- a/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/AckMessageImpl.java +++ b/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/AckMessageImpl.java @@ -25,7 +25,7 @@ import com.bloomberg.bmq.impl.infr.util.BitUtil; import java.io.IOException; -public class AckMessageImpl implements Streamable { +public final class AckMessageImpl implements Streamable { // This class defines the (repeated) payload following the 'AckHeader' // struct. diff --git a/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/AckMessageIterator.java b/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/AckMessageIterator.java index 45debf8..e38f606 100644 --- a/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/AckMessageIterator.java +++ b/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/AckMessageIterator.java @@ -28,6 +28,8 @@ public final class AckMessageIterator extends MessageIterator implements Iterato private AckHeader header; + @SuppressWarnings( + "this-escape") // isValid() and event() are `final` and call no non-final methods public AckMessageIterator(EventImpl ev) { super(ev); if (isValid()) { diff --git a/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/BinaryMessageProperty.java b/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/BinaryMessageProperty.java index 9c7a981..90d74d5 100644 --- a/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/BinaryMessageProperty.java +++ b/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/BinaryMessageProperty.java @@ -21,6 +21,7 @@ public BinaryMessageProperty() { super(PropertyType.BINARY, MessagePropertyHeader.MAX_PROPERTY_VALUE_LENGTH); } + @SuppressWarnings("this-escape") // setPropertyValue() is `final` and calls no non-final methods public BinaryMessageProperty(byte[] val) { this(); setPropertyValue(val); diff --git a/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/BoolMessageProperty.java b/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/BoolMessageProperty.java index 8af2925..1351076 100644 --- a/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/BoolMessageProperty.java +++ b/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/BoolMessageProperty.java @@ -21,6 +21,7 @@ public BoolMessageProperty() { super(PropertyType.BOOL, Byte.SIZE); } + @SuppressWarnings("this-escape") // setPropertyValue() is `final` and calls no non-final methods public BoolMessageProperty(boolean val) { this(); byte[] b = {(byte) (val ? 1 : 0)}; diff --git a/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/ByteMessageProperty.java b/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/ByteMessageProperty.java index de9e1cb..376f95b 100644 --- a/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/ByteMessageProperty.java +++ b/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/ByteMessageProperty.java @@ -21,6 +21,7 @@ public ByteMessageProperty() { super(PropertyType.BYTE, Byte.SIZE); } + @SuppressWarnings("this-escape") // setPropertyValue() is `final` and calls no non-final methods public ByteMessageProperty(byte val) { this(); byte[] b = {val}; diff --git a/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/EventBuilder.java b/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/EventBuilder.java index c7eb692..405bc33 100644 --- a/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/EventBuilder.java +++ b/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/EventBuilder.java @@ -29,7 +29,7 @@ protected EventBuilder(EventType type) { reset(type); } - public void reset(EventType type) { + public final void reset(EventType type) { eventHeader = new EventHeader(); eventHeader.setType(type); msgCount = 0; diff --git a/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/EventHeader.java b/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/EventHeader.java index 4a151db..792a62e 100644 --- a/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/EventHeader.java +++ b/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/EventHeader.java @@ -24,7 +24,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class EventHeader { +public final class EventHeader { static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); // This class represents the header for all the events received by the diff --git a/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/Int32MessageProperty.java b/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/Int32MessageProperty.java index 97cfcc9..8b796ed 100644 --- a/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/Int32MessageProperty.java +++ b/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/Int32MessageProperty.java @@ -23,6 +23,7 @@ public Int32MessageProperty() { super(PropertyType.INT32, Integer.SIZE); } + @SuppressWarnings("this-escape") // setPropertyValue() is `final` and calls no non-final methods public Int32MessageProperty(int val) { this(); ByteBuffer buffer = ByteBuffer.allocate(Integer.BYTES); diff --git a/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/Int64MessageProperty.java b/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/Int64MessageProperty.java index 7228f3f..c6c4647 100644 --- a/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/Int64MessageProperty.java +++ b/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/Int64MessageProperty.java @@ -23,6 +23,7 @@ public Int64MessageProperty() { super(PropertyType.INT64, Long.SIZE); } + @SuppressWarnings("this-escape") // setPropertyValue() is `final` and calls no non-final methods public Int64MessageProperty(long val) { this(); ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES); diff --git a/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/MessageIterator.java b/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/MessageIterator.java index 1a8140c..9332ab6 100644 --- a/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/MessageIterator.java +++ b/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/MessageIterator.java @@ -39,11 +39,11 @@ protected MessageIterator(EventImpl ev) { } } - protected EventImpl event() { + protected final EventImpl event() { return event; } - protected T fetchNextMessage(T message) { + protected final T fetchNextMessage(T message) { Argument.expectNonNull(message, "message"); try { if (currentPosition > 0) { @@ -65,7 +65,7 @@ protected T fetchNextMessage(T message) { } } - public boolean isValid() { + public final boolean isValid() { return event != null; } } diff --git a/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/MessagePropertiesHeader.java b/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/MessagePropertiesHeader.java index 4fcca7f..e31cf68 100644 --- a/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/MessagePropertiesHeader.java +++ b/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/MessagePropertiesHeader.java @@ -21,7 +21,7 @@ import java.io.DataOutput; import java.io.IOException; -public class MessagePropertiesHeader { +public final class MessagePropertiesHeader { // This class represents the header for message properties area in a PUT // or PUSH message. This header will be followed by one or more message // properties. diff --git a/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/MessageProperty.java b/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/MessageProperty.java index 7eb924a..19146d9 100644 --- a/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/MessageProperty.java +++ b/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/MessageProperty.java @@ -34,7 +34,7 @@ public void setPropertyName(String name) { propertyName = name; } - public void setPropertyValue(byte[] val) { + public final void setPropertyValue(byte[] val) { Argument.expectNonNull(val, "val"); Argument.expectNotGreater(val.length, maxValueSize, "property length"); propertyValue = Arrays.copyOf(val, val.length); diff --git a/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/PushMessageImpl.java b/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/PushMessageImpl.java index ce0e71c..f929b04 100644 --- a/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/PushMessageImpl.java +++ b/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/PushMessageImpl.java @@ -26,7 +26,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class PushMessageImpl implements Streamable { +public final class PushMessageImpl implements Streamable { static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); diff --git a/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/PushMessageIterator.java b/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/PushMessageIterator.java index 85e3b91..dd9df12 100644 --- a/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/PushMessageIterator.java +++ b/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/PushMessageIterator.java @@ -28,6 +28,7 @@ public final class PushMessageIterator extends MessageIterator private PushMessageImpl message; private PushMessageImpl nextMessage; + @SuppressWarnings("this-escape") // fetchNextMessage() is `final` and calls no non-final methods public PushMessageIterator(EventImpl ev) { super(ev); message = null; diff --git a/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/ShortMessageProperty.java b/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/ShortMessageProperty.java index 3dae80c..9fc508a 100644 --- a/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/ShortMessageProperty.java +++ b/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/ShortMessageProperty.java @@ -23,6 +23,7 @@ public ShortMessageProperty() { super(PropertyType.SHORT, Short.SIZE); } + @SuppressWarnings("this-escape") // setPropertyValue() is `final` and calls no non-final methods public ShortMessageProperty(short val) { this(); ByteBuffer buffer = ByteBuffer.allocate(Short.BYTES); diff --git a/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/StringMessageProperty.java b/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/StringMessageProperty.java index 0e10331..9aac244 100644 --- a/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/StringMessageProperty.java +++ b/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/StringMessageProperty.java @@ -28,6 +28,7 @@ public StringMessageProperty() { super(PropertyType.STRING, MessagePropertyHeader.MAX_PROPERTY_VALUE_LENGTH); } + @SuppressWarnings("this-escape") // setPropertyValue() is `final` and calls no non-final methods public StringMessageProperty(String val) { this(); setPropertyValue(val.getBytes(StandardCharsets.US_ASCII)); From c134dd2463352c396f03e394421f5b8a81fc6173 Mon Sep 17 00:00:00 2001 From: "Patrick M. Niedzielski" Date: Wed, 3 Dec 2025 12:13:19 -0500 Subject: [PATCH 4/5] Fix: avoid implicit cast from `long` to `int` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Compiling on modern JDKs results in the following warning: [WARNING] blazingmq-sdk-java/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/MessagePropertiesImpl.java:[234,24] implicit cast from long to int in compound assignment is possibly lossy This is fired on the line: // Add padding bytes totalLength += numPaddingBytes; where `totalLength` is an `int` and `numPaddingBytes` is a `long`. However, `numPaddingBytes` is initialized few lines above as // Read padding bytes final long numPaddingBytes = input.readByte(); Since `readByte()` returns a `byte`, there’s no reason for this constant to be `long`. This patch fixes the above warning by avoiding the needless conversion to `long`. Signed-off-by: Patrick M. Niedzielski --- .../bloomberg/bmq/impl/infr/proto/MessagePropertiesImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/MessagePropertiesImpl.java b/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/MessagePropertiesImpl.java index 86da37b..00a1ca0 100644 --- a/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/MessagePropertiesImpl.java +++ b/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/MessagePropertiesImpl.java @@ -216,7 +216,7 @@ public int streamInOld(T input) throws IOExc } // Read padding bytes - final int numPaddingBytes = input.readByte(); + final byte numPaddingBytes = input.readByte(); // Skip padding bytes if (input.skip(numPaddingBytes - 1) != numPaddingBytes - 1) { From 961742c86bdb60fbf69e769147b04751ff898178 Mon Sep 17 00:00:00 2001 From: "Patrick M. Niedzielski" Date: Tue, 8 Sep 2026 17:22:38 -0400 Subject: [PATCH 5/5] Fix: Update `google-java-format` version A newer version of `google-java-format` is required for newer JVMs. Unfortunately, there is no one version that supports both JVM 17 and JVM 25. As long as we support JVM 17, we need a conditional pin. This patch bumps the version conditionally, and reformats files accordingly. Signed-off-by: Patrick M. Niedzielski --- .../bmq/impl/CloseQueueStrategy.java | 1 + .../bmq/impl/infr/net/intf/TcpConnection.java | 1 + .../bmq/impl/infr/proto/AckHeader.java | 1 + .../bmq/impl/infr/proto/AckMessageImpl.java | 1 + .../bmq/impl/infr/proto/ConfirmHeader.java | 1 + .../bmq/impl/infr/proto/ConfirmMessage.java | 1 + .../bmq/impl/infr/proto/OptionHeader.java | 1 + .../bmq/impl/infr/proto/PushHeader.java | 1 + .../bmq/impl/infr/proto/PutHeader.java | 1 + pom.xml | 22 ++++++++++++++++++- 10 files changed, 30 insertions(+), 1 deletion(-) diff --git a/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/CloseQueueStrategy.java b/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/CloseQueueStrategy.java index f767bc5..2cbe8bf 100644 --- a/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/CloseQueueStrategy.java +++ b/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/CloseQueueStrategy.java @@ -45,6 +45,7 @@ public enum Scenario { // response during opening sequence LATE_ONE_STEP_CLOSING; + // reaction on late incoming open queue // response during opening sequence or // configure queue response during closing diff --git a/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/net/intf/TcpConnection.java b/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/net/intf/TcpConnection.java index 1a1e67a..ba8619e 100644 --- a/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/net/intf/TcpConnection.java +++ b/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/net/intf/TcpConnection.java @@ -86,6 +86,7 @@ int connect( boolean isWritable(); void waitUntilWritable(); + // Cannot be invoked from I/O thread. InetSocketAddress localAddress(); diff --git a/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/AckHeader.java b/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/AckHeader.java index d8ac63b..58b7675 100644 --- a/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/AckHeader.java +++ b/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/AckHeader.java @@ -87,6 +87,7 @@ public final class AckHeader implements Streamable { // Maximum size (bytes) of an 'AckMessageImpl'. public static final int MIN_HEADER_SIZE = 1; + // Minimum size (bytes) of a 'AckHeader' (which is sufficient to // capture header words). This value should *never* change. diff --git a/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/AckMessageImpl.java b/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/AckMessageImpl.java index c1997e0..f625915 100644 --- a/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/AckMessageImpl.java +++ b/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/AckMessageImpl.java @@ -86,6 +86,7 @@ public final class AckMessageImpl implements Streamable { // Constant to indicate no correlation Id. public static final int MESSAGE_SIZE = 24; + // Current size (bytes) of the message. public AckMessageImpl() { diff --git a/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/ConfirmHeader.java b/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/ConfirmHeader.java index 146abf7..ca367ff 100644 --- a/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/ConfirmHeader.java +++ b/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/ConfirmHeader.java @@ -77,6 +77,7 @@ public final class ConfirmHeader implements Streamable { // Maximum size (bytes) of an 'ConfirmMessage'. public static final int MIN_HEADER_SIZE = 1; + // Minimum size (bytes) of a 'ConfirmHeader' (which is sufficient to // capture header words). This value should *never* change. diff --git a/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/ConfirmMessage.java b/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/ConfirmMessage.java index eeb3408..c925822 100644 --- a/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/ConfirmMessage.java +++ b/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/ConfirmMessage.java @@ -57,6 +57,7 @@ public class ConfirmMessage implements Streamable { private int subQueueId; public static final int MESSAGE_SIZE = 24; + // Current size (bytes) of the message. public ConfirmMessage() { diff --git a/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/OptionHeader.java b/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/OptionHeader.java index 0ed4078..0cdb472 100644 --- a/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/OptionHeader.java +++ b/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/OptionHeader.java @@ -121,6 +121,7 @@ public final class OptionHeader implements Streamable { // (including this OptionHeader). public static final int HEADER_SIZE = 4; + // Current size (bytes) of the header. public OptionHeader() { diff --git a/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/PushHeader.java b/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/PushHeader.java index cde2350..3055a71 100644 --- a/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/PushHeader.java +++ b/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/PushHeader.java @@ -149,6 +149,7 @@ public final class PushHeader { // TODO: set to 32 after 2nd release of "new style" brokers public static final int HEADER_SIZE_FOR_SCHEMA_ID = 32; + // Current size (bytes) of the header with schema id // TODO: remove after 2nd release of "new style" brokers diff --git a/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/PutHeader.java b/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/PutHeader.java index 0a63a9f..b2e776a 100644 --- a/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/PutHeader.java +++ b/bmq-sdk/src/main/java/com/bloomberg/bmq/impl/infr/proto/PutHeader.java @@ -166,6 +166,7 @@ public final class PutHeader { public static final int MAX_CORRELATION_ID = (1 << CORRELATION_ID_NUM_BITS) - 1; public static final int HEADER_SIZE = 36; + // Current size (bytes) of the header. public PutHeader() { diff --git a/pom.xml b/pom.xml index 6082f2b..b11ee66 100644 --- a/pom.xml +++ b/pom.xml @@ -87,6 +87,13 @@ limitations under the License. --> 17 + + 1.30.0 + false @@ -384,7 +391,7 @@ limitations under the License. --> src/main/java/com/bloomberg/bmq/impl/infr/util/expressionvalidator/ExpressionScanner.java - 1.16.0 + ${google-java-format.version} @@ -537,6 +544,19 @@ limitations under the License. --> + + JDK17 + + [17,18) + + + + 1.28.0 + + + ossrh