--- old/jdk/src/java.management/share/classes/javax/management/remote/rmi/RMIConnectorServer.java 2017-02-01 16:12:54.000000000 +0000 +++ /dev/null 2017-02-01 16:12:54.000000000 +0000 @@ -1,819 +0,0 @@ -/* - * Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package javax.management.remote.rmi; - - -import com.sun.jmx.remote.security.MBeanServerFileAccessController; -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.JMXAuthenticator; - -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; - -/** - *

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.

- * - * @since 1.5 - */ -public class RMIConnectorServer extends JMXConnectorServer { - /** - *

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 "true" or - * "false". The default value is false.

- */ - public static final String JNDI_REBIND_ATTRIBUTE = - "jmx.remote.jndi.rebind"; - - /** - *

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 RMIClientSocketFactory and can - * only be specified in the Map argument supplied when - * creating a connector server.

- */ - public static final String RMI_CLIENT_SOCKET_FACTORY_ATTRIBUTE = - "jmx.remote.rmi.client.socket.factory"; - - /** - *

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 RMIServerSocketFactory and can - * only be specified in the Map argument supplied when - * creating a connector server.

- */ - public static final String RMI_SERVER_SOCKET_FACTORY_ATTRIBUTE = - "jmx.remote.rmi.server.socket.factory"; - - /** - * Name of the attribute that specifies a list of class names acceptable - * as parameters to the {@link RMIServer#newClient(java.lang.Object) RMIServer.newClient()} - * remote method call. - *

- * This list of classes should correspond to the transitive closure of the - * credentials class (or classes) used by the installed {@linkplain JMXAuthenticator} - * associated with the {@linkplain RMIServer} implementation. - *

- * If the attribute is not set, or is null, then any class is - * deemed acceptable. - */ - public static final String CREDENTIAL_TYPES = - "jmx.remote.rmi.server.credential.types"; - - /** - *

Makes an RMIConnectorServer. - * This is equivalent to calling {@link #RMIConnectorServer( - * JMXServiceURL,Map,RMIServerImpl,MBeanServer) - * RMIConnectorServer(directoryURL,environment,null,null)}

- * - * @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 url is null. - * - * @exception MalformedURLException if url does not - * conform to the syntax for an RMI connector, or if its protocol - * is not recognized by this implementation. Only "rmi" is 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 environment) - throws IOException { - this(url, environment, (MBeanServer) null); - } - - /** - *

Makes an RMIConnectorServer for the given MBean - * server. - * This is equivalent to calling {@link #RMIConnectorServer( - * JMXServiceURL,Map,RMIServerImpl,MBeanServer) - * RMIConnectorServer(directoryURL,environment,null,mbeanServer)}

- * - * @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 url is null. - * - * @exception MalformedURLException if url does not - * conform to the syntax for an RMI connector, or if its protocol - * is not recognized by this implementation. Only "rmi" is 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 environment, - MBeanServer mbeanServer) - throws IOException { - this(url, environment, (RMIServerImpl) null, mbeanServer); - } - - /** - *

Makes an RMIConnectorServer for the given MBean - * server.

- * - * @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 url. - * If this parameter is non null, the protocol type specified by - * url is not constrained, and is assumed to be valid. - * Otherwise, only "rmi" 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 url is null. - * - * @exception MalformedURLException if url does not - * conform to the syntax for an RMI connector, or if its protocol - * is not recognized by this implementation. Only "rmi" is recognized - * when rmiServerImpl 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 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"))) { - 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; - } - - /** - *

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.

- * - * @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 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 usemap = new HashMap( - (this.attributes==null)?Collections.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); - } - - /** - *

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 - * IOException.

- * - *

The behavior of this method when called for the first time - * depends on the parameters that were supplied at construction, - * as described below.

- * - *

First, an object of a subclass of {@link RMIServerImpl} is - * required, to export the connector server through RMI:

- * - *
    - * - *
  • If an RMIServerImpl was supplied to the - * constructor, it is used. - * - *
  • Otherwise, if the JMXServiceURL - * was null, or its protocol part was rmi, an object - * of type {@link RMIJRMPServerImpl} is created. - * - *
  • Otherwise, the implementation can create an - * implementation-specific {@link RMIServerImpl} or it can throw - * {@link MalformedURLException}. - * - *
- * - *

If the given address includes a JNDI directory URL as - * specified in the package documentation for {@link - * javax.management.remote.rmi}, then this - * RMIConnectorServer will bootstrap by binding the - * RMIServerImpl to the given address.

- * - *

If the URL path part of the JMXServiceURL was - * empty or a single slash (/), 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 - * rmi are described in the package documentation for - * {@link javax.management.remote.rmi}.

- * - *

The behavior when the URL path is neither empty nor a JNDI - * directory URL, or when the protocol is not rmi, - * is implementation defined, and may include throwing - * {@link MalformedURLException} when the connector server is created - * or when it is started.

- * - * @exception IllegalStateException if the connector server has - * not been attached to an MBean server. - * @exception IOException if the connector server cannot be - * started. - */ - 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."); - } - } - - /** - *

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.

- * - *

Once a connector server has been stopped, it cannot be started - * again.

- * - *

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.

- * - *

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.

- * - *

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.

- * - *

This method calls the method {@link RMIServerImpl#close() - * close} on the connector server's RMIServerImpl - * object.

- * - *

If the RMIServerImpl was bound to a JNDI - * directory by the {@link #start() start} method, it is unbound - * from the directory by this method.

- * - * @exception IOException if the server cannot be closed cleanly, - * or if the RMIServerImpl 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 RMIServerImpl 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 getAttributes() { - Map 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 JMXServiceURL. - * @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 int port; - if (address == null) - port = 0; - else - port = address.getPort(); - - 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 attributes) - throws IOException { - - final String protocol, host; - final int port; - - if (address == null) { - 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); - } - - /** - * Returns the IOR of the given rmiServer. - **/ - static String encodeStub( - RMIServer rmiServer, Map env) throws IOException { - return "/stub/" + encodeJRMPStub(rmiServer, env); - } - - static String encodeJRMPStub( - RMIServer rmiServer, Map env) - throws IOException { - ByteArrayOutputStream bout = new ByteArrayOutputStream(); - ObjectOutputStream oout = new ObjectOutputStream(bout); - oout.writeObject(rmiServer); - oout.close(); - byte[] bytes = bout.toByteArray(); - return byteArrayToBase64(bytes); - } - - /** - * Object that we will bind to the registry. - * This object is a stub connected to our RMIServerImpl. - **/ - private static RMIServer objectToBind( - RMIServerImpl rmiServer, Map env) - throws IOException { - return (RMIServer)rmiServer.toStub(); - } - - private static RMIServerImpl newJRMPServer(Map 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 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> 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 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 openedServers = - new HashSet(); -} --- /dev/null 2017-02-01 16:12:54.000000000 +0000 +++ new/jdk/src/java.management.rmi/share/classes/javax/management/remote/rmi/RMIConnectorServer.java 2017-02-01 16:12:54.000000000 +0000 @@ -0,0 +1,819 @@ +/* + * Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package javax.management.remote.rmi; + + +import com.sun.jmx.remote.security.MBeanServerFileAccessController; +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.JMXAuthenticator; + +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; + +/** + *

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.

+ * + * @since 1.5 + */ +public class RMIConnectorServer extends JMXConnectorServer { + /** + *

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 "true" or + * "false". The default value is false.

+ */ + public static final String JNDI_REBIND_ATTRIBUTE = + "jmx.remote.jndi.rebind"; + + /** + *

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 RMIClientSocketFactory and can + * only be specified in the Map argument supplied when + * creating a connector server.

+ */ + public static final String RMI_CLIENT_SOCKET_FACTORY_ATTRIBUTE = + "jmx.remote.rmi.client.socket.factory"; + + /** + *

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 RMIServerSocketFactory and can + * only be specified in the Map argument supplied when + * creating a connector server.

+ */ + public static final String RMI_SERVER_SOCKET_FACTORY_ATTRIBUTE = + "jmx.remote.rmi.server.socket.factory"; + + /** + * Name of the attribute that specifies a list of class names acceptable + * as parameters to the {@link RMIServer#newClient(java.lang.Object) RMIServer.newClient()} + * remote method call. + *

+ * This list of classes should correspond to the transitive closure of the + * credentials class (or classes) used by the installed {@linkplain JMXAuthenticator} + * associated with the {@linkplain RMIServer} implementation. + *

+ * If the attribute is not set, or is null, then any class is + * deemed acceptable. + */ + public static final String CREDENTIAL_TYPES = + "jmx.remote.rmi.server.credential.types"; + + /** + *

Makes an RMIConnectorServer. + * This is equivalent to calling {@link #RMIConnectorServer( + * JMXServiceURL,Map,RMIServerImpl,MBeanServer) + * RMIConnectorServer(directoryURL,environment,null,null)}

+ * + * @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 url is null. + * + * @exception MalformedURLException if url does not + * conform to the syntax for an RMI connector, or if its protocol + * is not recognized by this implementation. Only "rmi" is 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 environment) + throws IOException { + this(url, environment, (MBeanServer) null); + } + + /** + *

Makes an RMIConnectorServer for the given MBean + * server. + * This is equivalent to calling {@link #RMIConnectorServer( + * JMXServiceURL,Map,RMIServerImpl,MBeanServer) + * RMIConnectorServer(directoryURL,environment,null,mbeanServer)}

+ * + * @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 url is null. + * + * @exception MalformedURLException if url does not + * conform to the syntax for an RMI connector, or if its protocol + * is not recognized by this implementation. Only "rmi" is 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 environment, + MBeanServer mbeanServer) + throws IOException { + this(url, environment, (RMIServerImpl) null, mbeanServer); + } + + /** + *

Makes an RMIConnectorServer for the given MBean + * server.

+ * + * @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 url. + * If this parameter is non null, the protocol type specified by + * url is not constrained, and is assumed to be valid. + * Otherwise, only "rmi" 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 url is null. + * + * @exception MalformedURLException if url does not + * conform to the syntax for an RMI connector, or if its protocol + * is not recognized by this implementation. Only "rmi" is recognized + * when rmiServerImpl 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 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"))) { + 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; + } + + /** + *

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.

+ * + * @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 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 usemap = new HashMap( + (this.attributes==null)?Collections.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); + } + + /** + *

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 + * IOException.

+ * + *

The behavior of this method when called for the first time + * depends on the parameters that were supplied at construction, + * as described below.

+ * + *

First, an object of a subclass of {@link RMIServerImpl} is + * required, to export the connector server through RMI:

+ * + *
    + * + *
  • If an RMIServerImpl was supplied to the + * constructor, it is used. + * + *
  • Otherwise, if the JMXServiceURL + * was null, or its protocol part was rmi, an object + * of type {@link RMIJRMPServerImpl} is created. + * + *
  • Otherwise, the implementation can create an + * implementation-specific {@link RMIServerImpl} or it can throw + * {@link MalformedURLException}. + * + *
+ * + *

If the given address includes a JNDI directory URL as + * specified in the package documentation for {@link + * javax.management.remote.rmi}, then this + * RMIConnectorServer will bootstrap by binding the + * RMIServerImpl to the given address.

+ * + *

If the URL path part of the JMXServiceURL was + * empty or a single slash (/), 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 + * rmi are described in the package documentation for + * {@link javax.management.remote.rmi}.

+ * + *

The behavior when the URL path is neither empty nor a JNDI + * directory URL, or when the protocol is not rmi, + * is implementation defined, and may include throwing + * {@link MalformedURLException} when the connector server is created + * or when it is started.

+ * + * @exception IllegalStateException if the connector server has + * not been attached to an MBean server. + * @exception IOException if the connector server cannot be + * started. + */ + 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."); + } + } + + /** + *

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.

+ * + *

Once a connector server has been stopped, it cannot be started + * again.

+ * + *

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.

+ * + *

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.

+ * + *

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.

+ * + *

This method calls the method {@link RMIServerImpl#close() + * close} on the connector server's RMIServerImpl + * object.

+ * + *

If the RMIServerImpl was bound to a JNDI + * directory by the {@link #start() start} method, it is unbound + * from the directory by this method.

+ * + * @exception IOException if the server cannot be closed cleanly, + * or if the RMIServerImpl 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 RMIServerImpl 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 getAttributes() { + Map 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 JMXServiceURL. + * @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 int port; + if (address == null) + port = 0; + else + port = address.getPort(); + + 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 attributes) + throws IOException { + + final String protocol, host; + final int port; + + if (address == null) { + 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); + } + + /** + * Returns the IOR of the given rmiServer. + **/ + static String encodeStub( + RMIServer rmiServer, Map env) throws IOException { + return "/stub/" + encodeJRMPStub(rmiServer, env); + } + + static String encodeJRMPStub( + RMIServer rmiServer, Map env) + throws IOException { + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + ObjectOutputStream oout = new ObjectOutputStream(bout); + oout.writeObject(rmiServer); + oout.close(); + byte[] bytes = bout.toByteArray(); + return byteArrayToBase64(bytes); + } + + /** + * Object that we will bind to the registry. + * This object is a stub connected to our RMIServerImpl. + **/ + private static RMIServer objectToBind( + RMIServerImpl rmiServer, Map env) + throws IOException { + return (RMIServer)rmiServer.toStub(); + } + + private static RMIServerImpl newJRMPServer(Map 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 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> 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 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 openedServers = + new HashSet(); +}