You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
854 lines
32 KiB
854 lines
32 KiB
/*
|
|
* Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved.
|
|
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*/
|
|
|
|
package javax.management.remote.rmi;
|
|
|
|
|
|
import com.sun.jmx.remote.security.MBeanServerFileAccessController;
|
|
import com.sun.jmx.remote.internal.IIOPHelper;
|
|
import com.sun.jmx.remote.util.ClassLogger;
|
|
import com.sun.jmx.remote.util.EnvHelp;
|
|
|
|
import java.io.ByteArrayOutputStream;
|
|
import java.io.IOException;
|
|
import java.io.ObjectOutputStream;
|
|
import java.net.MalformedURLException;
|
|
import java.rmi.server.RMIClientSocketFactory;
|
|
import java.rmi.server.RMIServerSocketFactory;
|
|
import java.util.Collections;
|
|
import java.util.HashMap;
|
|
import java.util.HashSet;
|
|
import java.util.Hashtable;
|
|
import java.util.Map;
|
|
import java.util.Set;
|
|
|
|
import javax.management.InstanceNotFoundException;
|
|
import javax.management.MBeanServer;
|
|
|
|
import javax.management.remote.JMXConnectionNotification;
|
|
import javax.management.remote.JMXConnector;
|
|
import javax.management.remote.JMXConnectorServer;
|
|
import javax.management.remote.JMXServiceURL;
|
|
import javax.management.remote.MBeanServerForwarder;
|
|
|
|
import javax.naming.InitialContext;
|
|
import javax.naming.NamingException;
|
|
|
|
/**
|
|
* <p>A JMX API connector server that creates RMI-based connections
|
|
* from remote clients. Usually, such connector servers are made
|
|
* using {@link javax.management.remote.JMXConnectorServerFactory
|
|
* JMXConnectorServerFactory}. However, specialized applications can
|
|
* use this class directly, for example with an {@link RMIServerImpl}
|
|
* object.</p>
|
|
*
|
|
* @since 1.5
|
|
*/
|
|
public class RMIConnectorServer extends JMXConnectorServer {
|
|
/**
|
|
* <p>Name of the attribute that specifies whether the {@link
|
|
* RMIServer} stub that represents an RMI connector server should
|
|
* override an existing stub at the same address. The value
|
|
* associated with this attribute, if any, should be a string that
|
|
* is equal, ignoring case, to <code>"true"</code> or
|
|
* <code>"false"</code>. The default value is false.</p>
|
|
*/
|
|
public static final String JNDI_REBIND_ATTRIBUTE =
|
|
"jmx.remote.jndi.rebind";
|
|
|
|
/**
|
|
* <p>Name of the attribute that specifies the {@link
|
|
* RMIClientSocketFactory} for the RMI objects created in
|
|
* conjunction with this connector. The value associated with this
|
|
* attribute must be of type <code>RMIClientSocketFactory</code> and can
|
|
* only be specified in the <code>Map</code> argument supplied when
|
|
* creating a connector server.</p>
|
|
*/
|
|
public static final String RMI_CLIENT_SOCKET_FACTORY_ATTRIBUTE =
|
|
"jmx.remote.rmi.client.socket.factory";
|
|
|
|
/**
|
|
* <p>Name of the attribute that specifies the {@link
|
|
* RMIServerSocketFactory} for the RMI objects created in
|
|
* conjunction with this connector. The value associated with this
|
|
* attribute must be of type <code>RMIServerSocketFactory</code> and can
|
|
* only be specified in the <code>Map</code> argument supplied when
|
|
* creating a connector server.</p>
|
|
*/
|
|
public static final String RMI_SERVER_SOCKET_FACTORY_ATTRIBUTE =
|
|
"jmx.remote.rmi.server.socket.factory";
|
|
|
|
/**
|
|
* <p>Makes an <code>RMIConnectorServer</code>.
|
|
* This is equivalent to calling {@link #RMIConnectorServer(
|
|
* JMXServiceURL,Map,RMIServerImpl,MBeanServer)
|
|
* RMIConnectorServer(directoryURL,environment,null,null)}</p>
|
|
*
|
|
* @param url the URL defining how to create the connector server.
|
|
* Cannot be null.
|
|
*
|
|
* @param environment attributes governing the creation and
|
|
* storing of the RMI object. Can be null, which is equivalent to
|
|
* an empty Map.
|
|
*
|
|
* @exception IllegalArgumentException if <code>url</code> is null.
|
|
*
|
|
* @exception MalformedURLException if <code>url</code> does not
|
|
* conform to the syntax for an RMI connector, or if its protocol
|
|
* is not recognized by this implementation. Only "rmi" and "iiop"
|
|
* are valid when this constructor is used.
|
|
*
|
|
* @exception IOException if the connector server cannot be created
|
|
* for some reason or if it is inevitable that its {@link #start()
|
|
* start} method will fail.
|
|
*/
|
|
public RMIConnectorServer(JMXServiceURL url, Map<String,?> environment)
|
|
throws IOException {
|
|
this(url, environment, (MBeanServer) null);
|
|
}
|
|
|
|
/**
|
|
* <p>Makes an <code>RMIConnectorServer</code> for the given MBean
|
|
* server.
|
|
* This is equivalent to calling {@link #RMIConnectorServer(
|
|
* JMXServiceURL,Map,RMIServerImpl,MBeanServer)
|
|
* RMIConnectorServer(directoryURL,environment,null,mbeanServer)}</p>
|
|
*
|
|
* @param url the URL defining how to create the connector server.
|
|
* Cannot be null.
|
|
*
|
|
* @param environment attributes governing the creation and
|
|
* storing of the RMI object. Can be null, which is equivalent to
|
|
* an empty Map.
|
|
*
|
|
* @param mbeanServer the MBean server to which the new connector
|
|
* server is attached, or null if it will be attached by being
|
|
* registered as an MBean in the MBean server.
|
|
*
|
|
* @exception IllegalArgumentException if <code>url</code> is null.
|
|
*
|
|
* @exception MalformedURLException if <code>url</code> does not
|
|
* conform to the syntax for an RMI connector, or if its protocol
|
|
* is not recognized by this implementation. Only "rmi" and "iiop"
|
|
* are valid when this constructor is used.
|
|
*
|
|
* @exception IOException if the connector server cannot be created
|
|
* for some reason or if it is inevitable that its {@link #start()
|
|
* start} method will fail.
|
|
*/
|
|
public RMIConnectorServer(JMXServiceURL url, Map<String,?> environment,
|
|
MBeanServer mbeanServer)
|
|
throws IOException {
|
|
this(url, environment, (RMIServerImpl) null, mbeanServer);
|
|
}
|
|
|
|
/**
|
|
* <p>Makes an <code>RMIConnectorServer</code> for the given MBean
|
|
* server.</p>
|
|
*
|
|
* @param url the URL defining how to create the connector server.
|
|
* Cannot be null.
|
|
*
|
|
* @param environment attributes governing the creation and
|
|
* storing of the RMI object. Can be null, which is equivalent to
|
|
* an empty Map.
|
|
*
|
|
* @param rmiServerImpl An implementation of the RMIServer interface,
|
|
* consistent with the protocol type specified in <var>url</var>.
|
|
* If this parameter is non null, the protocol type specified by
|
|
* <var>url</var> is not constrained, and is assumed to be valid.
|
|
* Otherwise, only "rmi" and "iiop" will be recognized.
|
|
*
|
|
* @param mbeanServer the MBean server to which the new connector
|
|
* server is attached, or null if it will be attached by being
|
|
* registered as an MBean in the MBean server.
|
|
*
|
|
* @exception IllegalArgumentException if <code>url</code> is null.
|
|
*
|
|
* @exception MalformedURLException if <code>url</code> does not
|
|
* conform to the syntax for an RMI connector, or if its protocol
|
|
* is not recognized by this implementation. Only "rmi" and "iiop"
|
|
* are recognized when <var>rmiServerImpl</var> is null.
|
|
*
|
|
* @exception IOException if the connector server cannot be created
|
|
* for some reason or if it is inevitable that its {@link #start()
|
|
* start} method will fail.
|
|
*
|
|
* @see #start
|
|
*/
|
|
public RMIConnectorServer(JMXServiceURL url, Map<String,?> environment,
|
|
RMIServerImpl rmiServerImpl,
|
|
MBeanServer mbeanServer)
|
|
throws IOException {
|
|
super(mbeanServer);
|
|
|
|
if (url == null) throw new
|
|
IllegalArgumentException("Null JMXServiceURL");
|
|
if (rmiServerImpl == null) {
|
|
final String prt = url.getProtocol();
|
|
if (prt == null || !(prt.equals("rmi") || prt.equals("iiop"))) {
|
|
final String msg = "Invalid protocol type: " + prt;
|
|
throw new MalformedURLException(msg);
|
|
}
|
|
final String urlPath = url.getURLPath();
|
|
if (!urlPath.equals("")
|
|
&& !urlPath.equals("/")
|
|
&& !urlPath.startsWith("/jndi/")) {
|
|
final String msg = "URL path must be empty or start with " +
|
|
"/jndi/";
|
|
throw new MalformedURLException(msg);
|
|
}
|
|
}
|
|
|
|
if (environment == null)
|
|
this.attributes = Collections.emptyMap();
|
|
else {
|
|
EnvHelp.checkAttributes(environment);
|
|
this.attributes = Collections.unmodifiableMap(environment);
|
|
}
|
|
|
|
this.address = url;
|
|
this.rmiServerImpl = rmiServerImpl;
|
|
}
|
|
|
|
/**
|
|
* <p>Returns a client stub for this connector server. A client
|
|
* stub is a serializable object whose {@link
|
|
* JMXConnector#connect(Map) connect} method can be used to make
|
|
* one new connection to this connector server.</p>
|
|
*
|
|
* @param env client connection parameters of the same sort that
|
|
* could be provided to {@link JMXConnector#connect(Map)
|
|
* JMXConnector.connect(Map)}. Can be null, which is equivalent
|
|
* to an empty map.
|
|
*
|
|
* @return a client stub that can be used to make a new connection
|
|
* to this connector server.
|
|
*
|
|
* @exception UnsupportedOperationException if this connector
|
|
* server does not support the generation of client stubs.
|
|
*
|
|
* @exception IllegalStateException if the JMXConnectorServer is
|
|
* not started (see {@link #isActive()}).
|
|
*
|
|
* @exception IOException if a communications problem means that a
|
|
* stub cannot be created.
|
|
**/
|
|
public JMXConnector toJMXConnector(Map<String,?> env) throws IOException {
|
|
// The serialized for of rmiServerImpl is automatically
|
|
// a RMI server stub.
|
|
if (!isActive()) throw new
|
|
IllegalStateException("Connector is not active");
|
|
|
|
// Merge maps
|
|
Map<String, Object> usemap = new HashMap<String, Object>(
|
|
(this.attributes==null)?Collections.<String, Object>emptyMap():
|
|
this.attributes);
|
|
|
|
if (env != null) {
|
|
EnvHelp.checkAttributes(env);
|
|
usemap.putAll(env);
|
|
}
|
|
|
|
usemap = EnvHelp.filterAttributes(usemap);
|
|
|
|
final RMIServer stub=(RMIServer)rmiServerImpl.toStub();
|
|
|
|
return new RMIConnector(stub, usemap);
|
|
}
|
|
|
|
/**
|
|
* <p>Activates the connector server, that is starts listening for
|
|
* client connections. Calling this method when the connector
|
|
* server is already active has no effect. Calling this method
|
|
* when the connector server has been stopped will generate an
|
|
* <code>IOException</code>.</p>
|
|
*
|
|
* <p>The behavior of this method when called for the first time
|
|
* depends on the parameters that were supplied at construction,
|
|
* as described below.</p>
|
|
*
|
|
* <p>First, an object of a subclass of {@link RMIServerImpl} is
|
|
* required, to export the connector server through RMI:</p>
|
|
*
|
|
* <ul>
|
|
*
|
|
* <li>If an <code>RMIServerImpl</code> was supplied to the
|
|
* constructor, it is used.
|
|
*
|
|
* <li>Otherwise, if the protocol part of the
|
|
* <code>JMXServiceURL</code> supplied to the constructor was
|
|
* <code>iiop</code>, an object of type {@link RMIIIOPServerImpl}
|
|
* is created.
|
|
*
|
|
* <li>Otherwise, if the <code>JMXServiceURL</code>
|
|
* was null, or its protocol part was <code>rmi</code>, an object
|
|
* of type {@link RMIJRMPServerImpl} is created.
|
|
*
|
|
* <li>Otherwise, the implementation can create an
|
|
* implementation-specific {@link RMIServerImpl} or it can throw
|
|
* {@link MalformedURLException}.
|
|
*
|
|
* </ul>
|
|
*
|
|
* <p>If the given address includes a JNDI directory URL as
|
|
* specified in the package documentation for {@link
|
|
* javax.management.remote.rmi}, then this
|
|
* <code>RMIConnectorServer</code> will bootstrap by binding the
|
|
* <code>RMIServerImpl</code> to the given address.</p>
|
|
*
|
|
* <p>If the URL path part of the <code>JMXServiceURL</code> was
|
|
* empty or a single slash (<code>/</code>), then the RMI object
|
|
* will not be bound to a directory. Instead, a reference to it
|
|
* will be encoded in the URL path of the RMIConnectorServer
|
|
* address (returned by {@link #getAddress()}). The encodings for
|
|
* <code>rmi</code> and <code>iiop</code> are described in the
|
|
* package documentation for {@link
|
|
* javax.management.remote.rmi}.</p>
|
|
*
|
|
* <p>The behavior when the URL path is neither empty nor a JNDI
|
|
* directory URL, or when the protocol is neither <code>rmi</code>
|
|
* nor <code>iiop</code>, is implementation defined, and may
|
|
* include throwing {@link MalformedURLException} when the
|
|
* connector server is created or when it is started.</p>
|
|
*
|
|
* @exception IllegalStateException if the connector server has
|
|
* not been attached to an MBean server.
|
|
* @exception IOException if the connector server cannot be
|
|
* started, or in the case of the {@code iiop} protocol, that
|
|
* RMI/IIOP is not supported.
|
|
*/
|
|
public synchronized void start() throws IOException {
|
|
final boolean tracing = logger.traceOn();
|
|
|
|
if (state == STARTED) {
|
|
if (tracing) logger.trace("start", "already started");
|
|
return;
|
|
} else if (state == STOPPED) {
|
|
if (tracing) logger.trace("start", "already stopped");
|
|
throw new IOException("The server has been stopped.");
|
|
}
|
|
|
|
if (getMBeanServer() == null)
|
|
throw new IllegalStateException("This connector server is not " +
|
|
"attached to an MBean server");
|
|
|
|
// Check the internal access file property to see
|
|
// if an MBeanServerForwarder is to be provided
|
|
//
|
|
if (attributes != null) {
|
|
// Check if access file property is specified
|
|
//
|
|
String accessFile =
|
|
(String) attributes.get("jmx.remote.x.access.file");
|
|
if (accessFile != null) {
|
|
// Access file property specified, create an instance
|
|
// of the MBeanServerFileAccessController class
|
|
//
|
|
MBeanServerForwarder mbsf;
|
|
try {
|
|
mbsf = new MBeanServerFileAccessController(accessFile);
|
|
} catch (IOException e) {
|
|
throw EnvHelp.initCause(
|
|
new IllegalArgumentException(e.getMessage()), e);
|
|
}
|
|
// Set the MBeanServerForwarder
|
|
//
|
|
setMBeanServerForwarder(mbsf);
|
|
}
|
|
}
|
|
|
|
try {
|
|
if (tracing) logger.trace("start", "setting default class loader");
|
|
defaultClassLoader = EnvHelp.resolveServerClassLoader(
|
|
attributes, getMBeanServer());
|
|
} catch (InstanceNotFoundException infc) {
|
|
IllegalArgumentException x = new
|
|
IllegalArgumentException("ClassLoader not found: "+infc);
|
|
throw EnvHelp.initCause(x,infc);
|
|
}
|
|
|
|
if (tracing) logger.trace("start", "setting RMIServer object");
|
|
final RMIServerImpl rmiServer;
|
|
|
|
if (rmiServerImpl != null)
|
|
rmiServer = rmiServerImpl;
|
|
else
|
|
rmiServer = newServer();
|
|
|
|
rmiServer.setMBeanServer(getMBeanServer());
|
|
rmiServer.setDefaultClassLoader(defaultClassLoader);
|
|
rmiServer.setRMIConnectorServer(this);
|
|
rmiServer.export();
|
|
|
|
try {
|
|
if (tracing) logger.trace("start", "getting RMIServer object to export");
|
|
final RMIServer objref = objectToBind(rmiServer, attributes);
|
|
|
|
if (address != null && address.getURLPath().startsWith("/jndi/")) {
|
|
final String jndiUrl = address.getURLPath().substring(6);
|
|
|
|
if (tracing)
|
|
logger.trace("start", "Using external directory: " + jndiUrl);
|
|
|
|
String stringBoolean = (String) attributes.get(JNDI_REBIND_ATTRIBUTE);
|
|
final boolean rebind = EnvHelp.computeBooleanFromString( stringBoolean );
|
|
|
|
if (tracing)
|
|
logger.trace("start", JNDI_REBIND_ATTRIBUTE + "=" + rebind);
|
|
|
|
try {
|
|
if (tracing) logger.trace("start", "binding to " + jndiUrl);
|
|
|
|
final Hashtable<?, ?> usemap = EnvHelp.mapToHashtable(attributes);
|
|
|
|
bind(jndiUrl, usemap, objref, rebind);
|
|
|
|
boundJndiUrl = jndiUrl;
|
|
} catch (NamingException e) {
|
|
// fit e in the nested exception if we are on 1.4
|
|
throw newIOException("Cannot bind to URL ["+jndiUrl+"]: "
|
|
+ e, e);
|
|
}
|
|
} else {
|
|
// if jndiURL is null, we must encode the stub into the URL.
|
|
if (tracing) logger.trace("start", "Encoding URL");
|
|
|
|
encodeStubInAddress(objref, attributes);
|
|
|
|
if (tracing) logger.trace("start", "Encoded URL: " + this.address);
|
|
}
|
|
} catch (Exception e) {
|
|
try {
|
|
rmiServer.close();
|
|
} catch (Exception x) {
|
|
// OK: we are already throwing another exception
|
|
}
|
|
if (e instanceof RuntimeException)
|
|
throw (RuntimeException) e;
|
|
else if (e instanceof IOException)
|
|
throw (IOException) e;
|
|
else
|
|
throw newIOException("Got unexpected exception while " +
|
|
"starting the connector server: "
|
|
+ e, e);
|
|
}
|
|
|
|
rmiServerImpl = rmiServer;
|
|
|
|
synchronized(openedServers) {
|
|
openedServers.add(this);
|
|
}
|
|
|
|
state = STARTED;
|
|
|
|
if (tracing) {
|
|
logger.trace("start", "Connector Server Address = " + address);
|
|
logger.trace("start", "started.");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* <p>Deactivates the connector server, that is, stops listening for
|
|
* client connections. Calling this method will also close all
|
|
* client connections that were made by this server. After this
|
|
* method returns, whether normally or with an exception, the
|
|
* connector server will not create any new client
|
|
* connections.</p>
|
|
*
|
|
* <p>Once a connector server has been stopped, it cannot be started
|
|
* again.</p>
|
|
*
|
|
* <p>Calling this method when the connector server has already
|
|
* been stopped has no effect. Calling this method when the
|
|
* connector server has not yet been started will disable the
|
|
* connector server object permanently.</p>
|
|
*
|
|
* <p>If closing a client connection produces an exception, that
|
|
* exception is not thrown from this method. A {@link
|
|
* JMXConnectionNotification} is emitted from this MBean with the
|
|
* connection ID of the connection that could not be closed.</p>
|
|
*
|
|
* <p>Closing a connector server is a potentially slow operation.
|
|
* For example, if a client machine with an open connection has
|
|
* crashed, the close operation might have to wait for a network
|
|
* protocol timeout. Callers that do not want to block in a close
|
|
* operation should do it in a separate thread.</p>
|
|
*
|
|
* <p>This method calls the method {@link RMIServerImpl#close()
|
|
* close} on the connector server's <code>RMIServerImpl</code>
|
|
* object.</p>
|
|
*
|
|
* <p>If the <code>RMIServerImpl</code> was bound to a JNDI
|
|
* directory by the {@link #start() start} method, it is unbound
|
|
* from the directory by this method.</p>
|
|
*
|
|
* @exception IOException if the server cannot be closed cleanly,
|
|
* or if the <code>RMIServerImpl</code> cannot be unbound from the
|
|
* directory. When this exception is thrown, the server has
|
|
* already attempted to close all client connections, if
|
|
* appropriate; to call {@link RMIServerImpl#close()}; and to
|
|
* unbind the <code>RMIServerImpl</code> from its directory, if
|
|
* appropriate. All client connections are closed except possibly
|
|
* those that generated exceptions when the server attempted to
|
|
* close them.
|
|
*/
|
|
public void stop() throws IOException {
|
|
final boolean tracing = logger.traceOn();
|
|
|
|
synchronized (this) {
|
|
if (state == STOPPED) {
|
|
if (tracing) logger.trace("stop","already stopped.");
|
|
return;
|
|
} else if (state == CREATED) {
|
|
if (tracing) logger.trace("stop","not started yet.");
|
|
}
|
|
|
|
if (tracing) logger.trace("stop", "stopping.");
|
|
state = STOPPED;
|
|
}
|
|
|
|
synchronized(openedServers) {
|
|
openedServers.remove(this);
|
|
}
|
|
|
|
IOException exception = null;
|
|
|
|
// rmiServerImpl can be null if stop() called without start()
|
|
if (rmiServerImpl != null) {
|
|
try {
|
|
if (tracing) logger.trace("stop", "closing RMI server.");
|
|
rmiServerImpl.close();
|
|
} catch (IOException e) {
|
|
if (tracing) logger.trace("stop", "failed to close RMI server: " + e);
|
|
if (logger.debugOn()) logger.debug("stop",e);
|
|
exception = e;
|
|
}
|
|
}
|
|
|
|
if (boundJndiUrl != null) {
|
|
try {
|
|
if (tracing)
|
|
logger.trace("stop",
|
|
"unbind from external directory: " + boundJndiUrl);
|
|
|
|
final Hashtable<?, ?> usemap = EnvHelp.mapToHashtable(attributes);
|
|
|
|
InitialContext ctx =
|
|
new InitialContext(usemap);
|
|
|
|
ctx.unbind(boundJndiUrl);
|
|
|
|
ctx.close();
|
|
} catch (NamingException e) {
|
|
if (tracing) logger.trace("stop", "failed to unbind RMI server: "+e);
|
|
if (logger.debugOn()) logger.debug("stop",e);
|
|
// fit e in as the nested exception if we are on 1.4
|
|
if (exception == null)
|
|
exception = newIOException("Cannot bind to URL: " + e, e);
|
|
}
|
|
}
|
|
|
|
if (exception != null) throw exception;
|
|
|
|
if (tracing) logger.trace("stop", "stopped");
|
|
}
|
|
|
|
public synchronized boolean isActive() {
|
|
return (state == STARTED);
|
|
}
|
|
|
|
public JMXServiceURL getAddress() {
|
|
if (!isActive())
|
|
return null;
|
|
return address;
|
|
}
|
|
|
|
public Map<String,?> getAttributes() {
|
|
Map<String, ?> map = EnvHelp.filterAttributes(attributes);
|
|
return Collections.unmodifiableMap(map);
|
|
}
|
|
|
|
@Override
|
|
public synchronized
|
|
void setMBeanServerForwarder(MBeanServerForwarder mbsf) {
|
|
super.setMBeanServerForwarder(mbsf);
|
|
if (rmiServerImpl != null)
|
|
rmiServerImpl.setMBeanServer(getMBeanServer());
|
|
}
|
|
|
|
/* We repeat the definitions of connection{Opened,Closed,Failed}
|
|
here so that they are accessible to other classes in this package
|
|
even though they have protected access. */
|
|
|
|
@Override
|
|
protected void connectionOpened(String connectionId, String message,
|
|
Object userData) {
|
|
super.connectionOpened(connectionId, message, userData);
|
|
}
|
|
|
|
@Override
|
|
protected void connectionClosed(String connectionId, String message,
|
|
Object userData) {
|
|
super.connectionClosed(connectionId, message, userData);
|
|
}
|
|
|
|
@Override
|
|
protected void connectionFailed(String connectionId, String message,
|
|
Object userData) {
|
|
super.connectionFailed(connectionId, message, userData);
|
|
}
|
|
|
|
/**
|
|
* Bind a stub to a registry.
|
|
* @param jndiUrl URL of the stub in the registry, extracted
|
|
* from the <code>JMXServiceURL</code>.
|
|
* @param attributes A Hashtable containing environment parameters,
|
|
* built from the Map specified at this object creation.
|
|
* @param rmiServer The object to bind in the registry
|
|
* @param rebind true if the object must be rebound.
|
|
**/
|
|
void bind(String jndiUrl, Hashtable<?, ?> attributes,
|
|
RMIServer rmiServer, boolean rebind)
|
|
throws NamingException, MalformedURLException {
|
|
// if jndiURL is not null, we nust bind the stub to a
|
|
// directory.
|
|
InitialContext ctx =
|
|
new InitialContext(attributes);
|
|
|
|
if (rebind)
|
|
ctx.rebind(jndiUrl, rmiServer);
|
|
else
|
|
ctx.bind(jndiUrl, rmiServer);
|
|
ctx.close();
|
|
}
|
|
|
|
/**
|
|
* Creates a new RMIServerImpl.
|
|
**/
|
|
RMIServerImpl newServer() throws IOException {
|
|
final boolean iiop = isIiopURL(address,true);
|
|
final int port;
|
|
if (address == null)
|
|
port = 0;
|
|
else
|
|
port = address.getPort();
|
|
if (iiop)
|
|
return newIIOPServer(attributes);
|
|
else
|
|
return newJRMPServer(attributes, port);
|
|
}
|
|
|
|
/**
|
|
* Encode a stub into the JMXServiceURL.
|
|
* @param rmiServer The stub object to encode in the URL
|
|
* @param attributes A Map containing environment parameters,
|
|
* built from the Map specified at this object creation.
|
|
**/
|
|
private void encodeStubInAddress(
|
|
RMIServer rmiServer, Map<String, ?> attributes)
|
|
throws IOException {
|
|
|
|
final String protocol, host;
|
|
final int port;
|
|
|
|
if (address == null) {
|
|
if (IIOPHelper.isStub(rmiServer))
|
|
protocol = "iiop";
|
|
else
|
|
protocol = "rmi";
|
|
host = null; // will default to local host name
|
|
port = 0;
|
|
} else {
|
|
protocol = address.getProtocol();
|
|
host = (address.getHost().equals("")) ? null : address.getHost();
|
|
port = address.getPort();
|
|
}
|
|
|
|
final String urlPath = encodeStub(rmiServer, attributes);
|
|
|
|
address = new JMXServiceURL(protocol, host, port, urlPath);
|
|
}
|
|
|
|
static boolean isIiopURL(JMXServiceURL directoryURL, boolean strict)
|
|
throws MalformedURLException {
|
|
String protocol = directoryURL.getProtocol();
|
|
if (protocol.equals("rmi"))
|
|
return false;
|
|
else if (protocol.equals("iiop"))
|
|
return true;
|
|
else if (strict) {
|
|
|
|
throw new MalformedURLException("URL must have protocol " +
|
|
"\"rmi\" or \"iiop\": \"" +
|
|
protocol + "\"");
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Returns the IOR of the given rmiServer.
|
|
**/
|
|
static String encodeStub(
|
|
RMIServer rmiServer, Map<String, ?> env) throws IOException {
|
|
if (IIOPHelper.isStub(rmiServer))
|
|
return "/ior/" + encodeIIOPStub(rmiServer, env);
|
|
else
|
|
return "/stub/" + encodeJRMPStub(rmiServer, env);
|
|
}
|
|
|
|
static String encodeJRMPStub(
|
|
RMIServer rmiServer, Map<String, ?> env)
|
|
throws IOException {
|
|
ByteArrayOutputStream bout = new ByteArrayOutputStream();
|
|
ObjectOutputStream oout = new ObjectOutputStream(bout);
|
|
oout.writeObject(rmiServer);
|
|
oout.close();
|
|
byte[] bytes = bout.toByteArray();
|
|
return byteArrayToBase64(bytes);
|
|
}
|
|
|
|
static String encodeIIOPStub(
|
|
RMIServer rmiServer, Map<String, ?> env)
|
|
throws IOException {
|
|
try {
|
|
Object orb = IIOPHelper.getOrb(rmiServer);
|
|
return IIOPHelper.objectToString(orb, rmiServer);
|
|
} catch (RuntimeException x) {
|
|
throw newIOException(x.getMessage(), x);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Object that we will bind to the registry.
|
|
* This object is a stub connected to our RMIServerImpl.
|
|
**/
|
|
private static RMIServer objectToBind(
|
|
RMIServerImpl rmiServer, Map<String, ?> env)
|
|
throws IOException {
|
|
return RMIConnector.
|
|
connectStub((RMIServer)rmiServer.toStub(),env);
|
|
}
|
|
|
|
private static RMIServerImpl newJRMPServer(Map<String, ?> env, int port)
|
|
throws IOException {
|
|
RMIClientSocketFactory csf = (RMIClientSocketFactory)
|
|
env.get(RMI_CLIENT_SOCKET_FACTORY_ATTRIBUTE);
|
|
RMIServerSocketFactory ssf = (RMIServerSocketFactory)
|
|
env.get(RMI_SERVER_SOCKET_FACTORY_ATTRIBUTE);
|
|
return new RMIJRMPServerImpl(port, csf, ssf, env);
|
|
}
|
|
|
|
private static RMIServerImpl newIIOPServer(Map<String, ?> env)
|
|
throws IOException {
|
|
return new RMIIIOPServerImpl(env);
|
|
}
|
|
|
|
private static String byteArrayToBase64(byte[] a) {
|
|
int aLen = a.length;
|
|
int numFullGroups = aLen/3;
|
|
int numBytesInPartialGroup = aLen - 3*numFullGroups;
|
|
int resultLen = 4*((aLen + 2)/3);
|
|
final StringBuilder result = new StringBuilder(resultLen);
|
|
|
|
// Translate all full groups from byte array elements to Base64
|
|
int inCursor = 0;
|
|
for (int i=0; i<numFullGroups; i++) {
|
|
int byte0 = a[inCursor++] & 0xff;
|
|
int byte1 = a[inCursor++] & 0xff;
|
|
int byte2 = a[inCursor++] & 0xff;
|
|
result.append(intToAlpha[byte0 >> 2]);
|
|
result.append(intToAlpha[(byte0 << 4)&0x3f | (byte1 >> 4)]);
|
|
result.append(intToAlpha[(byte1 << 2)&0x3f | (byte2 >> 6)]);
|
|
result.append(intToAlpha[byte2 & 0x3f]);
|
|
}
|
|
|
|
// Translate partial group if present
|
|
if (numBytesInPartialGroup != 0) {
|
|
int byte0 = a[inCursor++] & 0xff;
|
|
result.append(intToAlpha[byte0 >> 2]);
|
|
if (numBytesInPartialGroup == 1) {
|
|
result.append(intToAlpha[(byte0 << 4) & 0x3f]);
|
|
result.append("==");
|
|
} else {
|
|
// assert numBytesInPartialGroup == 2;
|
|
int byte1 = a[inCursor++] & 0xff;
|
|
result.append(intToAlpha[(byte0 << 4)&0x3f | (byte1 >> 4)]);
|
|
result.append(intToAlpha[(byte1 << 2)&0x3f]);
|
|
result.append('=');
|
|
}
|
|
}
|
|
// assert inCursor == a.length;
|
|
// assert result.length() == resultLen;
|
|
return result.toString();
|
|
}
|
|
|
|
/**
|
|
* This array is a lookup table that translates 6-bit positive integer
|
|
* index values into their "Base64 Alphabet" equivalents as specified
|
|
* in Table 1 of RFC 2045.
|
|
*/
|
|
private static final char intToAlpha[] = {
|
|
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
|
|
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
|
|
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
|
|
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
|
|
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
|
|
};
|
|
|
|
/**
|
|
* Construct a new IOException with a nested exception.
|
|
* The nested exception is set only if JDK {@literal >= 1.4}
|
|
*/
|
|
private static IOException newIOException(String message,
|
|
Throwable cause) {
|
|
final IOException x = new IOException(message);
|
|
return EnvHelp.initCause(x,cause);
|
|
}
|
|
|
|
|
|
// Private variables
|
|
// -----------------
|
|
|
|
private static ClassLogger logger =
|
|
new ClassLogger("javax.management.remote.rmi", "RMIConnectorServer");
|
|
|
|
private JMXServiceURL address;
|
|
private RMIServerImpl rmiServerImpl;
|
|
private final Map<String, ?> attributes;
|
|
private ClassLoader defaultClassLoader = null;
|
|
|
|
private String boundJndiUrl;
|
|
|
|
// state
|
|
private static final int CREATED = 0;
|
|
private static final int STARTED = 1;
|
|
private static final int STOPPED = 2;
|
|
|
|
private int state = CREATED;
|
|
private final static Set<RMIConnectorServer> openedServers =
|
|
new HashSet<RMIConnectorServer>();
|
|
}
|