/* * Copyright (c) 2002, 2017, 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.ObjectInputFilter; 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.
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.
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.
* 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. * * @deprecated Use {@link #CREDENTIALS_FILTER_PATTERN} with a * {@linkplain java.io.ObjectInputFilter.Config#createFilter * filter pattern} string instead. */ @Deprecated(since="10", forRemoval=true) public static final String CREDENTIAL_TYPES = "jmx.remote.rmi.server.credential.types"; /** * Name of the attribute that specifies an * {@link ObjectInputFilter} pattern string to filter classes acceptable * for {@link RMIServer#newClient(java.lang.Object) RMIServer.newClient()} * remote method call. *
* The filter pattern must be in same format as used in * {@link java.io.ObjectInputFilter.Config#createFilter} *
* This list of classes allowed by filter 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 then any class is deemed acceptable. * @see ObjectInputFilter */ public static final String CREDENTIALS_FILTER_PATTERN = "jmx.remote.rmi.server.credentials.filter.pattern"; /** * This attribute defines a pattern from which to create a * {@link java.io.ObjectInputFilter} that will be used when deserializing * objects sent to the {@code JMXConnectorServer} by any client. *
* The filter will be called for any class found in the serialized * stream sent to server by client, including all JMX defined classes * (such as {@link javax.management.ObjectName}), all method parameters, * and, if present in the stream, all classes transitively referred by * the serial form of any deserialized object. * The pattern must be in same format as used in * {@link java.io.ObjectInputFilter.Config#createFilter}. * It may define a white list of permitted classes, a black list of * rejected classes, a maximum depth for the deserialized objects, * etc. *
* To be functional, the filter should allow at least all the * concrete types in the transitive closure of all objects that * might get serialized when serializing all JMX classes referred * as parameters in the {@link * javax.management.remote.rmi.RMIConnection} interface, * plus all classes that a {@link javax.management.remote.rmi.RMIConnector client} * might need to transmit wrapped in {@linkplain java.rmi.MarshalledObject * marshalled objects} in order to interoperate with the MBeans registered * in the {@code MBeanServer}. That would potentially include all the * concrete {@linkplain javax.management.openmbean JMX OpenTypes} and the * classes they use in their serial form. *
* Care must be taken when defining such a filter, as defining * a white list too restrictive or a too wide a black list may * prevent legitimate clients from interoperating with the * {@code JMXConnectorServer}. */ public static final String SERIAL_FILTER_PATTERN = "jmx.remote.rmi.server.serial.filter.pattern"; /** *
Makes an RMIConnectorServer
.
* This is equivalent to calling {@link #RMIConnectorServer(
* JMXServiceURL,Map,RMIServerImpl,MBeanServer)
* RMIConnectorServer(directoryURL,environment,null,null)}
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, MapMakes an RMIConnectorServer
for the given MBean
* server.
* This is equivalent to calling {@link #RMIConnectorServer(
* JMXServiceURL,Map,RMIServerImpl,MBeanServer)
* RMIConnectorServer(directoryURL,environment,null,mbeanServer)}
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, MapMakes an RMIConnectorServer
for the given MBean
* server.
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, MapReturns 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(MapActivates 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:
* *RMIServerImpl
was supplied to the
* constructor, it is used.
*
* JMXServiceURL
* was null, or its protocol part was rmi
, an object
* of type {@link RMIJRMPServerImpl} is created.
*
* 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.
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.
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 MapJMXServiceURL
.
* @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