| /* |
| * Copyright 2002-2007 Sun Microsystems, Inc. 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. Sun designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, |
| * CA 95054 USA or visit www.sun.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.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. |
| */ |
| 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); |
| |
| final boolean rebind = EnvHelp.computeBooleanFromString( |
| attributes, |
| JNDI_REBIND_ATTRIBUTE); |
| |
| 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); |
| } |
| |
| 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. */ |
| |
| protected void connectionOpened(String connectionId, String message, |
| Object userData) { |
| super.connectionOpened(connectionId, message, userData); |
| } |
| |
| protected void connectionClosed(String connectionId, String message, |
| Object userData) { |
| super.connectionClosed(connectionId, message, userData); |
| } |
| |
| 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 attributes) |
| throws IOException { |
| |
| final String protocol, host; |
| final int port; |
| |
| if (address == null) { |
| if (rmiServer instanceof javax.rmi.CORBA.Stub) |
| 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 env) throws IOException { |
| if (rmiServer instanceof javax.rmi.CORBA.Stub) |
| return "/ior/" + encodeIIOPStub(rmiServer, env); |
| else |
| 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); |
| } |
| |
| static String encodeIIOPStub(RMIServer rmiServer, Map env) |
| throws IOException { |
| try { |
| javax.rmi.CORBA.Stub stub = |
| (javax.rmi.CORBA.Stub) rmiServer; |
| return stub._orb().object_to_string(stub); |
| } catch (org.omg.CORBA.BAD_OPERATION 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 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 >= 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>(); |
| } |