diff --git a/.github/workflows/ci-quick.yml b/.github/workflows/ci-quick.yml
new file mode 100644
index 00000000000..4cd613f988f
--- /dev/null
+++ b/.github/workflows/ci-quick.yml
@@ -0,0 +1,101 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+#
+# This is our most used build and the one used when a new PR is created/updated. It needs to
+# be and remain as green as possible. It is also executed when a PR is actually merged to make sure
+# apache/main stays stable. It runs a subset of the tests (the ones in the quick profile) to make sure it finishes
+# in a reasonable time.
+#
+name: CI Quick
+
+on:
+ push:
+ branches: [ "main", "activemq-6.2.x", "activemq-5.19.x" ]
+ pull_request:
+ branches: [ "main", "activemq-6.2.x", "activemq-5.19.x" ]
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
+jobs:
+ build:
+ name: build
+
+ permissions:
+ contents: read
+
+ timeout-minutes: 60
+
+ strategy:
+ matrix:
+ os: [ ubuntu-24.04, macos-26, windows-2025 ]
+ java-version: [ 11 ]
+
+ runs-on: ${{ matrix.os }}
+
+ steps:
+ - uses: actions/checkout@v6
+ - name: Set up JDK
+ uses: actions/setup-java@v5
+ with:
+ java-version: ${{ matrix.java-version }}
+ distribution: temurin
+ cache: 'maven'
+ - name: Build
+ run: mvn -U -B -e clean install -DskipTests
+ - name: Verify
+ run: mvn apache-rat:check
+
+ test:
+ name: test
+ needs: build
+
+ permissions:
+ contents: read
+ checks: write
+ pull-requests: write
+
+ timeout-minutes: 180
+
+ runs-on: ubuntu-24.04
+
+ steps:
+ - uses: actions/checkout@v6
+ - name: Set up JDK
+ uses: actions/setup-java@v5
+ with:
+ java-version: 11
+ distribution: temurin
+ cache: 'maven'
+ - name: Test
+ run: mvn -B -e -fae verify -Pactivemq.tests-quick -Dsurefire.rerunFailingTestsCount=3
+ - name: Upload Test Results
+ if: always()
+ uses: actions/upload-artifact@v7
+ with:
+ name: test-results
+ path: '**/target/surefire-reports/*.xml'
+ - name: Publish Test Results
+ if: always()
+ uses: EnricoMi/publish-unit-test-result-action@v2
+ with:
+ large_files: true
+ report_individual_runs: true
+ report_suite_logs: error
+ files: '**/target/surefire-reports/*.xml'
diff --git a/activemq-all/pom.xml b/activemq-all/pom.xml
index a42780bb8a9..ab3b6395735 100644
--- a/activemq-all/pom.xml
+++ b/activemq-all/pom.xml
@@ -14,7 +14,7 @@
org.apache.activemq
activemq-parent
- 5.19.7-TT.2-SNAPSHOT
+ 5.19.8-TT.1-SNAPSHOT
activemq-all
diff --git a/activemq-amqp/pom.xml b/activemq-amqp/pom.xml
index 50834e0b024..88f4793d7da 100644
--- a/activemq-amqp/pom.xml
+++ b/activemq-amqp/pom.xml
@@ -22,7 +22,7 @@
org.apache.activemq
activemq-parent
- 5.19.7-TT.2-SNAPSHOT
+ 5.19.8-TT.1-SNAPSHOT
activemq-amqp
diff --git a/activemq-blueprint/pom.xml b/activemq-blueprint/pom.xml
index 61a8412da22..e574cb23fb7 100644
--- a/activemq-blueprint/pom.xml
+++ b/activemq-blueprint/pom.xml
@@ -22,7 +22,7 @@
org.apache.activemq
activemq-parent
- 5.19.7-TT.2-SNAPSHOT
+ 5.19.8-TT.1-SNAPSHOT
activemq-blueprint
diff --git a/activemq-broker/pom.xml b/activemq-broker/pom.xml
index d312a33fc9a..34c95219865 100644
--- a/activemq-broker/pom.xml
+++ b/activemq-broker/pom.xml
@@ -22,7 +22,7 @@
org.apache.activemq
activemq-parent
- 5.19.7-TT.2-SNAPSHOT
+ 5.19.8-TT.1-SNAPSHOT
activemq-broker
diff --git a/activemq-broker/src/main/java/org/apache/activemq/broker/TransportConnection.java b/activemq-broker/src/main/java/org/apache/activemq/broker/TransportConnection.java
index ed2fd1f376e..2ede7a547d1 100644
--- a/activemq-broker/src/main/java/org/apache/activemq/broker/TransportConnection.java
+++ b/activemq-broker/src/main/java/org/apache/activemq/broker/TransportConnection.java
@@ -26,6 +26,7 @@
import java.util.List;
import java.util.Map;
import java.util.Properties;
+import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CountDownLatch;
@@ -163,6 +164,7 @@ public class TransportConnection implements Connection, Task, CommandVisitor {
private final ReentrantReadWriteLock serviceLock = new ReentrantReadWriteLock();
private String duplexNetworkConnectorId;
private final long connectedTimestamp;
+ private final CompletableFuture initialConnectionId = new CompletableFuture<>();
/**
* @param taskRunnerFactory - can be null if you want direct dispatch to the transport
@@ -851,11 +853,16 @@ public Response processAddConnection(ConnectionInfo info) throws Exception {
try {
broker.addConnection(context, info);
+ // Complete the future with the connectionId if we completed
+ // the broker.addConnection() chain successfully
+ initialConnectionId.complete(info.getConnectionId());
} catch (Exception e) {
synchronized (brokerConnectionStates) {
brokerConnectionStates.remove(info.getConnectionId());
}
unregisterConnectionState(info.getConnectionId());
+ // complete with the exception
+ initialConnectionId.completeExceptionally(e);
LOG.warn("Failed to add Connection id={}, clientId={}, clientIP={} due to {}",
info.getConnectionId(), clientId, info.getClientIp(), e.getLocalizedMessage());
//AMQ-6561 - stop for all exceptions on addConnection
@@ -1387,13 +1394,10 @@ public Response processBrokerInfo(BrokerInfo info) {
LOG.error(" Slave Brokers are no longer supported - slave trying to attach is: {}", info.getBrokerName());
} else if (info.isNetworkConnection() && !info.isDuplexConnection()) {
try {
- NetworkBridgeConfiguration config = getNetworkConfiguration(info);
- if (config.isSyncDurableSubs() && protocolVersion.get() >= CommandTypes.PROTOCOL_VERSION_DURABLE_SYNC) {
- LOG.debug("SyncDurableSubs is enabled, Sending BrokerSubscriptionInfo");
- dispatchSync(NetworkBridgeUtils.getBrokerSubscriptionInfo(this.broker.getBrokerService(), config));
- }
+ // register durable sync to be sent after ConnectionInfo has been handled
+ registerDurableSync(getNetworkConfiguration(info), info);
} catch (Exception e) {
- LOG.error("Failed to respond to network bridge creation from broker {}", info.getBrokerId(), e);
+ LOG.error("Failed to register durable sync for network bridge creation from broker {}", info.getBrokerId(), e);
return null;
}
} else if (info.isNetworkConnection() && info.isDuplexConnection()) {
@@ -1403,10 +1407,8 @@ public Response processBrokerInfo(BrokerInfo info) {
NetworkBridgeConfiguration config = getNetworkConfiguration(info);
config.setBrokerName(broker.getBrokerName());
- if (config.isSyncDurableSubs() && protocolVersion.get() >= CommandTypes.PROTOCOL_VERSION_DURABLE_SYNC) {
- LOG.debug("SyncDurableSubs is enabled, Sending BrokerSubscriptionInfo");
- dispatchSync(NetworkBridgeUtils.getBrokerSubscriptionInfo(this.broker.getBrokerService(), config));
- }
+ // register durable sync to be sent after ConnectionInfo has been handled
+ registerDurableSync(config, info);
// check for existing duplex connection hanging about
@@ -1473,6 +1475,30 @@ public Response processBrokerInfo(BrokerInfo info) {
return null;
}
+ private void registerDurableSync(final NetworkBridgeConfiguration config, final BrokerInfo info) {
+ if (config.isSyncDurableSubs() && protocolVersion.get() >= CommandTypes.PROTOCOL_VERSION_DURABLE_SYNC) {
+ // this will complete when the connection id has been set, or immediately if already set
+ initialConnectionId.whenComplete((connectionId, t) -> {
+ try {
+ if (t != null) {
+ LOG.warn("SyncDurableSubs will be skipped due to error {}",
+ t.getMessage());
+ return;
+ }
+ // check connection still registered
+ if (lookupConnectionState(connectionId) != null) {
+ LOG.debug("SyncDurableSubs is enabled, Sending BrokerSubscriptionInfo");
+ dispatchSync(NetworkBridgeUtils.getBrokerSubscriptionInfo(
+ this.broker.getBrokerService(), config));
+ }
+ } catch (Exception e) {
+ LOG.error("Failed to respond to network bridge creation from broker {}",
+ info.getBrokerId(), e);
+ }
+ });
+ }
+ }
+
@SuppressWarnings({"unchecked", "rawtypes"})
private HashMap createMap(Properties properties) {
return new HashMap(properties);
diff --git a/activemq-broker/src/main/java/org/apache/activemq/broker/jmx/BrokerView.java b/activemq-broker/src/main/java/org/apache/activemq/broker/jmx/BrokerView.java
index ad81f015832..b5530a63a2c 100644
--- a/activemq-broker/src/main/java/org/apache/activemq/broker/jmx/BrokerView.java
+++ b/activemq-broker/src/main/java/org/apache/activemq/broker/jmx/BrokerView.java
@@ -46,7 +46,7 @@ public class BrokerView implements BrokerViewMBean {
public static final Set DENIED_TRANSPORT_SCHEMES = Set.of("vm", "http",
"multicast", "zeroconf", "discovery", "fanout", "mock", "peer", "failover",
- "proxy", "reliable", "simple", "udp");
+ "proxy", "reliable", "simple", "udp", "masterslave");
ManagedRegionBroker broker;
@@ -574,26 +574,39 @@ private static void validateAllowedUrl(String uriString) throws URISyntaxExcepti
// Validate the URI does not contain a denied transport scheme
private static void validateAllowedUri(URI uri, int depth) throws URISyntaxException {
// Don't allow more than 5 nested URIs to prevent blowing the stack
- // If we are greater than 4 then this is the 5th level of composite
- if (depth > 4) {
+ if (depth > 5) {
throw new IllegalArgumentException("URI can't contain more than 5 nested composite URIs");
}
// First check the main URI scheme
validateAllowedScheme(uri.getScheme());
- // If composite, iterate and check each of the composite URIs
- if (URISupport.isCompositeURI(uri)) {
- URISupport.CompositeData data = URISupport.parseComposite(uri);
+ // We need to check if the URI is composite and/or contains nested URIs
+ // The utility method URISupport#isCompositeURI is not good enough here
+ // because it misses if there are no parentheses and also is primarily meant
+ // for checking comma separated URIs and not nested URIs.
+ //
+ // The best way to handle all cases is to use the same logic that the transports
+ // use to process the URIs and that is to simply attempt to parse it and check each
+ // of the parsed components. This wll correctly handle the case when there
+ // are parentheses and also when the parentheses are skipped.
+ final URISupport.CompositeData data;
+ try {
+ data = URISupport.parseComposite(uri);
+ } catch (URISyntaxException e) {
+ // If this is not a valid URI then we can stop checking
+ // This can happen when parsing a nested URI and at the last portion
+ return;
+ }
+
+ if (data.getComponents() != null) {
depth++;
for (URI component : data.getComponents()) {
- // Each URI could be a nested composite URI so call validateAllowedUri()
- // to validate it. This check if composite first so we don't add to
- // the recursive stack depth if there's a lot of URIs that are not composite
- if (URISupport.isCompositeURI(component)) {
+ // Each URI could be a nested and/or composite URI so call validateAllowedUri()
+ // to validate it. If the scheme is null then the original URI is not composite
+ // or nested so we can skip the check, and we are finished.
+ if (component.getScheme() != null) {
validateAllowedUri(component, depth);
- } else {
- validateAllowedScheme(component.getScheme());
}
}
}
diff --git a/activemq-broker/src/main/java/org/apache/activemq/security/AuthorizationBroker.java b/activemq-broker/src/main/java/org/apache/activemq/security/AuthorizationBroker.java
index 06eabd2584e..c65f9a38517 100644
--- a/activemq-broker/src/main/java/org/apache/activemq/security/AuthorizationBroker.java
+++ b/activemq-broker/src/main/java/org/apache/activemq/security/AuthorizationBroker.java
@@ -75,12 +75,21 @@ protected SecurityContext checkSecurityContext(ConnectionContext context) throws
return securityContext;
}
- protected boolean checkDestinationAdmin(SecurityContext securityContext, ActiveMQDestination destination) {
- Destination existing = this.getDestinationMap(destination).get(destination);
- if (existing != null) {
+ protected boolean checkDestinationAdminAdd(SecurityContext securityContext, ActiveMQDestination destination) {
+ if (this.getDestinationMap(destination).get(destination) != null) {
return true;
}
+ return checkDestinationAdmin(securityContext, destination);
+ }
+ protected boolean checkDestinationAdminRemove(SecurityContext securityContext, ActiveMQDestination destination) {
+ if (this.getDestinationMap(destination).get(destination) == null) {
+ return true;
+ }
+ return checkDestinationAdmin(securityContext, destination);
+ }
+
+ protected boolean checkDestinationAdmin(SecurityContext securityContext, ActiveMQDestination destination) {
if (!securityContext.isBrokerContext()) {
Set> allowedACLs = null;
if (!destination.isTemporary()) {
@@ -100,7 +109,7 @@ protected boolean checkDestinationAdmin(SecurityContext securityContext, ActiveM
public void addDestinationInfo(ConnectionContext context, DestinationInfo info) throws Exception {
final SecurityContext securityContext = checkSecurityContext(context);
- if (!checkDestinationAdmin(securityContext, info.getDestination())) {
+ if (!checkDestinationAdminAdd(securityContext, info.getDestination())) {
throw new SecurityException("User " + securityContext.getUserName() + " is not authorized to create: " + info.getDestination());
}
@@ -108,10 +117,10 @@ public void addDestinationInfo(ConnectionContext context, DestinationInfo info)
}
@Override
- public Destination addDestination(ConnectionContext context, ActiveMQDestination destination,boolean create) throws Exception {
+ public Destination addDestination(ConnectionContext context, ActiveMQDestination destination, boolean create) throws Exception {
final SecurityContext securityContext = checkSecurityContext(context);
- if (!checkDestinationAdmin(securityContext, destination)) {
+ if (!checkDestinationAdminAdd(securityContext, destination)) {
throw new SecurityException("User " + securityContext.getUserName() + " is not authorized to create: " + destination);
}
@@ -122,7 +131,7 @@ public Destination addDestination(ConnectionContext context, ActiveMQDestination
public void removeDestination(ConnectionContext context, ActiveMQDestination destination, long timeout) throws Exception {
final SecurityContext securityContext = checkSecurityContext(context);
- if (!checkDestinationAdmin(securityContext, destination)) {
+ if (!checkDestinationAdminRemove(securityContext, destination)) {
throw new SecurityException("User " + securityContext.getUserName() + " is not authorized to remove: " + destination);
}
@@ -135,7 +144,7 @@ public void removeDestination(ConnectionContext context, ActiveMQDestination des
public void removeDestinationInfo(ConnectionContext context, DestinationInfo info) throws Exception {
final SecurityContext securityContext = checkSecurityContext(context);
- if (!checkDestinationAdmin(securityContext, info.getDestination())) {
+ if (!checkDestinationAdminRemove(securityContext, info.getDestination())) {
throw new SecurityException("User " + securityContext.getUserName() + " is not authorized to remove: " + info.getDestination());
}
diff --git a/activemq-broker/src/main/java/org/apache/activemq/transport/vm/VMTransportFactory.java b/activemq-broker/src/main/java/org/apache/activemq/transport/vm/VMTransportFactory.java
index 56baaeec98f..63b94dac5d9 100644
--- a/activemq-broker/src/main/java/org/apache/activemq/transport/vm/VMTransportFactory.java
+++ b/activemq-broker/src/main/java/org/apache/activemq/transport/vm/VMTransportFactory.java
@@ -19,11 +19,14 @@
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
+import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
+import java.util.stream.Collectors;
import org.apache.activemq.broker.BrokerFactory;
import org.apache.activemq.broker.BrokerFactoryHandler;
import org.apache.activemq.broker.BrokerRegistry;
@@ -44,12 +47,29 @@
public class VMTransportFactory extends TransportFactory {
+ public static final String VM_TRANSPORT_FACTORY_SCHEMES_ENABLED_PROP =
+ "org.apache.activemq.transport.VM_TRANSPORT_FACTORY_SCHEMES_ENABLED";
+ public static final String DEFAULT_ALLOWED_SCHEMES = "broker,properties";
+
public static final ConcurrentMap BROKERS = new ConcurrentHashMap();
public static final ConcurrentMap CONNECTORS = new ConcurrentHashMap();
public static final ConcurrentMap SERVERS = new ConcurrentHashMap();
private static final Logger LOG = LoggerFactory.getLogger(VMTransportFactory.class);
BrokerFactoryHandler brokerFactoryHandler;
+ private final Set allowedSchemes;
+
+ public VMTransportFactory() {
+ final String allowedSchemes = System.getProperty(VM_TRANSPORT_FACTORY_SCHEMES_ENABLED_PROP,
+ DEFAULT_ALLOWED_SCHEMES);
+
+ // Asterisk will map to null which will allow all and skip checking
+ // Empty string will map to an empty set and will deny all
+ this.allowedSchemes = !allowedSchemes.equals("*") ?
+ Arrays.stream(allowedSchemes.split("\\s*,\\s*"))
+ .filter(s -> !s.isBlank())
+ .collect(Collectors.toUnmodifiableSet()) : null;
+ }
@Override
public Transport doConnect(URI location) throws Exception {
@@ -119,6 +139,7 @@ public Transport doCompositeConnect(URI location) throws Exception {
throw new IOException("Broker named '" + host + "' does not exist.");
}
try {
+ validateBrokerCreationSchema(host, brokerURI);
if (brokerFactoryHandler != null) {
broker = brokerFactoryHandler.createBroker(brokerURI);
} else {
@@ -162,6 +183,20 @@ public Transport doCompositeConnect(URI location) throws Exception {
return transport;
}
+ private void validateBrokerCreationSchema(String host, URI brokerURI) {
+ if (allowedSchemes != null) {
+ final String detectedScheme = brokerURI.getScheme();
+ if (detectedScheme == null) {
+ throw new IllegalArgumentException("Could not detect scheme in given URI [" + brokerURI + "]");
+ }
+ if (!allowedSchemes.contains(detectedScheme)){
+ throw new IllegalArgumentException("Broker named '" + host + "' does not exist and "
+ + "broker creation using the scheme '" + detectedScheme + "' is not enabled via the VMTransportFactory. "
+ + "To allow creation, configure the system property " + VM_TRANSPORT_FACTORY_SCHEMES_ENABLED_PROP);
+ }
+ }
+ }
+
private static String extractHost(URI location) {
String host = location.getHost();
if (host == null || host.length() == 0) {
diff --git a/activemq-cf/pom.xml b/activemq-cf/pom.xml
index 5dffd2da1d5..87877abc025 100644
--- a/activemq-cf/pom.xml
+++ b/activemq-cf/pom.xml
@@ -24,7 +24,7 @@
org.apache.activemq
activemq-parent
- 5.19.7-TT.2-SNAPSHOT
+ 5.19.8-TT.1-SNAPSHOT
activemq-cf
diff --git a/activemq-client-jakarta/pom.xml b/activemq-client-jakarta/pom.xml
index 026498d41da..5413b61758d 100644
--- a/activemq-client-jakarta/pom.xml
+++ b/activemq-client-jakarta/pom.xml
@@ -20,7 +20,7 @@
org.apache.activemq
activemq-parent
- 5.19.7-TT.2-SNAPSHOT
+ 5.19.8-TT.1-SNAPSHOT
activemq-client-jakarta
bundle
diff --git a/activemq-client/pom.xml b/activemq-client/pom.xml
index 5ed25b92515..5e735aef7a4 100644
--- a/activemq-client/pom.xml
+++ b/activemq-client/pom.xml
@@ -22,7 +22,7 @@
org.apache.activemq
activemq-parent
- 5.19.7-TT.2-SNAPSHOT
+ 5.19.8-TT.1-SNAPSHOT
activemq-client
diff --git a/activemq-client/src/main/java/org/apache/activemq/util/ClassLoadingAwareObjectInputStream.java b/activemq-client/src/main/java/org/apache/activemq/util/ClassLoadingAwareObjectInputStream.java
index 396b6502b7a..fe309990638 100644
--- a/activemq-client/src/main/java/org/apache/activemq/util/ClassLoadingAwareObjectInputStream.java
+++ b/activemq-client/src/main/java/org/apache/activemq/util/ClassLoadingAwareObjectInputStream.java
@@ -32,15 +32,21 @@ public class ClassLoadingAwareObjectInputStream extends ObjectInputStream {
private static final ClassLoader FALLBACK_CLASS_LOADER =
ClassLoadingAwareObjectInputStream.class.getClassLoader();
+ public static final Set> ALLOWED_JDK_TYPES = Set.of(
+ Boolean.class, Short.class, Integer.class, Long.class,
+ Float.class, Double.class, String.class, Character.class, Byte.class,
+ Throwable.class, Exception.class, StackTraceElement.class);
+
+ public static final String DEFAULT_SERIALIZABLE_PACKAGES = "org.apache.activemq,org.fusesource.hawtbuf,com.thoughtworks.xstream.mapper";
public static final String[] serializablePackages;
- private List trustedPackages = new ArrayList();
+ private List trustedPackages = new ArrayList<>();
private boolean trustAllPackages = false;
private final ClassLoader inLoader;
static {
- serializablePackages = System.getProperty("org.apache.activemq.SERIALIZABLE_PACKAGES","java.lang,org.apache.activemq,org.fusesource.hawtbuf,com.thoughtworks.xstream.mapper").split(",");
+ serializablePackages = System.getProperty("org.apache.activemq.SERIALIZABLE_PACKAGES", DEFAULT_SERIALIZABLE_PACKAGES).split(",");
}
public ClassLoadingAwareObjectInputStream(InputStream in) throws IOException {
@@ -98,7 +104,7 @@ private boolean trustAllPackages() {
}
private void checkSecurity(Class clazz) throws ClassNotFoundException {
- if (trustAllPackages() || clazz.isPrimitive()) {
+ if (trustAllPackages() || clazz.isPrimitive() || ALLOWED_JDK_TYPES.contains(clazz)) {
return;
}
diff --git a/activemq-client/src/main/java/org/apache/activemq/util/XStreamSupport.java b/activemq-client/src/main/java/org/apache/activemq/util/XStreamSupport.java
index 0fe4cfe90ff..50fdc9af6a0 100644
--- a/activemq-client/src/main/java/org/apache/activemq/util/XStreamSupport.java
+++ b/activemq-client/src/main/java/org/apache/activemq/util/XStreamSupport.java
@@ -27,13 +27,16 @@
public class XStreamSupport {
+ private static final Class>[] ALLOWED_JDK_TYPES =
+ ClassLoadingAwareObjectInputStream.ALLOWED_JDK_TYPES.toArray(new Class[0]);
+
public static XStream createXStream() {
XStream stream = new XStream();
stream.addPermission(NoTypePermission.NONE);
stream.addPermission(PrimitiveTypePermission.PRIMITIVES);
stream.allowTypeHierarchy(Collection.class);
stream.allowTypeHierarchy(Map.class);
- stream.allowTypes(new Class[]{String.class});
+ stream.allowTypes(ALLOWED_JDK_TYPES);
if (ClassLoadingAwareObjectInputStream.isAllAllowed()) {
stream.addPermission(AnyTypePermission.ANY);
} else {
diff --git a/activemq-client/src/test/java/org/apache/activemq/util/ClassLoadingAwareObjectInputStreamTest.java b/activemq-client/src/test/java/org/apache/activemq/util/ClassLoadingAwareObjectInputStreamTest.java
index 2e3e919ee43..ab256fb9d84 100644
--- a/activemq-client/src/test/java/org/apache/activemq/util/ClassLoadingAwareObjectInputStreamTest.java
+++ b/activemq-client/src/test/java/org/apache/activemq/util/ClassLoadingAwareObjectInputStreamTest.java
@@ -26,6 +26,7 @@
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.Arrays;
+import java.util.Set;
import java.util.UUID;
import java.util.Vector;
@@ -206,8 +207,23 @@ public void testPrimitveCharNotFiltered() throws Exception {
}
@Test
- public void testReadObjectStringNotFiltered() throws Exception {
- doTestReadObject(new String(name.getMethodName()), ACCEPTS_NONE_FILTER);
+ public void testReadObjectJdkTypesNotFiltered() throws Exception {
+ for (String filter : Set.of(ACCEPTS_ALL_FILTER, ACCEPTS_NONE_FILTER,
+ ClassLoadingAwareObjectInputStream.DEFAULT_SERIALIZABLE_PACKAGES)) {
+ doTestReadObject(Boolean.TRUE, filter);
+ doTestReadObject("test", filter);
+ doTestReadObject(Byte.valueOf("0"), filter);
+ doTestReadObject(Character.valueOf('a'), filter);
+ doTestReadObject(Integer.valueOf(100), filter);
+ doTestReadObject(Long.valueOf(0), filter);
+ doTestReadObject(Float.valueOf(0), filter);
+ doTestReadObject(Double.valueOf(0), filter);
+ }
+
+ // these also require collections classes in java util as well as StackTraceElement
+ // they also can't be compared for equality as they don't implement equals
+ doTestReadObject(new Exception(), "java.util", false);
+ doTestReadObject(new Throwable(), "java.util", false);
}
//----- Test that primitive arrays get past filters ----------------------//
@@ -429,6 +445,10 @@ public void testReadObjectFailsWithUnstrustedContentInTrustedType() throws Excep
//----- Internal methods -------------------------------------------------//
private void doTestReadObject(Object value, String filter) throws Exception {
+ doTestReadObject(value, filter, true);
+ }
+
+ private void doTestReadObject(Object value, String filter, boolean equalityCheck) throws Exception {
byte[] serialized = serializeObject(value);
try (ByteArrayInputStream input = new ByteArrayInputStream(serialized);
@@ -441,10 +461,12 @@ private void doTestReadObject(Object value, String filter) throws Exception {
Object result = reader.readObject();
assertNotNull(result);
assertEquals(value.getClass(), result.getClass());
- if (result.getClass().isArray()) {
- assertTrue(Arrays.deepEquals((Object[]) value, (Object[]) result));
- } else {
- assertEquals(value, result);
+ if (equalityCheck) {
+ if (result.getClass().isArray()) {
+ assertTrue(Arrays.deepEquals((Object[]) value, (Object[]) result));
+ } else {
+ assertEquals(value, result);
+ }
}
}
}
diff --git a/activemq-console/pom.xml b/activemq-console/pom.xml
index c2d3b66312a..f3dd6cf79a1 100644
--- a/activemq-console/pom.xml
+++ b/activemq-console/pom.xml
@@ -22,7 +22,7 @@
org.apache.activemq
activemq-parent
- 5.19.7-TT.2-SNAPSHOT
+ 5.19.8-TT.1-SNAPSHOT
activemq-console
diff --git a/activemq-http/pom.xml b/activemq-http/pom.xml
index e9d2d23abec..bc9458b716d 100644
--- a/activemq-http/pom.xml
+++ b/activemq-http/pom.xml
@@ -22,7 +22,7 @@
org.apache.activemq
activemq-parent
- 5.19.7-TT.2-SNAPSHOT
+ 5.19.8-TT.1-SNAPSHOT
activemq-http
diff --git a/activemq-jaas/pom.xml b/activemq-jaas/pom.xml
index 500cc1bba23..255bf8fa0cc 100644
--- a/activemq-jaas/pom.xml
+++ b/activemq-jaas/pom.xml
@@ -22,7 +22,7 @@
org.apache.activemq
activemq-parent
- 5.19.7-TT.2-SNAPSHOT
+ 5.19.8-TT.1-SNAPSHOT
activemq-jaas
diff --git a/activemq-jdbc-store/pom.xml b/activemq-jdbc-store/pom.xml
index b31151f3746..0beff98d4f8 100644
--- a/activemq-jdbc-store/pom.xml
+++ b/activemq-jdbc-store/pom.xml
@@ -22,7 +22,7 @@
org.apache.activemq
activemq-parent
- 5.19.7-TT.2-SNAPSHOT
+ 5.19.8-TT.1-SNAPSHOT
activemq-jdbc-store
diff --git a/activemq-jms-pool/pom.xml b/activemq-jms-pool/pom.xml
index 8d3ea4aa1d0..cda2d0c4d58 100644
--- a/activemq-jms-pool/pom.xml
+++ b/activemq-jms-pool/pom.xml
@@ -22,7 +22,7 @@
org.apache.activemq
activemq-parent
- 5.19.7-TT.2-SNAPSHOT
+ 5.19.8-TT.1-SNAPSHOT
activemq-jms-pool
diff --git a/activemq-kahadb-store/pom.xml b/activemq-kahadb-store/pom.xml
index 2b6190a1b1d..505394b1a27 100644
--- a/activemq-kahadb-store/pom.xml
+++ b/activemq-kahadb-store/pom.xml
@@ -22,7 +22,7 @@
org.apache.activemq
activemq-parent
- 5.19.7-TT.2-SNAPSHOT
+ 5.19.8-TT.1-SNAPSHOT
activemq-kahadb-store
diff --git a/activemq-karaf-itest/pom.xml b/activemq-karaf-itest/pom.xml
index 8225b51dc43..e40eea5ef51 100644
--- a/activemq-karaf-itest/pom.xml
+++ b/activemq-karaf-itest/pom.xml
@@ -22,7 +22,7 @@
org.apache.activemq
activemq-parent
- 5.19.7-TT.2-SNAPSHOT
+ 5.19.8-TT.1-SNAPSHOT
activemq-karaf-itest
diff --git a/activemq-karaf/pom.xml b/activemq-karaf/pom.xml
index 63680fca532..bcf871a80c6 100644
--- a/activemq-karaf/pom.xml
+++ b/activemq-karaf/pom.xml
@@ -22,7 +22,7 @@
org.apache.activemq
activemq-parent
- 5.19.7-TT.2-SNAPSHOT
+ 5.19.8-TT.1-SNAPSHOT
activemq-karaf
diff --git a/activemq-log4j-appender/pom.xml b/activemq-log4j-appender/pom.xml
index cca0b647f18..f895a00c068 100644
--- a/activemq-log4j-appender/pom.xml
+++ b/activemq-log4j-appender/pom.xml
@@ -22,7 +22,7 @@
org.apache.activemq
activemq-parent
- 5.19.7-TT.2-SNAPSHOT
+ 5.19.8-TT.1-SNAPSHOT
activemq-log4j-appender
diff --git a/activemq-mqtt/pom.xml b/activemq-mqtt/pom.xml
index d641e71b1d6..751fe023862 100644
--- a/activemq-mqtt/pom.xml
+++ b/activemq-mqtt/pom.xml
@@ -22,7 +22,7 @@
org.apache.activemq
activemq-parent
- 5.19.7-TT.2-SNAPSHOT
+ 5.19.8-TT.1-SNAPSHOT
activemq-mqtt
diff --git a/activemq-openwire-generator/pom.xml b/activemq-openwire-generator/pom.xml
index 63d048abf6c..4c9fba1157c 100644
--- a/activemq-openwire-generator/pom.xml
+++ b/activemq-openwire-generator/pom.xml
@@ -22,7 +22,7 @@
org.apache.activemq
activemq-parent
- 5.19.7-TT.2-SNAPSHOT
+ 5.19.8-TT.1-SNAPSHOT
activemq-openwire-generator
diff --git a/activemq-openwire-legacy/pom.xml b/activemq-openwire-legacy/pom.xml
index cff64ce2538..1eccf831e4d 100644
--- a/activemq-openwire-legacy/pom.xml
+++ b/activemq-openwire-legacy/pom.xml
@@ -22,7 +22,7 @@
org.apache.activemq
activemq-parent
- 5.19.7-TT.2-SNAPSHOT
+ 5.19.8-TT.1-SNAPSHOT
activemq-openwire-legacy
diff --git a/activemq-osgi/pom.xml b/activemq-osgi/pom.xml
index a04bd753270..8582b21d1f7 100644
--- a/activemq-osgi/pom.xml
+++ b/activemq-osgi/pom.xml
@@ -22,7 +22,7 @@
org.apache.activemq
activemq-parent
- 5.19.7-TT.2-SNAPSHOT
+ 5.19.8-TT.1-SNAPSHOT
activemq-osgi
diff --git a/activemq-partition/pom.xml b/activemq-partition/pom.xml
index f2283dd0b9d..1f3529ccc2a 100644
--- a/activemq-partition/pom.xml
+++ b/activemq-partition/pom.xml
@@ -22,7 +22,7 @@
org.apache.activemq
activemq-parent
- 5.19.7-TT.2-SNAPSHOT
+ 5.19.8-TT.1-SNAPSHOT
activemq-partition
diff --git a/activemq-pool/pom.xml b/activemq-pool/pom.xml
index bf8fe719706..c7069e10334 100644
--- a/activemq-pool/pom.xml
+++ b/activemq-pool/pom.xml
@@ -22,7 +22,7 @@
org.apache.activemq
activemq-parent
- 5.19.7-TT.2-SNAPSHOT
+ 5.19.8-TT.1-SNAPSHOT
activemq-pool
diff --git a/activemq-ra/pom.xml b/activemq-ra/pom.xml
index b245c60bdc6..e626cdf7944 100644
--- a/activemq-ra/pom.xml
+++ b/activemq-ra/pom.xml
@@ -22,7 +22,7 @@
org.apache.activemq
activemq-parent
- 5.19.7-TT.2-SNAPSHOT
+ 5.19.8-TT.1-SNAPSHOT
activemq-ra
diff --git a/activemq-rar/pom.xml b/activemq-rar/pom.xml
index 8a417be8792..c9c5a9c1fbd 100644
--- a/activemq-rar/pom.xml
+++ b/activemq-rar/pom.xml
@@ -22,7 +22,7 @@
org.apache.activemq
activemq-parent
- 5.19.7-TT.2-SNAPSHOT
+ 5.19.8-TT.1-SNAPSHOT
activemq-rar
diff --git a/activemq-run/pom.xml b/activemq-run/pom.xml
index 96b6180a9ac..776aaff52a6 100644
--- a/activemq-run/pom.xml
+++ b/activemq-run/pom.xml
@@ -22,7 +22,7 @@
org.apache.activemq
activemq-parent
- 5.19.7-TT.2-SNAPSHOT
+ 5.19.8-TT.1-SNAPSHOT
activemq-run
diff --git a/activemq-runtime-config/pom.xml b/activemq-runtime-config/pom.xml
index 1db2de38cd4..dce9674a1d7 100644
--- a/activemq-runtime-config/pom.xml
+++ b/activemq-runtime-config/pom.xml
@@ -22,7 +22,7 @@
org.apache.activemq
activemq-parent
- 5.19.7-TT.2-SNAPSHOT
+ 5.19.8-TT.1-SNAPSHOT
activemq-runtime-config
diff --git a/activemq-shiro/pom.xml b/activemq-shiro/pom.xml
index 1adb8338603..058d287f445 100644
--- a/activemq-shiro/pom.xml
+++ b/activemq-shiro/pom.xml
@@ -22,7 +22,7 @@
org.apache.activemq
activemq-parent
- 5.19.7-TT.2-SNAPSHOT
+ 5.19.8-TT.1-SNAPSHOT
activemq-shiro
diff --git a/activemq-spring/pom.xml b/activemq-spring/pom.xml
index cb57e930f4c..3f431b214c9 100644
--- a/activemq-spring/pom.xml
+++ b/activemq-spring/pom.xml
@@ -22,7 +22,7 @@
org.apache.activemq
activemq-parent
- 5.19.7-TT.2-SNAPSHOT
+ 5.19.8-TT.1-SNAPSHOT
activemq-spring
diff --git a/activemq-stomp/pom.xml b/activemq-stomp/pom.xml
index d1a0105b207..de60a193c1e 100644
--- a/activemq-stomp/pom.xml
+++ b/activemq-stomp/pom.xml
@@ -22,7 +22,7 @@
org.apache.activemq
activemq-parent
- 5.19.7-TT.2-SNAPSHOT
+ 5.19.8-TT.1-SNAPSHOT
activemq-stomp
diff --git a/activemq-stomp/src/test/java/org/apache/activemq/transport/stomp/StompTest.java b/activemq-stomp/src/test/java/org/apache/activemq/transport/stomp/StompTest.java
index 89f591f1f65..4142e1fd660 100644
--- a/activemq-stomp/src/test/java/org/apache/activemq/transport/stomp/StompTest.java
+++ b/activemq-stomp/src/test/java/org/apache/activemq/transport/stomp/StompTest.java
@@ -1167,6 +1167,62 @@ public void testTransformationSendJSONObject() throws Exception {
assertEquals("Dejan", object.getName());
}
+
+ @Test(timeout = 60000)
+ public void testTransformationReceiveXMLObjectDouble() throws Exception {
+ MessageConsumer consumer = session.createConsumer(queue);
+
+ String frame = "CONNECT\n" + "login:system\n" + "passcode:manager\n\n" + Stomp.NULL;
+ stompConnection.sendFrame(frame);
+
+ frame = stompConnection.receiveFrame();
+ assertTrue(frame.startsWith("CONNECTED"));
+
+ // Double should be allowed by default
+ frame = "SEND\n" + "destination:/queue/" + getQueueName() + "\n" +
+ "transformation:" + Stomp.Transformations.JMS_OBJECT_XML + "\n\n" +
+ "1.1" + Stomp.NULL;
+
+ stompConnection.sendFrame(frame);
+
+ Message message = consumer.receive(2500);
+ assertNotNull(message);
+
+ LOG.info("Broker sent: {}", message);
+
+ assertTrue(message instanceof ObjectMessage);
+ ObjectMessage objectMessage = (ObjectMessage)message;
+ Double object = (Double)objectMessage.getObject();
+ assertEquals(Double.valueOf(1.1), object);
+ }
+
+ @Test(timeout = 60000)
+ public void testTransformationSendXMLObjectNotAllowed() throws Exception {
+ MessageConsumer consumer = session.createConsumer(queue);
+
+ String frame = "CONNECT\n" + "login:system\n" + "passcode:manager\n\n" + Stomp.NULL;
+ stompConnection.sendFrame(frame);
+
+ frame = stompConnection.receiveFrame();
+ assertTrue(frame.startsWith("CONNECTED"));
+
+ // ProcessBuilder is not allowed by default so the conversion should fail and
+ // then fall back to using a TextMessage, as well as setting an error header
+ frame = "SEND\n" + "destination:/queue/" + getQueueName() + "\n" +
+ "transformation:" + Stomp.Transformations.JMS_OBJECT_XML + "\n\n" +
+ "id" + Stomp.NULL;
+
+ stompConnection.sendFrame(frame);
+
+ Message message = consumer.receive(2500);
+ assertNotNull(message);
+ LOG.info("Broker sent: {}", message);
+
+ // The message should be Text and marked with a transformation error header
+ assertTrue(message instanceof TextMessage);
+ assertEquals("java.lang.ProcessBuilder", message.getStringProperty("transformation-error"));
+ }
+
@Test(timeout = 60000)
public void testTransformationSubscribeXML() throws Exception {
diff --git a/activemq-tooling/activemq-junit/pom.xml b/activemq-tooling/activemq-junit/pom.xml
index 5fac700c003..ed6200aa63c 100644
--- a/activemq-tooling/activemq-junit/pom.xml
+++ b/activemq-tooling/activemq-junit/pom.xml
@@ -21,7 +21,7 @@
org.apache.activemq.tooling
activemq-tooling
- 5.19.7-TT.2-SNAPSHOT
+ 5.19.8-TT.1-SNAPSHOT
activemq-junit
diff --git a/activemq-tooling/activemq-maven-plugin/pom.xml b/activemq-tooling/activemq-maven-plugin/pom.xml
index bcab8e244a5..37a7939f4a6 100644
--- a/activemq-tooling/activemq-maven-plugin/pom.xml
+++ b/activemq-tooling/activemq-maven-plugin/pom.xml
@@ -21,7 +21,7 @@
org.apache.activemq.tooling
activemq-tooling
- 5.19.7-TT.2-SNAPSHOT
+ 5.19.8-TT.1-SNAPSHOT
activemq-maven-plugin
diff --git a/activemq-tooling/activemq-memtest-maven-plugin/pom.xml b/activemq-tooling/activemq-memtest-maven-plugin/pom.xml
index be109b28b7d..a2780d9b1f0 100644
--- a/activemq-tooling/activemq-memtest-maven-plugin/pom.xml
+++ b/activemq-tooling/activemq-memtest-maven-plugin/pom.xml
@@ -22,7 +22,7 @@
org.apache.activemq.tooling
activemq-tooling
- 5.19.7-TT.2-SNAPSHOT
+ 5.19.8-TT.1-SNAPSHOT
activemq-memtest-maven-plugin
diff --git a/activemq-tooling/activemq-perf-maven-plugin/pom.xml b/activemq-tooling/activemq-perf-maven-plugin/pom.xml
index 8dc5166faf4..a5fbab64ce3 100644
--- a/activemq-tooling/activemq-perf-maven-plugin/pom.xml
+++ b/activemq-tooling/activemq-perf-maven-plugin/pom.xml
@@ -21,7 +21,7 @@
org.apache.activemq.tooling
activemq-tooling
- 5.19.7-TT.2-SNAPSHOT
+ 5.19.8-TT.1-SNAPSHOT
activemq-perf-maven-plugin
diff --git a/activemq-tooling/pom.xml b/activemq-tooling/pom.xml
index b0f699920f1..0aea2f8a93e 100644
--- a/activemq-tooling/pom.xml
+++ b/activemq-tooling/pom.xml
@@ -22,7 +22,7 @@
org.apache.activemq
activemq-parent
- 5.19.7-TT.2-SNAPSHOT
+ 5.19.8-TT.1-SNAPSHOT
org.apache.activemq.tooling
diff --git a/activemq-unit-tests/pom.xml b/activemq-unit-tests/pom.xml
index 1265166627e..aa447ec816a 100644
--- a/activemq-unit-tests/pom.xml
+++ b/activemq-unit-tests/pom.xml
@@ -22,7 +22,7 @@
org.apache.activemq
activemq-parent
- 5.19.7-TT.2-SNAPSHOT
+ 5.19.8-TT.1-SNAPSHOT
activemq-unit-tests
diff --git a/activemq-unit-tests/src/test/java/org/apache/activemq/broker/jmx/MBeanTest.java b/activemq-unit-tests/src/test/java/org/apache/activemq/broker/jmx/MBeanTest.java
index 3dab66916f5..b5f31eede6b 100644
--- a/activemq-unit-tests/src/test/java/org/apache/activemq/broker/jmx/MBeanTest.java
+++ b/activemq-unit-tests/src/test/java/org/apache/activemq/broker/jmx/MBeanTest.java
@@ -2086,6 +2086,14 @@ protected void testAddTransportConnectorBlockedBrokerView(String scheme) throws
assertEquals("Transport scheme '" + scheme + "' is not allowed", e.getMessage());
}
+ try {
+ // verify any composite URI is blocked as well without parens
+ brokerView.addConnector("static:tcp://0.0.0.0:0," + scheme + "://" + brokerName);
+ fail("Should have failed trying to add connector with scheme: " + scheme);
+ } catch (IllegalArgumentException e) {
+ assertEquals("Transport scheme '" + scheme + "' is not allowed", e.getMessage());
+ }
+
try {
// verify nested composite URI is blocked
brokerView.addConnector("static:(static:(static:(" + scheme + "://localhost)))");
@@ -2108,6 +2116,15 @@ public void testNestedAddTransportConnector() throws Exception {
} catch (IllegalArgumentException e) {
assertEquals("URI can't contain more than 5 nested composite URIs", e.getMessage());
}
+
+ try {
+ // verify nested composite URI with more than 5 levels is blocked without parens
+ brokerView.addConnector(
+ "static:static:static:static:static:static:tcp://localhost:0");
+ fail("Should have failed trying to add vm connector bridge");
+ } catch (IllegalArgumentException e) {
+ assertEquals("URI can't contain more than 5 nested composite URIs", e.getMessage());
+ }
}
}
diff --git a/activemq-unit-tests/src/test/java/org/apache/activemq/config/BrokerXmlConfigFromJNDITest.java b/activemq-unit-tests/src/test/java/org/apache/activemq/config/BrokerXmlConfigFromJNDITest.java
index 936603c3b82..68139efe5cc 100644
--- a/activemq-unit-tests/src/test/java/org/apache/activemq/config/BrokerXmlConfigFromJNDITest.java
+++ b/activemq-unit-tests/src/test/java/org/apache/activemq/config/BrokerXmlConfigFromJNDITest.java
@@ -16,19 +16,34 @@
*/
package org.apache.activemq.config;
-import java.io.File;
-import java.util.Hashtable;
-
-import javax.naming.Context;
-import javax.naming.InitialContext;
+import static org.apache.activemq.util.VmTransportTestUtils.resetVmTransportFactory;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.activemq.test.JmsTopicSendReceiveWithTwoConnectionsTest;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import java.io.File;
+import java.util.Hashtable;
+
/**
*
*/
public class BrokerXmlConfigFromJNDITest extends JmsTopicSendReceiveWithTwoConnectionsTest {
+
+ @Override
+ protected void setUp() throws Exception {
+ // reset before each test
+ resetVmTransportFactory("xbean");
+ super.setUp();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ resetVmTransportFactory();
+ }
+
@Override
protected ActiveMQConnectionFactory createConnectionFactory() throws Exception {
assertBaseDirectoryContainsSpaces();
diff --git a/activemq-unit-tests/src/test/java/org/apache/activemq/jmx/JmxCreateNCTest.java b/activemq-unit-tests/src/test/java/org/apache/activemq/jmx/JmxCreateNCTest.java
index 3d3b8c1cdc5..b20518e884c 100644
--- a/activemq-unit-tests/src/test/java/org/apache/activemq/jmx/JmxCreateNCTest.java
+++ b/activemq-unit-tests/src/test/java/org/apache/activemq/jmx/JmxCreateNCTest.java
@@ -82,6 +82,31 @@ public void testBridgeRegistration() throws Exception {
assertEquals("NC", nc.getName());
}
+ @Test
+ public void testTransportSchemeBridgeAllowed() throws Exception {
+ // Test composite network connector uri
+ String name = proxy.addNetworkConnector("static:(tcp://localhost,amqp://localhost)");
+ proxy.removeNetworkConnector(name);
+
+ // Test composite with missing parens
+ name = proxy.addNetworkConnector("static:amqp://localhost,tcp://127.0.0.1:0");
+ proxy.removeNetworkConnector(name);
+
+ // verify direct connector as well
+ name = proxy.addNetworkConnector("static:stomp://localhost");
+ proxy.removeNetworkConnector(name);
+
+ // verify nested composite URI
+ name = proxy.addNetworkConnector(
+ "static:(static:(static:(tcp+ssl://localhost:0,auto+nio+ssl://localhost)))");
+ proxy.removeNetworkConnector(name);
+
+ // verify nested composite URI is not blocked when not using parens
+ name = proxy.addNetworkConnector(
+ "static:static:static:123://localhost:0,auto+nio+ssl://localhost");
+ proxy.removeNetworkConnector(name);
+ }
+
@Test
public void testTransportSchemeBridgeBlocked() throws Exception {
for (String deniedScheme : DENIED_TRANSPORT_SCHEMES) {
@@ -99,6 +124,14 @@ protected void testTransportSchemeBridgeBlocked(String scheme) throws Exception
assertEquals("Transport scheme '" + scheme + "' is not allowed", e.getMessage());
}
+ // Test composite with missing parens
+ try {
+ proxy.addNetworkConnector("static:" + scheme + "://localhost,tcp://127.0.0.1:0");
+ fail("Should have failed trying to add connector bridge with scheme: " + scheme);
+ } catch (IllegalArgumentException e) {
+ assertEquals("Transport scheme '" + scheme + "' is not allowed", e.getMessage());
+ }
+
// verify direct connector as well
try {
proxy.addNetworkConnector(scheme + "://localhost");
@@ -114,6 +147,14 @@ protected void testTransportSchemeBridgeBlocked(String scheme) throws Exception
} catch (IllegalArgumentException e) {
assertEquals("Transport scheme '" + scheme + "' is not allowed", e.getMessage());
}
+
+ try {
+ // verify nested composite URI is blocked when not using parens
+ proxy.addNetworkConnector("static:static:static:tcp://localhost:0," + scheme + "://localhost");
+ fail("Should have failed trying to add connector bridge with scheme: " + scheme);
+ } catch (IllegalArgumentException e) {
+ assertEquals("Transport scheme '" + scheme + "' is not allowed", e.getMessage());
+ }
}
@Test
@@ -124,12 +165,21 @@ public void testAddNetworkConnectorMaxComposite() throws Exception {
try {
// verify nested composite URI with more than 5 levels is blocked. This has 6 nested
- // (not including first wrapper url
+ // (not including first wrapper url)
proxy.addNetworkConnector(
"static:(static:(static:(static:(static:(static:(tcp://localhost:0))))))");
fail("Should have failed trying to add more than 5 connector bridges");
} catch (IllegalArgumentException e) {
assertEquals("URI can't contain more than 5 nested composite URIs", e.getMessage());
}
+
+ try {
+ // verify nested composite URI with more than 5 levels is blocked without parens
+ proxy.addNetworkConnector(
+ "static:static:static:static:static:static:tcp://localhost:0");
+ fail("Should have failed trying to add more than 5 connector bridges");
+ } catch (IllegalArgumentException e) {
+ assertEquals("URI can't contain more than 5 nested composite URIs", e.getMessage());
+ }
}
}
diff --git a/activemq-unit-tests/src/test/java/org/apache/activemq/network/AbstractDurableSyncNetworkBridgeTest.java b/activemq-unit-tests/src/test/java/org/apache/activemq/network/AbstractDurableSyncNetworkBridgeTest.java
new file mode 100644
index 00000000000..47df7ef8baa
--- /dev/null
+++ b/activemq-unit-tests/src/test/java/org/apache/activemq/network/AbstractDurableSyncNetworkBridgeTest.java
@@ -0,0 +1,123 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.network;
+
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+import org.apache.activemq.broker.BrokerService;
+import org.apache.activemq.util.Wait;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public abstract class AbstractDurableSyncNetworkBridgeTest extends DynamicNetworkTestSupport {
+
+ protected static final Logger LOG = LoggerFactory.getLogger(
+ AbstractDurableSyncNetworkBridgeTest.class);
+
+ protected abstract void doSetUpLocalBroker(boolean deleteAllMessages, boolean startNetworkConnector, File dataDir) throws Exception;
+
+ protected abstract void doSetUpRemoteBroker(boolean deleteAllMessages, File dataDir, int port) throws Exception;
+
+ protected void restartLocalBroker(boolean startNetworkConnector) throws Exception {
+ stopLocalBroker();
+ doSetUpLocalBroker(false, startNetworkConnector, localBroker.getDataDirectoryFile());
+ }
+
+ protected void restartRemoteBroker() throws Exception {
+ final int previousPort = remoteBroker.getTransportConnectors().get(0).getConnectUri().getPort();
+ final File dataDir = remoteBroker.getDataDirectoryFile();
+ stopRemoteBroker();
+ try {
+ doSetUpRemoteBroker(false, dataDir, previousPort);
+ } catch (final IOException e) {
+ if (e.getCause() instanceof java.net.BindException) {
+ // Previous port still in TIME_WAIT — use a new ephemeral port
+ doSetUpRemoteBroker(false, dataDir, 0);
+ // Update the local broker's network connector to point to the new port
+ updateLocalNetworkConnectorUri();
+ } else {
+ throw e;
+ }
+ }
+ }
+
+ protected void restartBroker(BrokerService broker, boolean startNetworkConnector) throws Exception {
+ if (broker.getBrokerName().equals("localBroker")) {
+ restartLocalBroker(startNetworkConnector);
+ } else {
+ restartRemoteBroker();
+ }
+ }
+
+ protected void waitForBridgeFullyStarted() throws Exception {
+ waitForBridgeFullyStarted(TimeUnit.SECONDS.toMillis(15), true);
+ }
+
+ protected void waitForBridgeFullyStarted(long millis, boolean duplex) throws Exception {
+ // Wait for the local bridge to be fully started (advisory consumers registered)
+ assertTrue("Local bridge should be fully started", Wait.waitFor(() -> {
+ if (localBroker.getNetworkConnectors().get(0).activeBridges().isEmpty()) {
+ return false;
+ }
+ final NetworkBridge bridge = localBroker.getNetworkConnectors().get(0).activeBridges().iterator().next();
+ if (bridge instanceof DemandForwardingBridgeSupport) {
+ return ((DemandForwardingBridgeSupport) bridge).startedLatch.getCount() == 0;
+ }
+ return true;
+ }, millis, 100));
+
+ // Also wait for the duplex bridge on the remote broker to be fully started.
+ // The duplex connector creates a separate DemandForwardingBridge on the remote side
+ // that also needs its advisory consumers registered before it can process events.
+ if (duplex) {
+ assertTrue("Duplex bridge should be fully started", Wait.waitFor(() -> {
+ final DemandForwardingBridge duplexBridge = findDuplexBridge(
+ remoteBroker.getTransportConnectors().get(0));
+ return duplexBridge != null && duplexBridge.startedLatch.getCount() == 0;
+ }, millis, 100));
+ }
+ }
+
+
+ /**
+ * When the remote broker restarts on a new ephemeral port (BindException fallback),
+ * any existing network connector on the local broker still points to the old port.
+ * This method stops the old connector and replaces it with one targeting the new URI.
+ */
+ protected void updateLocalNetworkConnectorUri() throws Exception {
+ if (localBroker == null) {
+ return;
+ }
+ final List connectors = localBroker.getNetworkConnectors();
+ if (connectors.isEmpty()) {
+ return;
+ }
+ final NetworkConnector oldConnector = connectors.get(0);
+ oldConnector.stop();
+ localBroker.removeNetworkConnector(oldConnector);
+ final NetworkConnector newConnector = configureLocalNetworkConnector();
+ localBroker.addNetworkConnector(newConnector);
+ newConnector.start();
+ }
+
+ protected abstract NetworkConnector configureLocalNetworkConnector() throws Exception;
+
+}
diff --git a/activemq-unit-tests/src/test/java/org/apache/activemq/network/DurableSyncNetworkBridgeAuthTest.java b/activemq-unit-tests/src/test/java/org/apache/activemq/network/DurableSyncNetworkBridgeAuthTest.java
new file mode 100644
index 00000000000..646dd4f184d
--- /dev/null
+++ b/activemq-unit-tests/src/test/java/org/apache/activemq/network/DurableSyncNetworkBridgeAuthTest.java
@@ -0,0 +1,270 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.network;
+
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.net.URI;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+import org.apache.activemq.broker.BrokerPlugin;
+import org.apache.activemq.broker.BrokerService;
+import org.apache.activemq.broker.TransportConnector;
+import org.apache.activemq.command.ActiveMQTopic;
+import org.apache.activemq.command.BrokerSubscriptionInfo;
+import org.apache.activemq.command.DiscoveryEvent;
+import org.apache.activemq.security.AuthenticationUser;
+import org.apache.activemq.security.SimpleAuthenticationPlugin;
+import org.apache.activemq.store.kahadb.KahaDBPersistenceAdapter;
+import org.apache.activemq.store.kahadb.disk.journal.Journal.JournalDiskSyncStrategy;
+import org.apache.activemq.transport.Transport;
+import org.apache.activemq.transport.TransportFilter;
+import org.apache.activemq.util.Wait;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.Timeout;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@RunWith(Parameterized.class)
+public class DurableSyncNetworkBridgeAuthTest extends AbstractDurableSyncNetworkBridgeTest {
+
+ protected static final Logger LOG = LoggerFactory.getLogger(DurableSyncNetworkBridgeAuthTest.class);
+
+ @Parameters(name="duplex={0}")
+ public static Collection