- /*
- * @(#)RMIServerImpl.java 1.54 04/06/03
- *
- * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
- * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
- */
-
- package javax.management.remote.rmi;
-
- import java.io.IOException;
- import java.lang.ref.WeakReference;
- import java.rmi.Remote;
- import java.rmi.server.RemoteObject;
- import java.rmi.server.RemoteServer;
- import java.rmi.server.ServerNotActiveException;
- import java.security.Principal;
- import java.security.AccessController;
- import java.security.PrivilegedAction;
- import java.util.ArrayList;
- import java.util.Collections;
- import java.util.Iterator;
- import java.util.List;
- import java.util.Map;
- import java.util.Set;
-
- import javax.management.MBeanServer;
- import javax.management.remote.JMXAuthenticator;
- import javax.management.remote.JMXConnectorFactory;
- import javax.management.remote.JMXConnectorServer;
-
- import javax.security.auth.Subject;
-
- import com.sun.jmx.remote.security.JMXPluggableAuthenticator;
- import com.sun.jmx.remote.util.ClassLogger;
- import com.sun.jmx.remote.internal.ArrayNotificationBuffer;
- import com.sun.jmx.remote.internal.NotificationBuffer;
-
- /**
- * <p>An RMI object representing a connector server. Remote clients
- * can make connections using the {@link #newClient(Object)} method. This
- * method returns an RMI object representing the connection.</p>
- *
- * <p>User code does not usually reference this class directly. RMI
- * connection servers are usually created with the class {@link
- * RMIConnectorServer}. Remote clients usually create connections
- * either with {@link JMXConnectorFactory} or by instantiating {@link
- * RMIConnector}.</p>
- *
- * <p>This is an abstract class. Concrete subclasses define the
- * details of the client connection objects, such as whether they use
- * JRMP or IIOP.</p>
- *
- * @since 1.5
- * @since.unbundled 1.0
- */
- public abstract class RMIServerImpl implements RMIServer {
- /**
- * <p>Constructs a new <code>RMIServerImpl</code>.</p>
- *
- * @param env the environment containing attributes for the new
- * <code>RMIServerImpl</code>. Can be null, which is equivalent
- * to an empty Map.
- */
- public RMIServerImpl(Map<String,?> env) {
- this.env = (env == null) ? Collections.EMPTY_MAP : env;
- if (logger.debugOn())
- logger.trace("RMIServerImpl","class="+this.getClass().getName());
- }
-
- void setRMIConnectorServer(RMIConnectorServer connServer)
- throws IOException {
- this.connServer = connServer;
- }
-
- /**
- * <p>Exports this RMI object.</p>
- *
- * @exception IOException if this RMI object cannot be exported.
- */
- protected abstract void export() throws IOException;
-
- /**
- * Returns a remotable stub for this server object.
- * @return a remotable stub.
- * @exception IOException if the stub cannot be obtained - e.g the
- * RMIServerImpl has not been exported yet.
- **/
- public abstract Remote toStub() throws IOException;
-
- /**
- * <p>Sets the default <code>ClassLoader</code> for this connector
- * server. New client connections will use this classloader.
- * Existing client connections are unaffected.</p>
- *
- * @param cl the new <code>ClassLoader</code> to be used by this
- * connector server.
- *
- * @see #getDefaultClassLoader
- */
- public synchronized void setDefaultClassLoader(ClassLoader cl) {
- this.cl = cl;
- }
-
- /**
- * <p>Gets the default <code>ClassLoader</code> used by this connector
- * server.</p>
- *
- * @return the default <code>ClassLoader</code> used by this
- * connector server.</p>
- *
- * @see #setDefaultClassLoader
- */
- public synchronized ClassLoader getDefaultClassLoader() {
- return cl;
- }
-
- /**
- * <p>Sets the <code>MBeanServer</code> to which this connector
- * server is attached. New client connections will interact
- * with this <code>MBeanServer</code>. Existing client connections are
- * unaffected.</p>
- *
- * @param mbs the new <code>MBeanServer</code>. Can be null, but
- * new client connections will be refused as long as it is.
- *
- * @see #getMBeanServer
- */
- public synchronized void setMBeanServer(MBeanServer mbs) {
- this.mbeanServer = mbs;
- }
-
- /**
- * <p>The <code>MBeanServer</code> to which this connector server
- * is attached. This is the last value passed to {@link
- * #setMBeanServer} on this object, or null if that method has
- * never been called.</p>
- *
- * @return the <code>MBeanServer</code> to which this connector
- * is attached.
- *
- * @see #setMBeanServer
- */
- public synchronized MBeanServer getMBeanServer() {
- return mbeanServer;
- }
-
- public String getVersion() {
- // Expected format is: "protocol-version implementation-name"
- return "1.0 java_runtime_" + (String)
- AccessController.doPrivileged(new PrivilegedAction() {
- public Object run() {
- return System.getProperty("java.runtime.version");
- }
- });
- }
-
- /**
- * <p>Creates a new client connection. This method calls {@link
- * #makeClient makeClient} and adds the returned client connection
- * object to an internal list. When this
- * <code>RMIServerImpl</code> is shut down via its {@link
- * #close()} method, the {@link RMIConnection#close() close()}
- * method of each object remaining in the list is called.</p>
- *
- * <p>The fact that a client connection object is in this internal
- * list does not prevent it from being garbage collected.</p>
- *
- * @param credentials this object specifies the user-defined
- * credentials to be passed in to the server in order to
- * authenticate the caller before creating the
- * <code>RMIConnection</code>. Can be null.
- *
- * @return the newly-created <code>RMIConnection</code>. This is
- * usually the object created by <code>makeClient</code>, though
- * an implementation may choose to wrap that object in another
- * object implementing <code>RMIConnection</code>.
- *
- * @exception IOException if the new client object cannot be
- * created or exported.
- *
- * @exception SecurityException if the given credentials do not allow
- * the server to authenticate the user successfully.
- *
- * @exception IllegalStateException if {@link #getMBeanServer()}
- * is null.
- */
- public RMIConnection newClient(Object credentials) throws IOException {
- final boolean tracing = logger.traceOn();
-
- if (tracing) logger.trace("newClient","making new client");
-
- if (getMBeanServer() == null)
- throw new IllegalStateException("Not attached to an MBean server");
-
- Subject subject = null;
- JMXAuthenticator authenticator =
- (JMXAuthenticator) env.get(JMXConnectorServer.AUTHENTICATOR);
- if (authenticator == null) {
- /*
- * Create the JAAS-based authenticator only if authentication
- * has been enabled
- */
- if (env.get("jmx.remote.x.password.file") != null ||
- env.get("jmx.remote.x.login.config") != null) {
- authenticator = new JMXPluggableAuthenticator(env);
- }
- }
- if (authenticator != null) {
- if (tracing) logger.trace("newClient","got authenticator: " +
- authenticator.getClass().getName());
- try {
- subject = authenticator.authenticate(credentials);
- } catch (SecurityException e) {
- logger.trace("newClient", "Authentication failed: " + e);
- throw e;
- }
- }
-
- if (tracing) {
- if (subject != null)
- logger.trace("newClient","subject is not null");
- else logger.trace("newClient","no subject");
- }
-
- final String connectionId = makeConnectionId(getProtocol(), subject);
-
- if (tracing)
- logger.trace("newClient","making new connection: " + connectionId);
-
- RMIConnection client = makeClient(connectionId, subject);
-
- connServer.connectionOpened(connectionId, "Connection opened", null);
-
- dropDeadReferences();
- WeakReference wr = new WeakReference(client);
- synchronized (clientList) {
- clientList.add(wr);
- }
-
- if (tracing)
- logger.trace("newClient","new connection done: " + connectionId );
-
- return client;
- }
-
- /**
- * <p>Creates a new client connection. This method is called by
- * the public method {@link #newClient(Object)}.</p>
- *
- * @param connectionId the ID of the new connection. Every
- * connection opened by this connector server will have a
- * different ID. The behavior is unspecified if this parameter is
- * null.
- *
- * @param subject the authenticated subject. Can be null.
- *
- * @return the newly-created <code>RMIConnection</code>.
- *
- * @exception IOException if the new client object cannot be
- * created or exported.
- */
- protected abstract RMIConnection makeClient(String connectionId,
- Subject subject)
- throws IOException;
-
- /**
- * <p>Closes a client connection made by {@link #makeClient makeClient}.
- *
- * @param client a connection previously returned by
- * <code>makeClient</code> on which the <code>closeClient</code>
- * method has not previously been called. The behavior is
- * unspecified if these conditions are violated, including the
- * case where <code>client</code> is null.
- *
- * @exception IOException if the client connection cannot be
- * closed.
- */
- protected abstract void closeClient(RMIConnection client)
- throws IOException;
-
- /**
- * <p>Returns the protocol string for this object. The string is
- * <code>rmi</code> for RMI/JRMP and <code>iiop</code> for RMI/IIOP.
- *
- * @return the protocol string for this object.
- */
- protected abstract String getProtocol();
-
- /**
- * <p>Method called when a client connection created by {@link
- * #makeClient makeClient} is closed. A subclass that defines
- * <code>makeClient</code> must arrange for this method to be
- * called when the resultant object's {@link RMIConnection#close()
- * close} method is called. This enables it to be removed from
- * the <code>RMIServerImpl</code>'s list of connections. It is
- * not an error for <code>client</code> not to be in that
- * list.</p>
- *
- * <p>After removing <code>client</code> from the list of
- * connections, this method calls {@link #closeClient
- * closeClient(client)}.</p>
- *
- * @param client the client connection that has been closed.
- *
- * @exception IOException if {@link #closeClient} throws this
- * exception.
- *
- * @exception NullPointerException if <code>client</code> is null.
- */
- protected void clientClosed(RMIConnection client) throws IOException {
- final boolean debug = logger.debugOn();
-
- if (debug) logger.trace("clientClosed","client="+client);
-
- if (client == null)
- throw new NullPointerException("Null client");
-
- synchronized (clientList) {
- dropDeadReferences();
- for (Iterator it = clientList.iterator(); it.hasNext(); ) {
- WeakReference wr = (WeakReference) it.next();
- if (wr.get() == client) {
- it.remove();
- break;
- }
- }
- /* It is not a bug for this loop not to find the client. In
- our close() method, we remove a client from the list before
- calling its close() method. */
- }
-
- if (debug) logger.trace("clientClosed", "closing client.");
- closeClient(client);
-
- if (debug) logger.trace("clientClosed", "sending notif");
- connServer.connectionClosed(client.getConnectionId(),
- "Client connection closed", null);
-
- if (debug) logger.trace("clientClosed","done");
- }
-
- /**
- * <p>Closes this connection server. This method first calls the
- * {@link #closeServer()} method so that no new client connections
- * will be accepted. Then, for each remaining {@link
- * RMIConnection} object returned by {@link #makeClient
- * makeClient}, its {@link RMIConnection#close() close} method is
- * called.</p>
- *
- * <p>The behaviour when this method is called more than once is
- * unspecified.</p>
- *
- * <p>If {@link #closeServer()} throws an
- * <code>IOException</code>, the individual connections are
- * nevertheless closed, and then the <code>IOException</code> is
- * thrown from this method.</p>
- *
- * <p>If {@link #closeServer()} returns normally but one or more
- * of the individual connections throws an
- * <code>IOException</code>, then, after closing all the
- * connections, one of those <code>IOException</code>s is thrown
- * from this method. If more than one connection throws an
- * <code>IOException</code>, it is unspecified which one is thrown
- * from this method.</p>
- *
- * @exception IOException if {@link #closeServer()} or one of the
- * {@link RMIConnection#close()} calls threw
- * <code>IOException</code>.
- */
- public synchronized void close() throws IOException {
- final boolean tracing = logger.traceOn();
- final boolean debug = logger.debugOn();
-
- if (tracing) logger.trace("close","closing");
-
- IOException ioException = null;
- try {
- if (debug) logger.debug("close","closing Server");
- closeServer();
- } catch (IOException e) {
- if (tracing) logger.trace("close","Failed to close server: " + e);
- if (debug) logger.debug("close",e);
- ioException = e;
- }
-
- if (debug) logger.debug("close","closing Clients");
- // Loop to close all clients
- while (true) {
- synchronized (clientList) {
- if (debug) logger.debug("close","droping dead references");
- dropDeadReferences();
-
- if (debug) logger.debug("close","client count: "+clientList.size());
- if (clientList.size() == 0)
- break;
- /* Loop until we find a non-null client. Because we called
- dropDeadReferences(), this will usually be the first
- element of the list, but a garbage collection could have
- happened in between. */
- for (Iterator it = clientList.iterator(); it.hasNext(); ) {
- WeakReference wr = (WeakReference) it.next();
- RMIConnection client = (RMIConnection) wr.get();
- it.remove();
- if (client != null) {
- try {
- client.close();
- } catch (IOException e) {
- if (tracing)
- logger.trace("close","Failed to close client: " + e);
- if (debug) logger.debug("close",e);
- if (ioException == null)
- ioException = e;
- }
- break;
- }
- }
- }
- }
-
- if(notifBuffer != null)
- notifBuffer.dispose();
-
- if (ioException != null) {
- if (tracing) logger.trace("close","close failed.");
- throw ioException;
- }
-
- if (tracing) logger.trace("close","closed.");
- }
-
- /**
- * <p>Called by {@link #close()} to close the connector server.
- * After returning from this method, the connector server must
- * not accept any new connections.</p>
- *
- * @exception IOException if the attempt to close the connector
- * server failed.
- */
- protected abstract void closeServer() throws IOException;
-
- private static synchronized String makeConnectionId(String protocol,
- Subject subject) {
- connectionIdNumber++;
-
- String clientHost = "";
- try {
- clientHost = RemoteServer.getClientHost();
- } catch (ServerNotActiveException e) {
- logger.trace("makeConnectionId", "getClientHost", e);
- }
-
- StringBuffer buf = new StringBuffer();
- buf.append(protocol).append(":");
- if (clientHost.length() > 0)
- buf.append("//").append(clientHost);
- buf.append(" ");
- if (subject != null) {
- Set principals = subject.getPrincipals();
- String sep = "";
- for (Iterator it = principals.iterator(); it.hasNext(); ) {
- Principal p = (Principal) it.next();
- String name = p.getName().replace(' ', '_').replace(';', ':');
- buf.append(sep).append(name);
- sep = ";";
- }
- }
- buf.append(" ").append(connectionIdNumber);
- if (logger.traceOn())
- logger.trace("newConnectionId","connectionId="+buf);
- return buf.toString();
- }
-
- private void dropDeadReferences() {
- synchronized (clientList) {
- for (Iterator it = clientList.iterator(); it.hasNext(); ) {
- WeakReference wr = (WeakReference) it.next();
- if (wr.get() == null)
- it.remove();
- }
- }
- }
-
- synchronized NotificationBuffer getNotifBuffer() {
- //Notification buffer is lazily created when the first client connects
- if(notifBuffer == null)
- notifBuffer =
- ArrayNotificationBuffer.getNotificationBuffer(mbeanServer,
- env);
- return notifBuffer;
- }
-
- private static final ClassLogger logger =
- new ClassLogger("javax.management.remote.rmi", "RMIServerImpl");
-
- /** List of WeakReference values. Each one references an
- RMIConnection created by this object, or null if the
- RMIConnection has been garbage-collected. */
- private final List clientList = new ArrayList();
-
- private ClassLoader cl;
-
- private MBeanServer mbeanServer;
-
- private final Map env;
-
- private RMIConnectorServer connServer;
-
- private static int connectionIdNumber;
-
- private NotificationBuffer notifBuffer;
- }