blob: 570ff01c4e23e21d8ff7075cf9fa51183e35f992 [file] [log] [blame]
/*
* 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.karaf.management;
import org.apache.karaf.jaas.config.KeystoreManager;
import org.apache.karaf.management.internal.MBeanInvocationHandler;
import org.osgi.framework.BundleContext;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Proxy;
import java.net.BindException;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.SocketException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.channels.ServerSocketChannel;
import java.rmi.AccessException;
import java.rmi.AlreadyBoundException;
import java.rmi.NotBoundException;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.RMIClientSocketFactory;
import java.rmi.server.RMIServerSocketFactory;
import java.rmi.server.UnicastRemoteObject;
import java.security.GeneralSecurityException;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;
import javax.management.JMException;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.management.remote.JMXConnectorServer;
import javax.management.remote.JMXConnectorServerFactory;
import javax.management.remote.JMXServiceURL;
import javax.management.remote.rmi.RMIConnectorServer;
import javax.management.remote.rmi.RMIJRMPServerImpl;
import javax.net.ServerSocketFactory;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLParameters;
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLServerSocketFactory;
import javax.rmi.ssl.SslRMIClientSocketFactory;
public class ConnectorServerFactory {
private enum AuthenticatorType {NONE, PASSWORD, CERTIFICATE}
private MBeanServer server;
private KarafMBeanServerGuard guard;
private String serviceUrl;
private boolean jmxmpEnabled;
private String jmxmpServiceUrl;
private String rmiServerHost;
private Map<String, Object> environment;
private Map<String, Object> jmxmpEnvironment;
private ObjectName objectName;
private ObjectName jmxmpObjectName;
private boolean threaded = false;
private boolean daemon = false;
private JMXConnectorServer connectorServer;
private Remote remoteServerStub;
private RMIJRMPServerImpl rmiServer;
private JMXConnectorServer jmxmpConnectorServer;
private long keyStoreAvailabilityTimeout = 5000;
private AuthenticatorType authenticatorType = AuthenticatorType.PASSWORD;
private boolean secured;
private KeystoreManager keystoreManager;
private String algorithm;
private String secureProtocol;
private String[] enabledProtocols;
private String[] enabledCipherSuites;
private String keyStore;
private String trustStore;
private String keyAlias;
private int port = Registry.REGISTRY_PORT;
private String host;
private Registry registry;
private boolean locate;
private boolean create = true;
private boolean locallyCreated;
private BundleContext bundleContext;
public MBeanServer getServer() {
return server;
}
public void setServer(MBeanServer server) {
this.server = server;
}
public KarafMBeanServerGuard getGuard() {
return guard;
}
public void setGuard(KarafMBeanServerGuard guard) {
this.guard = guard;
}
public String getServiceUrl() {
return serviceUrl;
}
public void setServiceUrl(String serviceUrl) {
this.serviceUrl = serviceUrl;
}
public boolean isJmxmpEnabled() {
return this.jmxmpEnabled;
}
public void setJmxmpEnabled(boolean jmxmpEnabled) {
this.jmxmpEnabled = jmxmpEnabled;
}
public String getJmxmpServiceUrl() {
return jmxmpServiceUrl;
}
public void setJmxmpServiceUrl(String jmxmpServiceUrl) {
this.jmxmpServiceUrl = jmxmpServiceUrl;
}
public String getRmiServerHost() {
return this.rmiServerHost;
}
public void setRmiServerHost(String rmiServerHost) {
this.rmiServerHost = rmiServerHost;
}
public Map<String, Object> getEnvironment() {
return environment;
}
public void setEnvironment(Map<String, Object> environment) {
this.environment = environment;
}
public Map<String, Object> getJmxmpEnvironment() {
return this.jmxmpEnvironment;
}
public void setJmxmpEnvironment(Map<String,Object> jmxmpEnvironment) {
this.jmxmpEnvironment = jmxmpEnvironment;
}
public ObjectName getObjectName() {
return objectName;
}
public void setObjectName(ObjectName objectName) {
this.objectName = objectName;
}
public ObjectName getJmxmpObjectName() {
return this.jmxmpObjectName;
}
public void setJmxmpObjectName(ObjectName jmxmpObjectName) {
this.jmxmpObjectName = jmxmpObjectName;
}
public boolean isThreaded() {
return threaded;
}
public void setThreaded(boolean threaded) {
this.threaded = threaded;
}
public boolean isDaemon() {
return daemon;
}
public void setDaemon(boolean daemon) {
this.daemon = daemon;
}
public String getAuthenticatorType() {
return this.authenticatorType.name().toLowerCase();
}
/**
* Authenticator type to use. Acceptable values are "none", "password", and "certificate"
*
* @param value The authenticator type: "none", "password", "certificate".
*/
public void setAuthenticatorType(String value) {
this.authenticatorType = AuthenticatorType.valueOf(value.toUpperCase());
}
/**
* Use this param to allow KeyStoreManager to wait for expected keystores to be loaded by other bundle
*
* @param keyStoreAvailabilityTimeout The keystore timeout.
*/
public void setKeyStoreAvailabilityTimeout(long keyStoreAvailabilityTimeout) {
this.keyStoreAvailabilityTimeout = keyStoreAvailabilityTimeout;
}
public boolean isSecured() {
return this.secured;
}
public void setSecured(boolean secured) {
this.secured = secured;
}
public void setKeystoreManager(KeystoreManager keystoreManager) {
this.keystoreManager = keystoreManager;
}
public KeystoreManager getKeystoreManager() {
return this.keystoreManager;
}
public String getKeyStore() {
return this.keyStore;
}
public void setKeyStore(String keyStore) {
this.keyStore = keyStore;
}
public String getTrustStore() {
return this.trustStore;
}
public void setTrustStore(String trustStore) {
this.trustStore = trustStore;
}
public String getKeyAlias() {
return this.keyAlias;
}
public void setKeyAlias(String keyAlias) {
this.keyAlias = keyAlias;
}
public String getAlgorithm() {
return this.algorithm;
}
/**
* Algorithm to use.
* As different JVMs have different implementations available, the default algorithm can be used by supplying the value "Default".
*
* @param algorithm the algorithm to use, or "Default" to use the default from {@link javax.net.ssl.KeyManagerFactory#getDefaultAlgorithm()}
*/
public void setAlgorithm(String algorithm) {
if ("default".equalsIgnoreCase(algorithm)) {
this.algorithm = KeyManagerFactory.getDefaultAlgorithm();
} else {
this.algorithm = algorithm;
}
}
public String getSecureProtocol() {
return this.secureProtocol;
}
public void setSecureProtocol(String secureProtocol) {
this.secureProtocol = secureProtocol;
}
private boolean isClientAuth() {
return this.authenticatorType.equals(AuthenticatorType.CERTIFICATE);
}
/**
* @return the create
*/
public boolean isCreate() {
return create;
}
/**
* @param create the create to set
*/
public void setCreate(boolean create) {
this.create = create;
}
/**
* @return the locate
*/
public boolean isLocate() {
return locate;
}
/**
* @param locate the locate to set
*/
public void setLocate(boolean locate) {
this.locate = locate;
}
/**
* @return the port
*/
public int getPort() {
return port;
}
/**
* @param port the port to set
*/
public void setPort(int port) {
this.port = port;
}
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
public void setBundleContext(BundleContext bundleContext) {
this.bundleContext = bundleContext;
}
public void init() throws Exception {
JMXServiceURL url = new JMXServiceURL(this.serviceUrl);
if (registry == null && locate) {
try {
Registry reg = LocateRegistry.getRegistry(host, getPort());
reg.list();
registry = reg;
} catch (RemoteException e) {
// ignore
}
}
if (registry == null && create) {
registry = new JmxRegistry(getPort(), getBindingName(url));
locallyCreated = true;
}
if (registry != null) {
// register the registry as an OSGi service
Hashtable<String, Object> props = new Hashtable<>();
props.put("port", getPort());
props.put("host", getHost());
bundleContext.registerService(Registry.class, registry, props);
}
if (this.server == null) {
throw new IllegalArgumentException("server must be set");
}
if ( isClientAuth() ) {
this.secured = true;
}
if ( this.secured ) {
setupSsl();
} else {
setupKarafRMIServerSocketFactory();
}
if ( ! AuthenticatorType.PASSWORD.equals( this.authenticatorType ) ) {
this.environment.remove( "jmx.remote.authenticator" );
}
MBeanInvocationHandler handler = new MBeanInvocationHandler(server, guard);
MBeanServer guardedServer = (MBeanServer) Proxy.newProxyInstance(server.getClass().getClassLoader(), new Class[]{ MBeanServer.class }, handler);
rmiServer = new RMIJRMPServerImpl(url.getPort(),
(RMIClientSocketFactory)environment.get(RMIConnectorServer.RMI_CLIENT_SOCKET_FACTORY_ATTRIBUTE),
(RMIServerSocketFactory)environment.get(RMIConnectorServer.RMI_SERVER_SOCKET_FACTORY_ATTRIBUTE), environment);
// Create the connector server now.
this.connectorServer = new RMIConnectorServer(url, environment, rmiServer, guardedServer);
if (this.objectName != null) {
this.server.registerMBean(this.connectorServer, this.objectName);
}
if (jmxmpEnabled) {
JMXServiceURL jmxmpUrl = new JMXServiceURL(this.jmxmpServiceUrl);
this.jmxmpConnectorServer = JMXConnectorServerFactory.newJMXConnectorServer(jmxmpUrl, this.jmxmpEnvironment, guardedServer);
if (this.jmxmpObjectName != null) {
this.server.registerMBean(this.jmxmpConnectorServer, this.jmxmpObjectName);
}
}
try {
if (this.threaded) {
Thread connectorThread = new Thread(() -> {
try {
Thread.currentThread().setContextClassLoader(ConnectorServerFactory.class.getClassLoader());
connectorServer.start();
remoteServerStub = rmiServer.toStub();
if (jmxmpEnabled && jmxmpConnectorServer != null) {
jmxmpConnectorServer.start();
}
} catch (IOException ex) {
if (ex.getCause() instanceof BindException){
// we want just the port message
int endIndex = ex.getMessage().indexOf("nested exception is");
// check to make sure we do not get an index out of range
if (endIndex > ex.getMessage().length() || endIndex < 0){
endIndex = ex.getMessage().length();
}
throw new RuntimeException("\n" + ex.getMessage().substring(0, endIndex) +
"\nYou may have started two containers. If you need to start a second container or the default ports are already in use " +
"update the config file etc/org.apache.karaf.management.cfg and change the Registry Port and Server Port to unused ports");
}
throw new RuntimeException("Could not start JMX connector server", ex);
}
});
connectorThread.setName("JMX Connector Thread [" + this.serviceUrl + "]");
connectorThread.setDaemon(this.daemon);
connectorThread.start();
} else {
this.connectorServer.start();
remoteServerStub = rmiServer.toStub();
if (jmxmpEnabled && jmxmpConnectorServer != null) {
jmxmpConnectorServer.start();
}
}
} catch (Exception ex) {
if (this.objectName != null) {
doUnregister(this.objectName);
}
if (jmxmpEnabled && this.jmxmpObjectName != null) {
doUnregister(this.jmxmpObjectName);
}
throw ex;
}
}
protected static String getBindingName(final JMXServiceURL jmxServiceURL) {
final String urlPath = jmxServiceURL.getURLPath();
try {
if (urlPath.startsWith("/jndi/")) {
return new URI(urlPath.substring(6)).getPath()
.replaceAll("^/+", "").replaceAll("/+$", "");
}
} catch (URISyntaxException e) {
// ignore
}
return "jmxrmi"; // use the default
}
public void destroy() throws Exception {
try {
if (this.connectorServer != null) {
this.connectorServer.stop();
}
if (this.jmxmpEnabled && this.jmxmpConnectorServer != null) {
this.jmxmpConnectorServer.stop();
}
if (registry != null && locallyCreated) {
Registry reg = registry;
registry = null;
UnicastRemoteObject.unexportObject(reg, true);
// clear TCPEndpointCache
try {
Class<?> cls = getClass().getClassLoader().loadClass("sun.rmi.transport.tcp.TCPEndpoint");
Field localEndpointsField = cls.getDeclaredField("localEndpoints");
Field ssfField = cls.getDeclaredField("ssf");
localEndpointsField.setAccessible(true);
ssfField.setAccessible(true);
Object localEndpoints = localEndpointsField.get(null);
if (localEndpoints != null) {
Map<Object, Object> map = (Map<Object, Object>) localEndpoints;
for (Iterator<Object> it = map.keySet().iterator(); it.hasNext(); ) {
Object key = it.next();
Object ssf = ssfField.get(key);
if (ssf != null && ssf.getClass().getPackage().getName().equals("org.apache.karaf.management")) {
it.remove();
}
}
}
} catch (Exception ignored) {
}
}
} finally {
if (this.objectName != null) {
doUnregister(this.objectName);
}
if (this.jmxmpEnabled && this.jmxmpObjectName != null) {
doUnregister(this.jmxmpObjectName);
}
}
}
protected void doUnregister(ObjectName objectName) {
try {
if (this.objectName != null && this.server.isRegistered(objectName)) {
this.server.unregisterMBean(objectName);
}
if (this.jmxmpObjectName != null && this.server.isRegistered(jmxmpObjectName)) {
this.server.unregisterMBean(jmxmpObjectName);
}
} catch (JMException ex) {
// Ignore
}
}
private void setupSsl() throws GeneralSecurityException {
SSLServerSocketFactory sssf = keystoreManager.createSSLServerFactory(null, secureProtocol, algorithm, keyStore, keyAlias, trustStore,keyStoreAvailabilityTimeout);
RMIServerSocketFactory rssf = new KarafSslRMIServerSocketFactory(sssf, isClientAuth(), getRmiServerHost(), getEnabledProtocols(),
getEnabledCipherSuites());
RMIClientSocketFactory rcsf = new SslRMIClientSocketFactory();
environment.put(RMIConnectorServer.RMI_SERVER_SOCKET_FACTORY_ATTRIBUTE, rssf);
environment.put(RMIConnectorServer.RMI_CLIENT_SOCKET_FACTORY_ATTRIBUTE, rcsf);
//@TODO secure RMI connector as well?
//env.put("com.sun.jndi.rmi.factory.socket", rcsf);
}
private void setupKarafRMIServerSocketFactory() {
RMIServerSocketFactory rssf = new KarafRMIServerSocketFactory(getRmiServerHost());
environment.put(RMIConnectorServer.RMI_SERVER_SOCKET_FACTORY_ATTRIBUTE, rssf);
}
private static class KarafSslRMIServerSocketFactory implements RMIServerSocketFactory {
private SSLServerSocketFactory sssf;
private boolean clientAuth;
private String rmiServerHost;
private String[] enabledProtocols;
private String[] enabledCipherSuites;
public KarafSslRMIServerSocketFactory(SSLServerSocketFactory sssf, boolean clientAuth, String rmiServerHost,
String[] enabledProtocols,
String[] enabledCipherSuites) {
this.sssf = sssf;
this.clientAuth = clientAuth;
this.rmiServerHost = rmiServerHost;
this.enabledProtocols = enabledProtocols;
this.enabledCipherSuites = enabledCipherSuites;
}
public ServerSocket createServerSocket(int port) throws IOException {
InetAddress host = InetAddress.getByName(rmiServerHost);
if (host.isLoopbackAddress()) {
final SSLServerSocket ss = (SSLServerSocket) sssf.createServerSocket(port, 50);
ss.setNeedClientAuth(clientAuth);
if (this.enabledProtocols != null && this.enabledProtocols.length > 0) {
ss.setEnabledProtocols(this.enabledProtocols);
}
if (this.enabledCipherSuites != null && this.enabledCipherSuites.length > 0) {
ss.setEnabledCipherSuites(this.enabledCipherSuites);
}
return new LocalOnlySSLServerSocket(ss);
} else {
final SSLServerSocket ss = (SSLServerSocket) sssf.createServerSocket(port, 50, InetAddress.getByName(rmiServerHost));
ss.setNeedClientAuth(clientAuth);
if (this.enabledProtocols != null && this.enabledProtocols.length > 0) {
ss.setEnabledProtocols(this.enabledProtocols);
}
if (this.enabledCipherSuites != null && this.enabledCipherSuites.length > 0) {
ss.setEnabledCipherSuites(this.enabledCipherSuites);
}
return ss;
}
}
}
private static class KarafRMIServerSocketFactory implements RMIServerSocketFactory {
private String rmiServerHost;
public KarafRMIServerSocketFactory(String rmiServerHost) {
this.rmiServerHost = rmiServerHost;
}
public ServerSocket createServerSocket(int port) throws IOException {
InetAddress host = InetAddress.getByName(rmiServerHost);
if (host.isLoopbackAddress()) {
final ServerSocket ss = ServerSocketFactory.getDefault().createServerSocket(port, 50);
return new LocalOnlyServerSocket(ss);
} else {
final ServerSocket ss = ServerSocketFactory.getDefault().createServerSocket(port, 50, InetAddress.getByName(rmiServerHost));
return ss;
}
}
}
private static class LocalOnlyServerSocket extends ServerSocket {
private final ServerSocket ss;
public LocalOnlyServerSocket(ServerSocket ss) throws IOException {
this.ss = ss;
}
@Override
public void bind(SocketAddress endpoint) throws IOException {
ss.bind(endpoint);
}
@Override
public void bind(SocketAddress endpoint, int backlog) throws IOException {
ss.bind(endpoint, backlog);
}
@Override
public InetAddress getInetAddress() {
return ss.getInetAddress();
}
@Override
public int getLocalPort() {
return ss.getLocalPort();
}
@Override
public SocketAddress getLocalSocketAddress() {
return ss.getLocalSocketAddress();
}
@Override
public Socket accept() throws IOException {
return checkLocal(ss.accept());
}
@Override
public void close() throws IOException {
ss.close();
}
@Override
public ServerSocketChannel getChannel() {
return ss.getChannel();
}
@Override
public boolean isBound() {
return ss.isBound();
}
@Override
public boolean isClosed() {
return ss.isClosed();
}
@Override
public void setSoTimeout(int timeout) throws SocketException {
ss.setSoTimeout(timeout);
}
@Override
public int getSoTimeout() throws IOException {
return ss.getSoTimeout();
}
@Override
public void setReuseAddress(boolean on) throws SocketException {
ss.setReuseAddress(on);
}
@Override
public boolean getReuseAddress() throws SocketException {
return ss.getReuseAddress();
}
@Override
public String toString() {
return ss.toString();
}
@Override
public void setReceiveBufferSize(int size) throws SocketException {
ss.setReceiveBufferSize(size);
}
@Override
public int getReceiveBufferSize() throws SocketException {
return ss.getReceiveBufferSize();
}
@Override
public void setPerformancePreferences(int connectionTime, int latency, int bandwidth) {
ss.setPerformancePreferences(connectionTime, latency, bandwidth);
}
}
private static class LocalOnlySSLServerSocket extends SSLServerSocket {
private final SSLServerSocket ss;
public LocalOnlySSLServerSocket(SSLServerSocket ss) throws IOException {
this.ss = ss;
}
@Override
public void bind(SocketAddress endpoint) throws IOException {
ss.bind(endpoint);
}
@Override
public void bind(SocketAddress endpoint, int backlog) throws IOException {
ss.bind(endpoint, backlog);
}
@Override
public InetAddress getInetAddress() {
return ss.getInetAddress();
}
@Override
public int getLocalPort() {
return ss.getLocalPort();
}
@Override
public SocketAddress getLocalSocketAddress() {
return ss.getLocalSocketAddress();
}
@Override
public Socket accept() throws IOException {
return checkLocal(ss.accept());
}
@Override
public void close() throws IOException {
ss.close();
}
@Override
public ServerSocketChannel getChannel() {
return ss.getChannel();
}
@Override
public boolean isBound() {
return ss.isBound();
}
@Override
public boolean isClosed() {
return ss.isClosed();
}
@Override
public void setSoTimeout(int timeout) throws SocketException {
ss.setSoTimeout(timeout);
}
@Override
public int getSoTimeout() throws IOException {
return ss.getSoTimeout();
}
@Override
public void setReuseAddress(boolean on) throws SocketException {
ss.setReuseAddress(on);
}
@Override
public boolean getReuseAddress() throws SocketException {
return ss.getReuseAddress();
}
@Override
public String toString() {
return ss.toString();
}
@Override
public void setReceiveBufferSize(int size) throws SocketException {
ss.setReceiveBufferSize(size);
}
@Override
public int getReceiveBufferSize() throws SocketException {
return ss.getReceiveBufferSize();
}
@Override
public void setPerformancePreferences(int connectionTime, int latency, int bandwidth) {
ss.setPerformancePreferences(connectionTime, latency, bandwidth);
}
public String[] getEnabledCipherSuites() {
return ss.getEnabledCipherSuites();
}
public void setEnabledCipherSuites(String[] strings) {
ss.setEnabledCipherSuites(strings);
}
public String[] getSupportedCipherSuites() {
return ss.getSupportedCipherSuites();
}
public String[] getSupportedProtocols() {
return ss.getSupportedProtocols();
}
public String[] getEnabledProtocols() {
return ss.getEnabledProtocols();
}
public void setEnabledProtocols(String[] strings) {
ss.setEnabledProtocols(strings);
}
public void setNeedClientAuth(boolean b) {
ss.setNeedClientAuth(b);
}
public boolean getNeedClientAuth() {
return ss.getNeedClientAuth();
}
public void setWantClientAuth(boolean b) {
ss.setWantClientAuth(b);
}
public boolean getWantClientAuth() {
return ss.getWantClientAuth();
}
public void setUseClientMode(boolean b) {
ss.setUseClientMode(b);
}
public boolean getUseClientMode() {
return ss.getUseClientMode();
}
public void setEnableSessionCreation(boolean b) {
ss.setEnableSessionCreation(b);
}
public boolean getEnableSessionCreation() {
return ss.getEnableSessionCreation();
}
public SSLParameters getSSLParameters() {
return ss.getSSLParameters();
}
public void setSSLParameters(SSLParameters sslParameters) {
ss.setSSLParameters(sslParameters);
}
}
private static Socket checkLocal(Socket socket) throws IOException {
InetAddress addr = socket.getInetAddress();
if (addr != null) {
if (addr.isLoopbackAddress()) {
return socket;
} else {
try {
Enumeration<NetworkInterface> nis = NetworkInterface.getNetworkInterfaces();
while (nis.hasMoreElements()) {
NetworkInterface ni = nis.nextElement();
Enumeration<InetAddress> ads = ni.getInetAddresses();
while (ads.hasMoreElements()) {
InetAddress ad = ads.nextElement();
if (ad.equals(addr)) {
return socket;
}
}
}
} catch (SocketException e) {
// Ignore
}
}
}
try {
socket.close();
} catch (Exception e) {
// Ignore
}
throw new IOException("Only connections from clients running on the host where the RMI remote objects have been exported are accepted.");
}
public String[] getEnabledProtocols() {
return enabledProtocols;
}
public void setEnabledProtocols(String[] enabledProtocols) {
this.enabledProtocols = enabledProtocols;
}
public String[] getEnabledCipherSuites() {
return enabledCipherSuites;
}
public void setEnabledCipherSuites(String[] enabledCipherSuites) {
this.enabledCipherSuites = enabledCipherSuites;
}
/*
* Better to use the internal API than re-invent the wheel.
*/
@SuppressWarnings("restriction")
private class JmxRegistry extends sun.rmi.registry.RegistryImpl {
private final String lookupName;
JmxRegistry(final int port, final String lookupName) throws RemoteException {
super(port);
this.lookupName = lookupName;
}
@Override
public Remote lookup(String s) throws RemoteException, NotBoundException {
return lookupName.equals(s) ? remoteServerStub : null;
}
@Override
public void bind(String s, Remote remote) throws RemoteException, AlreadyBoundException, AccessException {
}
@Override
public void unbind(String s) throws RemoteException, NotBoundException, AccessException {
}
@Override
public void rebind(String s, Remote remote) throws RemoteException, AccessException {
}
@Override
public String[] list() throws RemoteException {
return new String[] {lookupName};
}
}
}