853 lines · 32.3 KB · Java
RawDownload
/* * Copyright (c) 2002, 2008, 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.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.     */    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,false);                 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()

Lines 1–500 of 853.