FileDocCategorySizeDatePackage
RMIConnectorServer.javaAPI DocJava SE 6 API30396Tue Jun 10 00:26:18 BST 2008javax.management.remote.rmi

RMIConnectorServer

public class RMIConnectorServer extends JMXConnectorServer

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
since.unbundled
1.0

Fields Summary
public static final String
JNDI_REBIND_ATTRIBUTE

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
RMI_CLIENT_SOCKET_FACTORY_ATTRIBUTE

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_SERVER_SOCKET_FACTORY_ATTRIBUTE

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.

private static final char[]
intToAlpha
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 ClassLogger
logger
private JMXServiceURL
address
private RMIServerImpl
rmiServerImpl
private final Map
attributes
private ClassLoader
defaultClassLoader
private String
boundJndiUrl
private static final int
CREATED
private static final int
STARTED
private static final int
STOPPED
private int
state
private static final Set
openedServers
Constructors Summary
public RMIConnectorServer(JMXServiceURL url, Map environment)

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" 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.


                                                                                                                             
        
              
        this(url, environment, (MBeanServer) null);
    
public RMIConnectorServer(JMXServiceURL url, Map environment, MBeanServer mbeanServer)

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" 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.

        this(url, environment, (RMIServerImpl) null, mbeanServer);
    
public RMIConnectorServer(JMXServiceURL url, Map environment, RMIServerImpl rmiServerImpl, MBeanServer 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" 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 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" and "iiop" are 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

	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.EMPTY_MAP;
        else {
	    EnvHelp.checkAttributes(environment);
            this.attributes = Collections.unmodifiableMap(environment);
	}

        this.address = url;
        this.rmiServerImpl = rmiServerImpl;
    
Methods Summary
voidbind(java.lang.String jndiUrl, java.util.Hashtable attributes, javax.management.remote.rmi.RMIServer rmiServer, boolean rebind)
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.

        // 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();
    
private static java.lang.StringbyteArrayToBase64(byte[] a)

        int aLen = a.length;
        int numFullGroups = aLen/3;
        int numBytesInPartialGroup = aLen - 3*numFullGroups;
        int resultLen = 4*((aLen + 2)/3);
        StringBuffer result = new StringBuffer(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();
    
protected voidconnectionClosed(java.lang.String connectionId, java.lang.String message, java.lang.Object userData)

	super.connectionClosed(connectionId, message, userData);
    
protected voidconnectionFailed(java.lang.String connectionId, java.lang.String message, java.lang.Object userData)

	super.connectionFailed(connectionId, message, userData);
    
protected voidconnectionOpened(java.lang.String connectionId, java.lang.String message, java.lang.Object userData)

	super.connectionOpened(connectionId, message, userData);
    
static java.lang.StringencodeIIOPStub(javax.management.remote.rmi.RMIServer rmiServer, java.util.Map env)

	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);
	}
    
static java.lang.StringencodeJRMPStub(javax.management.remote.rmi.RMIServer rmiServer, java.util.Map env)

	ByteArrayOutputStream bout = new ByteArrayOutputStream();
	ObjectOutputStream oout = new ObjectOutputStream(bout);
	oout.writeObject(rmiServer);
	oout.close();
	byte[] bytes = bout.toByteArray();
	return byteArrayToBase64(bytes);
    
static java.lang.StringencodeStub(javax.management.remote.rmi.RMIServer rmiServer, java.util.Map env)
Returns the IOR of the given rmiServer.

        if (rmiServer instanceof javax.rmi.CORBA.Stub)
	    return "/ior/" + encodeIIOPStub(rmiServer, env);
	else
	    return "/stub/" + encodeJRMPStub(rmiServer, env);
    
private voidencodeStubInAddress(javax.management.remote.rmi.RMIServer rmiServer, java.util.Map attributes)
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.


	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);
    
public javax.management.remote.JMXServiceURLgetAddress()

        if (!isActive())
	    return null;
        return address;
    
public java.util.MapgetAttributes()

	Map map = EnvHelp.filterAttributes(attributes);
        return Collections.unmodifiableMap(map);
    
public synchronized booleanisActive()

	return (state == STARTED);
    
static booleanisIiopURL(javax.management.remote.JMXServiceURL directoryURL, boolean strict)

        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;
    
private static javax.management.remote.rmi.RMIServerImplnewIIOPServer(java.util.Map env)

        return new RMIIIOPServerImpl(env);
    
private static java.io.IOExceptionnewIOException(java.lang.String message, java.lang.Throwable cause)
Construct a new IOException with a nested exception. The nested exception is set only if JDK >= 1.4


                             
         
                                                
        final IOException x = new IOException(message);
        return EnvHelp.initCause(x,cause);
    
private static javax.management.remote.rmi.RMIServerImplnewJRMPServer(java.util.Map env, int port)

        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);
    
javax.management.remote.rmi.RMIServerImplnewServer()
Creates a new RMIServerImpl.

        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);
    
private static javax.management.remote.rmi.RMIServerobjectToBind(javax.management.remote.rmi.RMIServerImpl rmiServer, java.util.Map env)
Object that we will bind to the registry. This object is a stub connected to our RMIServerImpl.

        return RMIConnector.
            connectStub((RMIServer)rmiServer.toStub(),env);
    
public synchronized voidsetMBeanServerForwarder(javax.management.remote.MBeanServerForwarder mbsf)

        super.setMBeanServerForwarder(mbsf);
        if (rmiServerImpl != null)
            rmiServerImpl.setMBeanServer(getMBeanServer());
    
public synchronized voidstart()

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 protocol part of the JMXServiceURL supplied to the constructor was iiop, an object of type {@link RMIIIOPServerImpl} is created.
  • 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 and iiop 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 neither rmi nor iiop, 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.

	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 = null;
		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.");
        }
    
public voidstop()

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.

	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 javax.management.remote.JMXConnectortoJMXConnector(java.util.Map env)

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.

        // 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.EMPTY_MAP:
		    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);