From 9af5bd326500773d89ce30311af5e3cac2a9b3f5 Mon Sep 17 00:00:00 2001
From: Valery Kharseko <vharseko@3a-systems.ru>
Date: Tue, 29 Sep 2026 06:39:04 +0000
Subject: [PATCH] [#1112] Listen on the configured listen-address in the HTTP connection handler and the JMX RMI connector (#1113)
---
opendj-server-legacy/src/test/java/org/opends/server/TestCaseUtils.java | 56 +++
opendj-server-legacy/src/main/java/org/opends/server/protocols/jmx/JmxConnectionHandler.java | 19
opendj-server-legacy/src/test/java/org/opends/server/protocols/jmx/JmxListenAddressTestCase.java | 398 ++++++++++++++++++++++++
opendj-server-legacy/src/test/java/org/opends/server/protocols/http/HTTPConnectionHandlerTestCase.java | 271 +++++++++++++++-
opendj-maven-plugin/src/main/resources/config/xml/org/forgerock/opendj/server/config/JMXConnectionHandlerConfiguration.xml | 11
opendj-server-legacy/src/main/java/org/opends/server/protocols/jmx/DirectoryRMIServerSocketFactory.java | 18
opendj-server-legacy/src/main/java/org/opends/server/protocols/jmx/RmiConnector.java | 82 +++++
opendj-server-legacy/src/main/java/org/opends/server/protocols/http/HTTPConnectionHandler.java | 73 +++-
8 files changed, 862 insertions(+), 66 deletions(-)
diff --git a/opendj-maven-plugin/src/main/resources/config/xml/org/forgerock/opendj/server/config/JMXConnectionHandlerConfiguration.xml b/opendj-maven-plugin/src/main/resources/config/xml/org/forgerock/opendj/server/config/JMXConnectionHandlerConfiguration.xml
index d3a6dd8..c9c66a0 100644
--- a/opendj-maven-plugin/src/main/resources/config/xml/org/forgerock/opendj/server/config/JMXConnectionHandlerConfiguration.xml
+++ b/opendj-maven-plugin/src/main/resources/config/xml/org/forgerock/opendj/server/config/JMXConnectionHandlerConfiguration.xml
@@ -14,6 +14,7 @@
Copyright 2007-2009 Sun Microsystems, Inc.
Portions Copyright 2013-2015 ForgeRock AS.
+ Portions Copyright 2026 3A Systems, LLC.
! -->
<adm:managed-object name="jmx-connection-handler"
plural-name="jmx-connection-handlers"
@@ -66,7 +67,15 @@
<adm:description>
If no value is provided, then the
<adm:user-friendly-name />
- listens on all interfaces.
+ listens on all interfaces. The RMI stubs that JMX clients receive
+ name the host to connect to: with a specific address, the server sets
+ the java.rmi.server.hostname system property to that address, unless
+ the property is already set, for instance with
+ -Djava.rmi.server.hostname in the Java arguments of the server. The
+ property applies to every RMI object that the Java virtual machine
+ exports, including those of the platform JMX agent. With several JMX
+ connection handlers on different specific addresses, the last one
+ started sets it.
</adm:description>
<adm:requires-admin-action>
<adm:server-restart />
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/protocols/http/HTTPConnectionHandler.java b/opendj-server-legacy/src/main/java/org/opends/server/protocols/http/HTTPConnectionHandler.java
index 9f94105..a3a61d9 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/protocols/http/HTTPConnectionHandler.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/protocols/http/HTTPConnectionHandler.java
@@ -30,7 +30,6 @@
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
-import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -133,8 +132,8 @@
/** Indicates whether this connection handler is enabled. */
private boolean enabled;
- /** The set of listeners for this connection handler. */
- private final List<HostPort> listeners = new LinkedList<>();
+ /** The addresses and the port the HTTP server listens on, or starts with next. */
+ private volatile List<HostPort> listeners = Collections.emptyList();
/** The HTTP server embedded in OpenDJ. */
private HttpServer httpServer;
@@ -448,11 +447,7 @@
friendlyName = config.name();
}
- int listenPort = config.getListenPort();
- for (InetAddress a : config.getListenAddress())
- {
- listeners.add(new HostPort(a.getHostAddress(), listenPort));
- }
+ listeners = toListeners(config);
handlerName = getHandlerName(config);
@@ -770,7 +765,17 @@
this.httpServer = createHttpServer();
this.httpServer.getServerConfiguration().addHttpHandler(newGrizzlyHttpHandler(new RootHttpApplication()));
logger.trace("Starting HTTP server...");
- this.httpServer.start();
+ try
+ {
+ this.httpServer.start();
+ }
+ catch (IOException | RuntimeException e)
+ {
+ // HttpServer.start() stops at the first listener that cannot bind and leaves the listeners started before it
+ // bound: release their addresses, since the caller only forgets about the server.
+ this.httpServer.shutdownNow();
+ throw e;
+ }
logger.trace("HTTP server started");
logger.info(NOTE_CONNHANDLER_STARTED_LISTENING, handlerName);
}
@@ -790,13 +795,41 @@
setHttpStatsProbe(server);
}
- // Configure the network listener
- final NetworkListener listener = new NetworkListener(
- "OpenDJ-HTTP", NetworkListener.DEFAULT_NETWORK_HOST, initConfig.getListenPort());
- server.addListener(listener);
+ // Configure one network listener per listen address, and report them: a configuration change can replace
+ // initConfig after the initialization. HttpServer keys its listeners by name, and each listener owns its
+ // transport, so neither can be shared.
+ listeners = toListeners(initConfig);
+ final int numRequestHandlers = getNumRequestHandlers(currentConfig.getNumRequestHandlers(), friendlyName);
+ for (InetAddress address : initConfig.getListenAddress())
+ {
+ final String host = address.getHostAddress();
+ final NetworkListener listener = new NetworkListener("OpenDJ-HTTP " + host, host, initConfig.getListenPort());
+ server.addListener(listener);
+ configureTransport(listener.getTransport(), numRequestHandlers);
- // Configure the network transport
- final TCPNIOTransport transport = listener.getTransport();
+ // Configure SSL
+ if (sslEngineConfigurator != null)
+ {
+ listener.setSecure(true);
+ listener.setSSLEngineConfig(sslEngineConfigurator);
+ }
+ }
+
+ return server;
+ }
+
+ private static List<HostPort> toListeners(HTTPConnectionHandlerCfg config)
+ {
+ final List<HostPort> hostPorts = new ArrayList<>();
+ for (InetAddress address : config.getListenAddress())
+ {
+ hostPorts.add(new HostPort(address.getHostAddress(), config.getListenPort()));
+ }
+ return Collections.unmodifiableList(hostPorts);
+ }
+
+ private void configureTransport(TCPNIOTransport transport, int numRequestHandlers)
+ {
transport.setReuseAddress(currentConfig.isAllowTCPReuseAddress());
transport.setKeepAlive(currentConfig.isUseTCPKeepAlive());
transport.setTcpNoDelay(currentConfig.isUseTCPNoDelay());
@@ -807,18 +840,8 @@
transport.setWriteBufferSize(bufferSize);
transport.setIOStrategy(SameThreadIOStrategy.getInstance());
- final int numRequestHandlers = getNumRequestHandlers(currentConfig.getNumRequestHandlers(), friendlyName);
transport.setSelectorRunnersCount(numRequestHandlers);
transport.setServerConnectionBackLog(currentConfig.getAcceptBacklog());
-
- // Configure SSL
- if (sslEngineConfigurator != null)
- {
- listener.setSecure(true);
- listener.setSSLEngineConfig(sslEngineConfigurator);
- }
-
- return server;
}
private void setHttpStatsProbe(HttpServer server)
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/protocols/jmx/DirectoryRMIServerSocketFactory.java b/opendj-server-legacy/src/main/java/org/opends/server/protocols/jmx/DirectoryRMIServerSocketFactory.java
index 8c6d086..82081b9 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/protocols/jmx/DirectoryRMIServerSocketFactory.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/protocols/jmx/DirectoryRMIServerSocketFactory.java
@@ -47,6 +47,9 @@
/** Indicate if we required the client authentication via SSL. */
private final boolean needClientCertificate;
+ /** The address to listen on, which could be INADDR_ANY. */
+ private final InetAddress listenAddress;
+
/**
* Constructs a new <code>DirectoryRMIServerSocketFactory</code> with the
* specified SSL socket configuration.
@@ -59,13 +62,18 @@
* connections accepted by server sockets created by this
* factory; <code>false</code> to not require client
* authentication.
+ *
+ * @param listenAddress
+ * the address the server sockets created by this factory
+ * listen on
*/
public DirectoryRMIServerSocketFactory(SSLSocketFactory sslSocketFactory,
- boolean needClientCertificate)
+ boolean needClientCertificate, InetAddress listenAddress)
{
// Initialize the configuration parameters.
this.needClientCertificate = needClientCertificate;
this.sslSocketFactory = sslSocketFactory;
+ this.listenAddress = listenAddress;
}
/**
@@ -97,7 +105,7 @@
@Override
public ServerSocket createServerSocket(int port) throws IOException
{
- return new ServerSocket(port, 0, InetAddress.getByName("0.0.0.0"))
+ return new ServerSocket(port, 0, listenAddress)
{
@Override
public Socket accept() throws IOException
@@ -162,7 +170,8 @@
private boolean checkParameters(DirectoryRMIServerSocketFactory that)
{
return needClientCertificate == that.needClientCertificate
- && sslSocketFactory.equals(that.sslSocketFactory);
+ && sslSocketFactory.equals(that.sslSocketFactory)
+ && listenAddress.equals(that.listenAddress);
}
/**
@@ -177,6 +186,7 @@
{
return getClass().hashCode()
+ Boolean.valueOf(needClientCertificate).hashCode()
- + sslSocketFactory.hashCode();
+ + sslSocketFactory.hashCode()
+ + listenAddress.hashCode();
}
}
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/protocols/jmx/JmxConnectionHandler.java b/opendj-server-legacy/src/main/java/org/opends/server/protocols/jmx/JmxConnectionHandler.java
index dbbb523..346024e 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/protocols/jmx/JmxConnectionHandler.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/protocols/jmx/JmxConnectionHandler.java
@@ -13,6 +13,7 @@
*
* Copyright 2006-2009 Sun Microsystems, Inc.
* Portions Copyright 2013-2016 ForgeRock AS.
+ * Portions Copyright 2026 3A Systems, LLC.
*/
package org.opends.server.protocols.jmx;
@@ -88,6 +89,14 @@
private final List<HostPort> listeners = new LinkedList<>();
/**
+ * The address the RMI registry and the RMI connector listen on. A change of
+ * listen-address takes effect when the handler restarts, as its
+ * configuration says: the registry, the connector and the listeners stay
+ * on the same address until then.
+ */
+ private InetAddress listenAddress;
+
+ /**
* Creates a new instance of this JMX connection handler. It must be
* initialized before it may be used.
*/
@@ -136,7 +145,7 @@
}
listeners.clear();
- listeners.add(HostPort.allAddresses(config.getListenPort()));
+ listeners.add(new HostPort(listenAddress.getHostAddress(), config.getListenPort()));
rmiConnector.finalizeConnectionHandler(portChanged);
try
@@ -224,13 +233,14 @@
/**
- * Get the JMX connection handler's listen address.
+ * Get the JMX connection handler's listen address: the one it was
+ * initialized with, until it restarts.
*
* @return Returns the JMX connection handler's listen address.
*/
public InetAddress getListenAddress()
{
- return currentConfig.getListenAddress();
+ return listenAddress;
}
/**
@@ -301,8 +311,9 @@
protocol = "JMX";
}
+ listenAddress = config.getListenAddress();
listeners.clear();
- listeners.add(HostPort.allAddresses(config.getListenPort()));
+ listeners.add(new HostPort(listenAddress.getHostAddress(), config.getListenPort()));
connectionHandlerName = "JMX Connection Handler " + config.getListenPort();
// Create a system property to store the JMX port the server is
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/protocols/jmx/RmiConnector.java b/opendj-server-legacy/src/main/java/org/opends/server/protocols/jmx/RmiConnector.java
index 3b7f82e..626f94d 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/protocols/jmx/RmiConnector.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/protocols/jmx/RmiConnector.java
@@ -22,9 +22,11 @@
import java.io.IOException;
import java.net.InetAddress;
+import java.net.UnknownHostException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
+import java.rmi.server.RMIServerSocketFactory;
import java.util.HashMap;
import java.util.Map;
import java.util.SortedSet;
@@ -168,6 +170,18 @@
*/
private String rmiVersion;
+ /**
+ * The system property that names the host the stubs of the objects exported
+ * through RMI advertise.
+ */
+ private static final String RMI_SERVER_HOSTNAME = "java.rmi.server.hostname";
+
+ /**
+ * The value this server gave to {@code java.rmi.server.hostname}, or
+ * {@code null} if the server did not set it.
+ */
+ private static String rmiServerHostnameSetByServer;
+
// ===================================================================
// CONSTRUCTOR
// ===================================================================
@@ -319,8 +333,10 @@
// ---------------------
// init an ssl context
// ---------------------
+ // Both server socket factories listen on the configured listen address:
+ // the default RMI factory would listen on every interface.
SslRMIClientSocketFactory rmiClientSockeyFactory = null;
- DirectoryRMIServerSocketFactory rmiServerSockeyFactory = null;
+ RMIServerSocketFactory rmiServerSockeyFactory;
if (jmxConnectionHandler.isUseSSL())
{
if (logger.isTraceEnabled())
@@ -355,7 +371,8 @@
SSLSocketFactory ssf = ctx.getSocketFactory();
// set the Server socket factory in the JMX map
- rmiServerSockeyFactory = new DirectoryRMIServerSocketFactory(ssf, false);
+ rmiServerSockeyFactory = new DirectoryRMIServerSocketFactory(
+ ssf, false, jmxConnectionHandler.getListenAddress());
env.put(
"jmx.remote.rmi.server.socket.factory",
rmiServerSockeyFactory);
@@ -376,6 +393,8 @@
{
logger.trace("UNSECURE CONNECTION");
}
+ rmiServerSockeyFactory = new OpendsRmiServerSocketFactory(
+ jmxConnectionHandler.getListenAddress());
}
// specify the rmi JMX authenticator to be used
@@ -399,6 +418,7 @@
{
logger.trace("Create and start the JMX RMI connector");
}
+ advertiseListenAddress(jmxConnectionHandler.getListenAddress());
OpendsRMIJRMPServerImpl opendsRmiConnectorServer =
new OpendsRMIJRMPServerImpl(jmxConnectionHandler.getRmiPort(),
rmiClientSockeyFactory, rmiServerSockeyFactory, env);
@@ -426,6 +446,64 @@
}
+ /**
+ * Makes the stub of the RMI connector advertise the listen address.
+ * <p>
+ * The stub that a client gets from the RMI registry advertises the host that
+ * {@code java.rmi.server.hostname} names, or else the address of the local host,
+ * whatever address the connector listens on: a connector bound to another
+ * address refuses the client. The JDK reads the property again at each export.
+ * A value that this server did not set is the operator's, and is kept.
+ *
+ * @param listenAddress
+ * the address the connector listens on
+ */
+ private static synchronized void advertiseListenAddress(InetAddress listenAddress)
+ {
+ final String hostname = System.getProperty(RMI_SERVER_HOSTNAME);
+ if (hostname != null && !hostname.equals(rmiServerHostnameSetByServer))
+ {
+ return;
+ }
+ if (listenAddress.isAnyLocalAddress())
+ {
+ // Cleared, the property would leave the JDK advertising the host it last
+ // read: the address a connector of this server listened on before, which
+ // a remote client cannot reach if it was a loopback one. Advertise the
+ // local host instead, as the JDK does by default.
+ final InetAddress localHost = getLocalHostOrNull();
+ if (rmiServerHostnameSetByServer != null
+ && localHost != null && !localHost.isLoopbackAddress())
+ {
+ rmiServerHostnameSetByServer = localHost.getHostAddress();
+ System.setProperty(RMI_SERVER_HOSTNAME, rmiServerHostnameSetByServer);
+ }
+ else
+ {
+ System.clearProperty(RMI_SERVER_HOSTNAME);
+ rmiServerHostnameSetByServer = null;
+ }
+ }
+ else
+ {
+ rmiServerHostnameSetByServer = listenAddress.getHostAddress();
+ System.setProperty(RMI_SERVER_HOSTNAME, rmiServerHostnameSetByServer);
+ }
+ }
+
+ private static InetAddress getLocalHostOrNull()
+ {
+ try
+ {
+ return InetAddress.getLocalHost();
+ }
+ catch (UnknownHostException e)
+ {
+ logger.traceException(e);
+ return null;
+ }
+ }
+
static void configureJmxDeserializationProtection(Map<String, Object> env)
{
// Tightly constrain the credentials object exchanged during authentication
diff --git a/opendj-server-legacy/src/test/java/org/opends/server/TestCaseUtils.java b/opendj-server-legacy/src/test/java/org/opends/server/TestCaseUtils.java
index 3d41854..52a49da 100644
--- a/opendj-server-legacy/src/test/java/org/opends/server/TestCaseUtils.java
+++ b/opendj-server-legacy/src/test/java/org/opends/server/TestCaseUtils.java
@@ -52,7 +52,10 @@
import java.lang.management.ThreadMXBean;
import java.net.BindException;
import java.net.ConnectException;
+import java.net.Inet4Address;
+import java.net.InetAddress;
import java.net.InetSocketAddress;
+import java.net.NetworkInterface;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;
@@ -130,6 +133,7 @@
import org.opends.server.util.DynamicConstants;
import org.opends.server.util.LDIFReader;
import org.opends.server.util.TestTimer;
+import org.testng.SkipException;
import com.forgerock.opendj.util.OperatingSystem;
@@ -966,6 +970,58 @@
}
/**
+ * Returns an IPv4 address of the local host other than a loopback one, for the tests of a listen
+ * address: a listener bound to the loopback address refuses connections to it, while a listener
+ * bound to the wildcard address accepts them.
+ *
+ * @return an IPv4 address of an interface of the local host which is up and not a loopback one
+ * @throws IOException
+ * if the network interfaces cannot be listed
+ * @throws SkipException
+ * if the local host has no such address
+ */
+ public static InetAddress getNonLoopbackAddress() throws IOException
+ {
+ for (NetworkInterface nif : Collections.list(NetworkInterface.getNetworkInterfaces()))
+ {
+ if (!nif.isUp() || nif.isLoopback())
+ {
+ continue;
+ }
+ for (InetAddress address : Collections.list(nif.getInetAddresses()))
+ {
+ if (address instanceof Inet4Address && !address.isLinkLocalAddress())
+ {
+ return address;
+ }
+ }
+ }
+ throw new SkipException("the local host has no IPv4 address other than a loopback one");
+ }
+
+ /**
+ * Tells whether a connection to the given address and port is accepted right now.
+ *
+ * @param address
+ * the address to connect to
+ * @param port
+ * the port to connect to
+ * @return {@code true} if the connection is accepted, {@code false} if it fails
+ */
+ public static boolean isAcceptingConnections(InetAddress address, int port)
+ {
+ try (Socket socket = new Socket())
+ {
+ socket.connect(new InetSocketAddress(address, port), 5000);
+ return true;
+ }
+ catch (IOException e)
+ {
+ return false;
+ }
+ }
+
+ /**
* Finds a free server socket port on the local host.
*
* @return The free port.
diff --git a/opendj-server-legacy/src/test/java/org/opends/server/protocols/http/HTTPConnectionHandlerTestCase.java b/opendj-server-legacy/src/test/java/org/opends/server/protocols/http/HTTPConnectionHandlerTestCase.java
index 456284b..42d97f1 100644
--- a/opendj-server-legacy/src/test/java/org/opends/server/protocols/http/HTTPConnectionHandlerTestCase.java
+++ b/opendj-server-legacy/src/test/java/org/opends/server/protocols/http/HTTPConnectionHandlerTestCase.java
@@ -15,17 +15,38 @@
*/
package org.opends.server.protocols.http;
+import static java.util.concurrent.TimeUnit.*;
+
+import static org.assertj.core.api.Assertions.*;
+import static org.opends.server.TestCaseUtils.*;
+import static org.opends.server.util.ServerConstants.*;
import static org.testng.Assert.*;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.TrustManager;
+
import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.opendj.server.config.meta.HTTPConnectionHandlerCfgDefn;
import org.forgerock.opendj.server.config.server.HTTPConnectionHandlerCfg;
+import org.opends.admin.ads.util.BlindTrustManager;
import org.opends.server.DirectoryServerTestCase;
import org.opends.server.TestCaseUtils;
import org.opends.server.core.DirectoryServer;
+import org.opends.server.extensions.DummyAlertHandler;
import org.opends.server.extensions.InitializationUtils;
import org.opends.server.types.Entry;
+import org.opends.server.types.HostPort;
+import org.opends.server.util.TestTimer;
import org.testng.annotations.BeforeClass;
+import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
@SuppressWarnings("javadoc")
@@ -34,6 +55,9 @@
{
private static final LocalizableMessage STOP_REASON = LocalizableMessage.raw("Don't need a reason.");
+ /** An address of TEST-NET-1 (RFC 5737), which is never assigned to an interface of the host. */
+ private static final String UNASSIGNED_ADDRESS = "192.0.2.1";
+
@BeforeClass
public void setUp() throws Exception
{
@@ -53,32 +77,7 @@
public void testStartWaitsForListenPort() throws Exception
{
final int listenPort = TestCaseUtils.findFreePort();
- Entry handlerEntry = TestCaseUtils.makeEntry(
- "dn: cn=HTTP Connection Handler,cn=Connection Handlers,cn=config",
- "objectClass: top",
- "objectClass: ds-cfg-connection-handler",
- "objectClass: ds-cfg-http-connection-handler",
- "cn: HTTP Connection Handler",
- "ds-cfg-java-class: org.opends.server.protocols.http.HTTPConnectionHandler",
- "ds-cfg-enabled: true",
- "ds-cfg-listen-address: 127.0.0.1",
- "ds-cfg-listen-port: " + listenPort,
- "ds-cfg-accept-backlog: 128",
- "ds-cfg-keep-stats: false",
- "ds-cfg-use-tcp-keep-alive: true",
- "ds-cfg-use-tcp-no-delay: true",
- "ds-cfg-allow-tcp-reuse-address: true",
- "ds-cfg-max-request-size: 5 megabytes",
- "ds-cfg-buffer-size: 4096 bytes",
- "ds-cfg-max-blocked-write-time-limit: 2 minutes",
- "ds-cfg-use-ssl: false",
- "ds-cfg-ssl-client-auth-policy: optional",
- "ds-cfg-ssl-cert-nickname: server-cert");
- HTTPConnectionHandlerCfg config =
- InitializationUtils.getConfiguration(HTTPConnectionHandlerCfgDefn.getInstance(), handlerEntry);
-
- HTTPConnectionHandler handler = new HTTPConnectionHandler();
- handler.initializeConnectionHandler(DirectoryServer.getInstance().getServerContext(), config);
+ HTTPConnectionHandler handler = newHandler(listenPort, "127.0.0.1");
try
{
handler.start();
@@ -88,10 +87,222 @@
}
finally
{
- handler.processServerShutdown(STOP_REASON);
- handler.finalizeConnectionHandler(STOP_REASON);
- handler.join(10000);
- assertFalse(handler.isAlive(), "the connection handler thread is still running");
+ stop(handler);
}
}
+
+ /** A handler restricted to the loopback address must not answer on the other addresses of the host. */
+ @Test
+ public void listensOnlyOnTheConfiguredListenAddress() throws Exception
+ {
+ final InetAddress external = getNonLoopbackAddress();
+ final int listenPort = TestCaseUtils.findFreePort();
+ HTTPConnectionHandler handler = newHandler(listenPort, "127.0.0.1");
+ try
+ {
+ handler.start();
+
+ assertTrue(isAcceptingConnections(loopback(), listenPort), "the configured address is not listened on");
+ assertFalse(isAcceptingConnections(external, listenPort),
+ "the handler answers on " + external.getHostAddress() + ", which is not one of its listen addresses");
+ assertThat(handler.getListeners()).containsExactly(new HostPort("127.0.0.1", listenPort));
+ }
+ finally
+ {
+ stop(handler);
+ }
+ }
+
+ @DataProvider
+ public Object[][] useSSL()
+ {
+ return new Object[][] { { false }, { true } };
+ }
+
+ /** Each of several listen addresses gets its own listener, with the handler's SSL settings. */
+ @Test(dataProvider = "useSSL")
+ public void listensOnEveryConfiguredListenAddress(boolean useSSL) throws Exception
+ {
+ final InetAddress external = getNonLoopbackAddress();
+ final int listenPort = TestCaseUtils.findFreePort();
+ HTTPConnectionHandler handler = newHandler(useSSL, listenPort, "127.0.0.1", external.getHostAddress());
+ try
+ {
+ handler.start();
+
+ assertAnswers(loopback(), listenPort, useSSL);
+ assertAnswers(external, listenPort, useSSL);
+ assertThat(handler.getListeners()).containsOnly(
+ new HostPort("127.0.0.1", listenPort), new HostPort(external.getHostAddress(), listenPort));
+ }
+ finally
+ {
+ stop(handler);
+ }
+ }
+
+ /**
+ * When one listen address cannot be bound, the handler does not start, and must not keep the
+ * addresses it has already bound: nothing would ever release them.
+ * <p>
+ * The listener of 127.0.0.1 starts before the one of {@value #UNASSIGNED_ADDRESS}: the embedded
+ * server starts its listeners in the order of a hash map of their names.
+ */
+ @Test
+ public void releasesTheBoundListenAddressesWhenAnotherCannotBeBound() throws Exception
+ {
+ final int listenPort = TestCaseUtils.findFreePort();
+ HTTPConnectionHandler handler = newHandler(listenPort, "127.0.0.1", UNASSIGNED_ADDRESS);
+ try
+ {
+ handler.start();
+
+ // The handler thread tries twice, then disables the handler: wait for it to give up.
+ final InetAddress loopback = loopback();
+ new TestTimer.Builder().maxSleep(10, SECONDS).sleepTimes(100, MILLISECONDS).toTimer()
+ .repeatUntilSuccess(new TestTimer.CallableVoid()
+ {
+ @Override
+ public void call() throws Exception
+ {
+ assertFalse(isAcceptingConnections(loopback, listenPort),
+ "127.0.0.1 is still listened on although the handler could not start");
+ }
+ });
+ }
+ finally
+ {
+ stop(handler);
+ }
+ }
+
+ /**
+ * A handler that could not start starts again after a change of its listen address, and then
+ * reports the address it listens on, not the one it was initialized with.
+ */
+ @Test
+ public void reportsTheListenAddressItStartsWithAfterAChange() throws Exception
+ {
+ final int listenPort = TestCaseUtils.findFreePort();
+ final int givenUp = DummyAlertHandler.getAlertCount(ALERT_TYPE_HTTP_CONNECTION_HANDLER_CONSECUTIVE_FAILURES);
+ HTTPConnectionHandler handler = newHandler(listenPort, UNASSIGNED_ADDRESS);
+ try
+ {
+ handler.start();
+
+ // The handler thread tries twice, then disables the handler: a change applied before it gives up is undone.
+ final TestTimer timer = new TestTimer.Builder().maxSleep(10, SECONDS).sleepTimes(100, MILLISECONDS).toTimer();
+ timer.repeatUntilSuccess(new TestTimer.CallableVoid()
+ {
+ @Override
+ public void call() throws Exception
+ {
+ assertThat(DummyAlertHandler.getAlertCount(ALERT_TYPE_HTTP_CONNECTION_HANDLER_CONSECUTIVE_FAILURES))
+ .as("the handler has not given up starting").isGreaterThan(givenUp);
+ }
+ });
+ final HTTPConnectionHandlerCfg changedConfig = newConfig(false, listenPort, "127.0.0.1");
+ final InetAddress loopback = loopback();
+ timer.repeatUntilSuccess(new TestTimer.CallableVoid()
+ {
+ @Override
+ public void call() throws Exception
+ {
+ // Applied again until it holds: the handler thread disables the handler just after it sends the alert.
+ handler.applyConfigurationChange(changedConfig);
+ assertTrue(isAcceptingConnections(loopback, listenPort), "the new listen address is not listened on");
+ }
+ });
+ assertThat(handler.getListeners()).containsExactly(new HostPort("127.0.0.1", listenPort));
+ }
+ finally
+ {
+ stop(handler);
+ }
+ }
+
+ /**
+ * Asserts that a listener answers on the address: over TLS, a completed handshake, which a
+ * listener without the handler's SSL settings cannot give.
+ */
+ private static void assertAnswers(InetAddress address, int port, boolean useSSL) throws Exception
+ {
+ if (!useSSL)
+ {
+ assertTrue(isAcceptingConnections(address, port), address.getHostAddress() + " is not listened on");
+ return;
+ }
+ final SSLContext client = SSLContext.getInstance("TLS");
+ client.init(null, new TrustManager[] { new BlindTrustManager() }, null);
+ try (SSLSocket socket = (SSLSocket) client.getSocketFactory().createSocket(address, port))
+ {
+ socket.setSoTimeout(10000);
+ socket.startHandshake();
+ }
+ catch (IOException e)
+ {
+ throw new AssertionError(address.getHostAddress() + " does not complete a TLS handshake", e);
+ }
+ }
+
+ private static HTTPConnectionHandler newHandler(int listenPort, String... listenAddresses) throws Exception
+ {
+ return newHandler(false, listenPort, listenAddresses);
+ }
+
+ private static HTTPConnectionHandler newHandler(boolean useSSL, int listenPort, String... listenAddresses)
+ throws Exception
+ {
+ HTTPConnectionHandler handler = new HTTPConnectionHandler();
+ handler.initializeConnectionHandler(DirectoryServer.getInstance().getServerContext(),
+ newConfig(useSSL, listenPort, listenAddresses));
+ return handler;
+ }
+
+ private static HTTPConnectionHandlerCfg newConfig(boolean useSSL, int listenPort, String... listenAddresses)
+ throws Exception
+ {
+ final List<String> ldif = new ArrayList<>();
+ Collections.addAll(ldif,
+ "dn: cn=HTTP Connection Handler,cn=Connection Handlers,cn=config",
+ "objectClass: top",
+ "objectClass: ds-cfg-connection-handler",
+ "objectClass: ds-cfg-http-connection-handler",
+ "cn: HTTP Connection Handler",
+ "ds-cfg-java-class: org.opends.server.protocols.http.HTTPConnectionHandler",
+ "ds-cfg-enabled: true");
+ for (String listenAddress : listenAddresses)
+ {
+ ldif.add("ds-cfg-listen-address: " + listenAddress);
+ }
+ Collections.addAll(ldif,
+ "ds-cfg-listen-port: " + listenPort,
+ "ds-cfg-accept-backlog: 128",
+ "ds-cfg-keep-stats: false",
+ "ds-cfg-use-tcp-keep-alive: true",
+ "ds-cfg-use-tcp-no-delay: true",
+ "ds-cfg-allow-tcp-reuse-address: true",
+ "ds-cfg-max-request-size: 5 megabytes",
+ "ds-cfg-buffer-size: 4096 bytes",
+ "ds-cfg-max-blocked-write-time-limit: 2 minutes",
+ "ds-cfg-use-ssl: " + useSSL,
+ "ds-cfg-key-manager-provider: cn=JKS,cn=Key Manager Providers,cn=config",
+ "ds-cfg-ssl-client-auth-policy: optional",
+ "ds-cfg-ssl-cert-nickname: server-cert");
+ Entry handlerEntry = TestCaseUtils.makeEntry(ldif.toArray(new String[0]));
+ return InitializationUtils.getConfiguration(HTTPConnectionHandlerCfgDefn.getInstance(), handlerEntry);
+ }
+
+ private static InetAddress loopback() throws UnknownHostException
+ {
+ return InetAddress.getByName("127.0.0.1");
+ }
+
+ private static void stop(HTTPConnectionHandler handler) throws InterruptedException
+ {
+ handler.processServerShutdown(STOP_REASON);
+ handler.finalizeConnectionHandler(STOP_REASON);
+ handler.join(10000);
+ assertFalse(handler.isAlive(), "the connection handler thread is still running");
+ }
}
diff --git a/opendj-server-legacy/src/test/java/org/opends/server/protocols/jmx/JmxListenAddressTestCase.java b/opendj-server-legacy/src/test/java/org/opends/server/protocols/jmx/JmxListenAddressTestCase.java
new file mode 100644
index 0000000..815d886
--- /dev/null
+++ b/opendj-server-legacy/src/test/java/org/opends/server/protocols/jmx/JmxListenAddressTestCase.java
@@ -0,0 +1,398 @@
+/*
+ * The contents of this file are subject to the terms of the Common Development and
+ * Distribution License (the License). You may not use this file except in compliance with the
+ * License.
+ *
+ * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
+ * specific language governing permission and limitations under the License.
+ *
+ * When distributing Covered Software, include this CDDL Header Notice in each file and include
+ * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
+ * Header, with the fields enclosed by brackets [] replaced by your own identifying
+ * information: "Portions copyright [year] [name of copyright owner]".
+ *
+ * Copyright 2026 3A Systems, LLC.
+ */
+package org.opends.server.protocols.jmx;
+
+import static org.assertj.core.api.Assertions.*;
+import static org.opends.server.TestCaseUtils.*;
+import static org.testng.Assert.*;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.rmi.registry.LocateRegistry;
+import java.rmi.registry.Registry;
+import java.security.cert.X509Certificate;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.management.remote.JMXConnector;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509ExtendedTrustManager;
+
+import org.forgerock.i18n.LocalizableMessage;
+import org.forgerock.opendj.config.server.ConfigChangeResult;
+import org.forgerock.opendj.ldap.ResultCode;
+import org.forgerock.opendj.server.config.meta.JMXConnectionHandlerCfgDefn;
+import org.forgerock.opendj.server.config.server.JMXConnectionHandlerCfg;
+import org.opends.server.DirectoryServerTestCase;
+import org.opends.server.TestCaseUtils;
+import org.opends.server.core.DirectoryServer;
+import org.opends.server.extensions.InitializationUtils;
+import org.opends.server.types.Entry;
+import org.opends.server.types.HostPort;
+import org.testng.SkipException;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+/**
+ * The JMX connection handler listens on its listen address only: with both its RMI registry, on
+ * the listen port, and its RMI connector, on the RMI port. A JMX client reaches it there.
+ */
+@SuppressWarnings("javadoc")
+@Test(groups = { "precommit", "jmx" }, sequential = true)
+public class JmxListenAddressTestCase extends DirectoryServerTestCase
+{
+ private static final LocalizableMessage STOP_REASON = LocalizableMessage.raw("Don't need a reason.");
+
+ private static final String USER_DN = "cn=JMX Reader,o=test";
+ private static final String USER_PASSWORD = "password";
+
+ private static final String RMI_SERVER_HOSTNAME = "java.rmi.server.hostname";
+
+ /** The address the handlers listen on. */
+ private InetAddress listenAddress;
+ /** An address of the host the handlers do not listen on. */
+ private InetAddress otherAddress;
+ /** The default SSL context of the JVM before this test replaced it. */
+ private SSLContext defaultSSLContext;
+
+ @BeforeClass
+ public void setUp() throws Exception
+ {
+ TestCaseUtils.startServer();
+ TestCaseUtils.initializeTestBackend(true);
+ TestCaseUtils.addEntries(
+ "dn: " + USER_DN,
+ "objectClass: top",
+ "objectClass: person",
+ "objectClass: organizationalPerson",
+ "objectClass: inetOrgPerson",
+ "cn: JMX Reader",
+ "sn: Reader",
+ "userPassword: " + USER_PASSWORD,
+ "ds-privilege-name: jmx-read",
+ "ds-pwp-password-policy-dn: cn=Clear UserPassword Policy,cn=Password Policies,cn=config");
+
+ // The stub of the RMI connector advertises the local host, unless told otherwise: listen on
+ // the address that is not the local host's, or a session would get through even with a stub
+ // that does not advertise the listen address. When the local host is exactly 127.0.0.1, the
+ // JDK learns its host from the connector's own binding in the registry instead, and no
+ // choice of address makes that difference visible.
+ final InetAddress loopback = InetAddress.getByName("127.0.0.1");
+ final InetAddress external = getNonLoopbackAddress();
+ final boolean localHostIsLoopback =
+ loopback.getHostAddress().equals(InetAddress.getLocalHost().getHostAddress());
+ listenAddress = localHostIsLoopback ? external : loopback;
+ otherAddress = localHostIsLoopback ? loopback : external;
+
+ // The stub of an SSL connector carries an SslRMIClientSocketFactory, which takes the default
+ // SSL context of the JVM the first time anything uses it, and keeps it: that happens as soon as
+ // the connector starts, before any client of this test connects.
+ defaultSSLContext = SSLContext.getDefault();
+ final SSLContext blindContext = SSLContext.getInstance("TLS");
+ blindContext.init(null, new TrustManager[] { new BlindExtendedTrustManager() }, null);
+ SSLContext.setDefault(blindContext);
+ }
+
+ /**
+ * Trusts any server, whatever host name it is reached by: the factory checks the host name of
+ * the server when the JDK asks for it, and a trust manager that is not an extended one gets that
+ * check added by the JDK. The test certificate names no host.
+ */
+ private static final class BlindExtendedTrustManager extends X509ExtendedTrustManager
+ {
+ @Override
+ public void checkClientTrusted(X509Certificate[] chain, String authType)
+ {
+ // Trusts any client.
+ }
+
+ @Override
+ public void checkClientTrusted(X509Certificate[] chain, String authType, Socket socket)
+ {
+ // Trusts any client.
+ }
+
+ @Override
+ public void checkClientTrusted(X509Certificate[] chain, String authType, SSLEngine engine)
+ {
+ // Trusts any client.
+ }
+
+ @Override
+ public void checkServerTrusted(X509Certificate[] chain, String authType)
+ {
+ // Trusts any server.
+ }
+
+ @Override
+ public void checkServerTrusted(X509Certificate[] chain, String authType, Socket socket)
+ {
+ // Trusts any server.
+ }
+
+ @Override
+ public void checkServerTrusted(X509Certificate[] chain, String authType, SSLEngine engine)
+ {
+ // Trusts any server.
+ }
+
+ @Override
+ public X509Certificate[] getAcceptedIssuers()
+ {
+ return new X509Certificate[0];
+ }
+ }
+
+ @AfterClass(alwaysRun = true)
+ public void restoreDefaultSSLContext()
+ {
+ if (defaultSSLContext != null)
+ {
+ SSLContext.setDefault(defaultSSLContext);
+ }
+ }
+
+ @DataProvider
+ public Object[][] useSSL()
+ {
+ return new Object[][] { { false }, { true } };
+ }
+
+ @Test(dataProvider = "useSSL")
+ public void listensOnlyOnTheConfiguredListenAddress(boolean useSSL) throws Exception
+ {
+ final int[] ports = TestCaseUtils.findFreePorts(2);
+ final int listenPort = ports[0];
+ final int rmiPort = ports[1];
+
+ JmxConnectionHandler handler = newHandler(listenAddress, listenPort, rmiPort, useSSL);
+ try
+ {
+ // Starts the RMI registry and the RMI connector in this thread.
+ handler.run();
+ assertNotNull(handler.getRMIConnector().jmxRmiConnectorNoClientCertificate, "the RMI connector did not start");
+
+ assertListensOnlyOn(listenAddress, listenPort, rmiPort);
+ assertThat(handler.getListeners()).containsExactly(new HostPort(listenAddress.getHostAddress(), listenPort));
+ assertOpensASession(listenAddress, listenPort);
+ }
+ finally
+ {
+ handler.finalizeConnectionHandler(STOP_REASON);
+ }
+ }
+
+ /** A change that restarts the RMI connector keeps the listen address that is reported. */
+ @Test
+ public void reportsTheListenAddressAfterAChangeOfTheRmiPort() throws Exception
+ {
+ final int[] ports = TestCaseUtils.findFreePorts(3);
+ final int listenPort = ports[0];
+
+ JmxConnectionHandler handler = newHandler(listenAddress, listenPort, ports[1], false);
+ try
+ {
+ handler.run();
+ applyConfigurationChange(handler, newConfig(listenAddress, listenPort, ports[2], false));
+
+ assertListensOnlyOn(listenAddress, listenPort, ports[2]);
+ assertThat(handler.getListeners()).containsExactly(new HostPort(listenAddress.getHostAddress(), listenPort));
+ }
+ finally
+ {
+ handler.finalizeConnectionHandler(STOP_REASON);
+ }
+ }
+
+ /**
+ * A change of listen-address takes effect when the handler restarts: until then, the RMI
+ * registry, the RMI connector restarted by another change and the listeners stay on the address
+ * the handler was initialized with.
+ */
+ @Test
+ public void keepsItsListenAddressUntilItRestarts() throws Exception
+ {
+ final int[] ports = TestCaseUtils.findFreePorts(3);
+ final int listenPort = ports[0];
+
+ JmxConnectionHandler handler = newHandler(listenAddress, listenPort, ports[1], false);
+ try
+ {
+ handler.run();
+ applyConfigurationChange(handler, newConfig(otherAddress, listenPort, ports[2], false));
+
+ assertListensOnlyOn(listenAddress, listenPort, ports[2]);
+ assertThat(handler.getListeners()).containsExactly(new HostPort(listenAddress.getHostAddress(), listenPort));
+ assertOpensASession(listenAddress, listenPort);
+ }
+ finally
+ {
+ handler.finalizeConnectionHandler(STOP_REASON);
+ }
+ }
+
+ /**
+ * A handler restarted on the wildcard address no longer sends clients to the loopback address a
+ * previous one listened on.
+ */
+ @Test
+ public void stopsAdvertisingTheLoopbackAddressOnTheWildcardAddress() throws Exception
+ {
+ if (InetAddress.getLocalHost().isLoopbackAddress())
+ {
+ throw new SkipException("the JDK advertises a loopback address anyway");
+ }
+ final int[] ports = TestCaseUtils.findFreePorts(4);
+ JmxConnectionHandler handler = newHandler(InetAddress.getByName("127.0.0.1"), ports[0], ports[1], false);
+ try
+ {
+ handler.run();
+ }
+ finally
+ {
+ handler.finalizeConnectionHandler(STOP_REASON);
+ }
+
+ handler = newHandler(InetAddress.getByName("0.0.0.0"), ports[2], ports[3], false);
+ try
+ {
+ handler.run();
+
+ final Registry registry = LocateRegistry.getRegistry("127.0.0.1", ports[2]);
+ final String[] names = registry.list();
+ assertThat(names).isNotEmpty();
+ for (String name : names)
+ {
+ assertThat(registry.lookup(name).toString()).doesNotContain("[127.0.0.1:").doesNotContain("[0.0.0.0:");
+ }
+ }
+ finally
+ {
+ handler.finalizeConnectionHandler(STOP_REASON);
+ }
+ }
+
+ /**
+ * The host that the operator gives the stubs to advertise, behind a NAT for instance, is kept.
+ * <p>
+ * The name is one the server never records itself, which advertises addresses only. It is
+ * reachable, since the registry calls the stubs bound in it. Runs last: once cleared, the property
+ * leaves the JDK advertising the host it last read.
+ */
+ @Test(priority = 1)
+ public void keepsTheOperatorsRmiServerHostname() throws Exception
+ {
+ final int[] ports = TestCaseUtils.findFreePorts(2);
+ final String operatorsHostname = "localhost";
+ System.setProperty(RMI_SERVER_HOSTNAME, operatorsHostname);
+ JmxConnectionHandler handler = newHandler(listenAddress, ports[0], ports[1], false);
+ try
+ {
+ handler.run();
+ assertNotNull(handler.getRMIConnector().jmxRmiConnectorNoClientCertificate, "the RMI connector did not start");
+
+ assertEquals(System.getProperty(RMI_SERVER_HOSTNAME), operatorsHostname);
+ }
+ finally
+ {
+ handler.finalizeConnectionHandler(STOP_REASON);
+ System.clearProperty(RMI_SERVER_HOSTNAME);
+ }
+ }
+
+ private void assertListensOnlyOn(InetAddress address, int listenPort, int rmiPort) throws IOException
+ {
+ final InetAddress other = address.equals(listenAddress) ? otherAddress : listenAddress;
+ assertReachable(other);
+
+ assertTrue(isAcceptingConnections(address, listenPort),
+ "the RMI registry does not listen on " + address.getHostAddress());
+ assertFalse(isAcceptingConnections(other, listenPort),
+ "the RMI registry answers on " + other.getHostAddress() + ", which is not the listen address");
+ assertTrue(isAcceptingConnections(address, rmiPort),
+ "the RMI connector does not listen on " + address.getHostAddress());
+ assertFalse(isAcceptingConnections(other, rmiPort),
+ "the RMI connector answers on " + other.getHostAddress() + ", which is not the listen address");
+ }
+
+ /** A refusal on an address proves something only if a listener on every address answers there. */
+ private static void assertReachable(InetAddress address) throws IOException
+ {
+ try (ServerSocket control = new ServerSocket(0))
+ {
+ assertTrue(isAcceptingConnections(address, control.getLocalPort()),
+ address.getHostAddress() + " is not reachable from this host, so a refusal on it proves nothing");
+ }
+ }
+
+ /**
+ * Opens a JMX session through the RMI registry, then calls the RMI connector: the call goes to
+ * the address that the stub of the connector advertises.
+ */
+ private static void assertOpensASession(InetAddress address, int listenPort) throws Exception
+ {
+ final Map<String, Object> env = new HashMap<>();
+ env.put(JMXConnector.CREDENTIALS, new String[] { USER_DN, USER_PASSWORD });
+ env.put("jmx.remote.x.client.connection.check.period", 0);
+
+ try (OpendsJmxConnector connector = new OpendsJmxConnector(address.getHostAddress(), listenPort, env))
+ {
+ connector.connect();
+ assertThat(connector.getMBeanServerConnection().getMBeanCount()).isPositive();
+ }
+ }
+
+ private static void applyConfigurationChange(JmxConnectionHandler handler, JMXConnectionHandlerCfg config)
+ {
+ final ConfigChangeResult result = handler.applyConfigurationChange(config);
+ assertEquals(result.getResultCode(), ResultCode.SUCCESS, String.valueOf(result.getMessages()));
+ }
+
+ private static JmxConnectionHandler newHandler(InetAddress listenAddress, int listenPort, int rmiPort,
+ boolean useSSL) throws Exception
+ {
+ JmxConnectionHandler handler = new JmxConnectionHandler();
+ handler.initializeConnectionHandler(DirectoryServer.getInstance().getServerContext(),
+ newConfig(listenAddress, listenPort, rmiPort, useSSL));
+ return handler;
+ }
+
+ private static JMXConnectionHandlerCfg newConfig(InetAddress listenAddress, int listenPort, int rmiPort,
+ boolean useSSL) throws Exception
+ {
+ Entry handlerEntry = TestCaseUtils.makeEntry(
+ "dn: cn=Listen Address JMX Connection Handler,cn=Connection Handlers,cn=config",
+ "objectClass: top",
+ "objectClass: ds-cfg-connection-handler",
+ "objectClass: ds-cfg-jmx-connection-handler",
+ "cn: Listen Address JMX Connection Handler",
+ "ds-cfg-java-class: org.opends.server.protocols.jmx.JmxConnectionHandler",
+ "ds-cfg-enabled: true",
+ "ds-cfg-listen-address: " + listenAddress.getHostAddress(),
+ "ds-cfg-listen-port: " + listenPort,
+ "ds-cfg-rmi-port: " + rmiPort,
+ "ds-cfg-use-ssl: " + useSSL,
+ "ds-cfg-key-manager-provider: cn=JKS,cn=Key Manager Providers,cn=config",
+ "ds-cfg-ssl-cert-nickname: server-cert");
+ return InitializationUtils.getConfiguration(JMXConnectionHandlerCfgDefn.getInstance(), handlerEntry);
+ }
+}
--
Gitblit v1.10.0