Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2624,6 +2624,12 @@ public ConnectionContext getAdminConnectionContext() throws Exception {

protected void startManagementContext() throws Exception {
getManagementContext().setBrokerName(brokerName);
// Reuse the broker-level SSL context for JMX by default
// This avoids duplicating SSL config in activemq.xml while still allowing an
// explicit managementContext sslContext to override when one is needed
if (getManagementContext().getSslContext() == null && getSslContext() != null) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a logic flaw here-- just b/c an SslContext exists on the broker, does not mean the intent is to wire it to the management context.

If there is a fallback option, there most likely will need to be a config flag on the managementContext to affirm that ssl should be enabled.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mattrpav right this was the option I used in my PR #1661

Copy link
Author

@asaxena14 asaxena14 Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, the PR #1661 already supports explicit wiring via: " ". Adding a fallback code, guarded by an explicit flag, does not seem necessary and would add complexity without clear benefit.

getManagementContext().setSslContext(getSslContext());
}
getManagementContext().start();
adminView = new BrokerView(this, null);
ObjectName objectName = getBrokerObjectName();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package org.apache.activemq.broker.jmx;

import java.io.IOException;
import java.io.Serializable;
import java.lang.management.ManagementFactory;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
Expand All @@ -27,14 +28,19 @@
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
import java.rmi.server.RMIClientSocketFactory;
import java.rmi.server.RMIServerSocketFactory;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.HashMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.net.ServerSocket;
import java.security.GeneralSecurityException;

import javax.management.Attribute;
import javax.management.InstanceNotFoundException;
Expand All @@ -50,8 +56,12 @@
import javax.management.remote.JMXServiceURL;
import javax.management.remote.rmi.RMIConnectorServer;
import javax.management.remote.rmi.RMIJRMPServerImpl;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLServerSocketFactory;
import javax.rmi.ssl.SslRMIClientSocketFactory;

import org.apache.activemq.Service;
import org.apache.activemq.broker.SslContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
Expand Down Expand Up @@ -115,6 +125,7 @@ public class ManagementContext implements Service {
private List<ObjectName> suppressMBeanList;
private Remote serverStub;
private RMIJRMPServerImpl server;
private SslContext sslContext;

public ManagementContext() {
this(null);
Expand Down Expand Up @@ -282,6 +293,15 @@ public void setBrokerName(String brokerName) {
this.brokerName = brokerName;
}

public SslContext getSslContext() {
return sslContext;
}

public void setSslContext(SslContext sslContext) {
this.sslContext = sslContext;
}


/**
* @return Returns the jmxDomainName.
*/
Expand Down Expand Up @@ -578,13 +598,35 @@ private void createConnector(MBeanServer mbeanServer) throws IOException {
// This is handy to use if you have a firewall and need to force JMX to use fixed ports.
rmiServer = ""+getConnectorHost()+":" + rmiServerPort;
}

Map<String, Object> connectorEnvironment = new HashMap<>();
if (environment != null) {
connectorEnvironment.putAll(environment);
}

RMIClientSocketFactory clientSocketFactory = null;
RMIServerSocketFactory serverSocketFactory = null;

if (sslContext != null) {
try {
SSLContext context = sslContext.getSSLContext();
clientSocketFactory = new SslRMIClientSocketFactory();
serverSocketFactory = new SslContextServerSocketFactory(context);

connectorEnvironment.put(RMIConnectorServer.RMI_CLIENT_SOCKET_FACTORY_ATTRIBUTE, clientSocketFactory);
connectorEnvironment.put(RMIConnectorServer.RMI_SERVER_SOCKET_FACTORY_ATTRIBUTE, serverSocketFactory);
} catch (GeneralSecurityException e) {
throw new IOException("Failed to initialize JMX SSLContext", e);
}
}

server = new RMIJRMPServerImpl(connectorPort, null, null, environment);
server = new RMIJRMPServerImpl(connectorPort, clientSocketFactory, serverSocketFactory, connectorEnvironment);

final String serviceURL = "service:jmx:rmi://" + rmiServer + "/jndi/rmi://" +getConnectorHost()+":" + connectorPort + connectorPath;
final String serviceURL =
"service:jmx:rmi://" + rmiServer + "/jndi/rmi://" + getConnectorHost() + ":" + connectorPort + connectorPath;
final JMXServiceURL url = new JMXServiceURL(serviceURL);

connectorServer = new RMIConnectorServer(url, environment, server, ManagementFactory.getPlatformMBeanServer());
connectorServer = new RMIConnectorServer(url, connectorEnvironment, server, mbeanServer);
LOG.debug("Created JMXConnectorServer {}", connectorServer);
}

Expand Down Expand Up @@ -710,4 +752,28 @@ private Registry jmxRegistry(final int port) throws RemoteException {
}
}));
}

private static final class SslContextServerSocketFactory implements RMIServerSocketFactory, Serializable {
private static final long serialVersionUID = 1L;
private final SSLServerSocketFactory delegate;

private SslContextServerSocketFactory(SSLContext context) {
this.delegate = context.getServerSocketFactory();
}

@Override
public ServerSocket createServerSocket(int port) throws IOException {
return delegate.createServerSocket(port);
}

@Override
public boolean equals(Object obj) {
return obj != null && obj.getClass() == SslContextServerSocketFactory.class;
}

@Override
public int hashCode() {
return SslContextServerSocketFactory.class.hashCode();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
*/
package org.apache.activemq.xbean;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.URI;
import java.util.HashMap;
import java.util.Hashtable;
Expand Down Expand Up @@ -45,6 +47,11 @@ public class ManagementContextXBeanConfigTest extends TestCase {
private static final String MANAGEMENT_CONTEXT_TEST_XML = "org/apache/activemq/xbean/management-context-test.xml";
private static final String MANAGEMENT_CONTEXT_TEST_XML_CONNECTOR_PATH =
"org/apache/activemq/xbean/management-context-test-connector-path.xml";
private static final String MANAGEMENT_CONTEXT_TEST_XML_SSL_CONTEXT =
"org/apache/activemq/xbean/management-context-test-ssl-context.xml";
private static final String MANAGEMENT_CONTEXT_TEST_XML_SSL_CONTEXT_EXPLICIT =
"org/apache/activemq/xbean/management-context-test-ssl-context-explicit.xml";
private static final String TEST_MANAGEMENT_CONNECTOR_PORT_PROPERTY = "test.management.connector.port";

private static final transient Logger LOG = LoggerFactory.getLogger(ManagementContextXBeanConfigTest.class);

Expand Down Expand Up @@ -101,19 +108,72 @@ public void testFailAuthentication() throws Exception {
fail("Should have thrown an exception");
}

private int findOpenPort() throws IOException {
ServerSocket socket = new ServerSocket(0);
try {
return socket.getLocalPort();
} finally {
socket.close();
}
}

public void testManagementContextUsesBrokerSslContextByDefault() throws Exception {
int port = findOpenPort();
System.setProperty(TEST_MANAGEMENT_CONNECTOR_PORT_PROPERTY, Integer.toString(port));
brokerService = getBrokerService(MANAGEMENT_CONTEXT_TEST_XML_SSL_CONTEXT);
brokerService.start();

assertNotNull("ManagementContext SSL context should be inherited from BrokerService",
brokerService.getManagementContext().getSslContext());
assertTrue("JMX connector should be started when broker SSL context is configured",
brokerService.getManagementContext().isConnectorStarted());

assertNotNull("ManagementContext should retain SSL context after start",
brokerService.getManagementContext().getSslContext());
}

public void testManagementContextUsesExplicitSslContext() throws Exception {
int port = findOpenPort();
System.setProperty(TEST_MANAGEMENT_CONNECTOR_PORT_PROPERTY, Integer.toString(port));
brokerService = getBrokerService(MANAGEMENT_CONTEXT_TEST_XML_SSL_CONTEXT_EXPLICIT);
brokerService.start();

assertNotNull("BrokerService SSL context should be configured",
brokerService.getSslContext());
assertNotNull("ManagementContext explicit SSL context should be configured",
brokerService.getManagementContext().getSslContext());
assertNotSame("ManagementContext should use explicit SSL context instead of broker fallback",
brokerService.getSslContext(), brokerService.getManagementContext().getSslContext());
assertTrue("JMX connector should be started when explicit management SSL context is configured",
brokerService.getManagementContext().isConnectorStarted());

assertNotNull("ManagementContext should retain SSL context after start",
brokerService.getManagementContext().getSslContext());
}

public void assertAuthentication(JMXConnector connector) throws Exception {
connector.connect();
MBeanServerConnection connection = connector.getMBeanServerConnection();
ObjectName name = new ObjectName("test.domain:type=Broker,brokerName=localhost");
BrokerViewMBean mbean = MBeanServerInvocationHandler
.newProxyInstance(connection, name, BrokerViewMBean.class, true);
LOG.info("Broker " + mbean.getBrokerId() + " - " + mbean.getBrokerName());
try {
connector.connect();
MBeanServerConnection connection = connector.getMBeanServerConnection();
ObjectName name = new ObjectName("test.domain:type=Broker,brokerName=localhost");
BrokerViewMBean mbean = MBeanServerInvocationHandler
.newProxyInstance(connection, name, BrokerViewMBean.class, true);
LOG.info("Broker " + mbean.getBrokerId() + " - " + mbean.getBrokerName());
} finally {
connector.close();
}
}

@Override
protected void tearDown() throws Exception {
if (brokerService != null) {
brokerService.stop();
try {
if (brokerService != null) {
brokerService.stop();
brokerService.waitUntilStopped();
brokerService = null;
}
} finally {
System.clearProperty(TEST_MANAGEMENT_CONNECTOR_PORT_PROPERTY);
}
}

Expand All @@ -122,3 +182,4 @@ private BrokerService getBrokerService(String uri) throws Exception {
}

}

Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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 file can only be parsed using the xbean-spring library -->
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:amq="http://activemq.apache.org/schema/core"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://activemq.apache.org/schema/core http://activemq.apache.org/schema/core/activemq-core.xsd">

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" />

<bean id="managementSslContext" class="org.apache.activemq.spring.SpringSslContext">
<property name="keyStore" value="file:src/test/resources/org/apache/activemq/security/broker1.ks"/>
<property name="keyStorePassword" value="password"/>
<property name="trustStore" value="file:src/test/resources/org/apache/activemq/security/client.ks"/>
<property name="trustStorePassword" value="password"/>
</bean>

<broker useJmx="true" persistent="false" xmlns="http://activemq.apache.org/schema/core">
<sslContext>
<sslContext
keyStore="file:src/test/resources/org/apache/activemq/security/broker2.ks" keyStorePassword="password"
trustStore="file:src/test/resources/org/apache/activemq/security/client.ks" trustStorePassword="password"/>
</sslContext>

<managementContext>
<managementContext createConnector="true" connectorPort="${test.management.connector.port}" jmxDomainName="test.domain">
<property xmlns="http://www.springframework.org/schema/beans" name="sslContext" ref="managementSslContext"/>
</managementContext>
</managementContext>
</broker>

</beans>
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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 file can only be parsed using the xbean-spring library -->
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:amq="http://activemq.apache.org/schema/core"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://activemq.apache.org/schema/core http://activemq.apache.org/schema/core/activemq-core.xsd">

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" />

<broker useJmx="true" persistent="false" xmlns="http://activemq.apache.org/schema/core">
<sslContext>
<sslContext
keyStore="file:src/test/resources/org/apache/activemq/security/broker2.ks" keyStorePassword="password"
trustStore="file:src/test/resources/org/apache/activemq/security/client.ks" trustStorePassword="password"/>
</sslContext>

<managementContext>
<managementContext createConnector="true" connectorPort="${test.management.connector.port}" jmxDomainName="test.domain">
</managementContext>
</managementContext>
</broker>

</beans>