FileDocCategorySizeDatePackage
JMXConnectorServerFactory.javaAPI DocJava SE 5 API11898Fri Aug 26 14:57:38 BST 2005javax.management.remote

JMXConnectorServerFactory.java

/*
 * @(#)JMXConnectorServerFactory.java	1.17 04/05/05
 * 
 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
 * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */

package javax.management.remote;

import java.io.IOException;
import java.net.MalformedURLException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import javax.management.MBeanServer;
import javax.management.ObjectName;

import com.sun.jmx.remote.util.ClassLogger;
import com.sun.jmx.remote.util.EnvHelp;

/**
 * <p>Factory to create JMX API connector servers.  There
 * are no instances of this class.</p>
 *
 * <p>Each connector server is created by an instance of {@link
 * JMXConnectorServerProvider}.  This instance is found as follows.  Suppose
 * the given {@link JMXServiceURL} looks like
 * <code>"service:jmx:<em>protocol</em>:<em>remainder</em>"</code>.
 * Then the factory will attempt to find the appropriate {@link
 * JMXConnectorServerProvider} for <code><em>protocol</em></code>.  Each
 * occurrence of the character <code>+</code> or <code>-</code> in
 * <code><em>protocol</em></code> is replaced by <code>.</code> or
 * <code>_</code>, respectively.</p>
 *
 * <p>A <em>provider package list</em> is searched for as follows:</p>
 *
 * <ol>
 *
 * <li>If the <code>environment</code> parameter to {@link
 * #newJMXConnectorServer(JMXServiceURL,Map,MBeanServer)
 * newJMXConnectorServer} contains the key
 * <code>jmx.remote.protocol.provider.pkgs</code> then the associated
 * value is the provider package list.
 *
 * <li>Otherwise, if the system property
 * <code>jmx.remote.protocol.provider.pkgs</code> exists, then its value
 * is the provider package list.
 *
 * <li>Otherwise, there is no provider package list.
 *
 * </ol>
 *
 * <p>The provider package list is a string that is interpreted as a
 * list of non-empty Java package names separated by vertical bars
 * (<code>|</code>).  If the string is empty, then so is the provider
 * package list.  If the provider package list is not a String, or if
 * it contains an element that is an empty string, a {@link
 * JMXProviderException} is thrown.</p>
 *
 * <p>If the provider package list exists and is not empty, then for
 * each element <code><em>pkg</em></code> of the list, the factory
 * will attempt to load the class
 *
 * <blockquote>
 * <code><em>pkg</em>.<em>protocol</em>.ServerProvider</code>
 * </blockquote>

 * <p>If the <code>environment</code> parameter to {@link
 * #newJMXConnectorServer(JMXServiceURL, Map, MBeanServer)
 * newJMXConnectorServer} contains the key
 * <code>jmx.remote.protocol.provider.class.loader</code> then the
 * associated value is the class loader to use to load the provider.
 * If the associated value is not an instance of {@link
 * java.lang.ClassLoader}, an {@link
 * java.lang.IllegalArgumentException} is thrown.</p>
 *
 * <p>If the <code>jmx.remote.protocol.provider.class.loader</code>
 * key is not present in the <code>environment</code> parameter, the
 * calling thread's context class loader is used.</p>
 *
 * <p>If the attempt to load this class produces a {@link
 * ClassNotFoundException}, the search for a handler continues with
 * the next element of the list.</p>
 *
 * <p>Otherwise, a problem with the provider found is signalled by a
 * {@link JMXProviderException} whose {@link
 * JMXProviderException#getCause() <em>cause</em>} indicates the
 * underlying exception, as follows:</p>
 *
 * <ul>
 *
 * <li>if the attempt to load the class produces an exception other
 * than <code>ClassNotFoundException</code>, that is the
 * <em>cause</em>;
 *
 * <li>if {@link Class#newInstance()} for the class produces an
 * exception, that is the <em>cause</em>.
 *
 * </ul>
 *
 * <p>If no provider is found by the above steps, including the
 * default case where there is no provider package list, then the
 * implementation will use its own provider for
 * <code><em>protocol</em></code>, or it will throw a
 * <code>MalformedURLException</code> if there is none.  An
 * implementation may choose to find providers by other means.  For
 * example, it may support the <a
 * href="http://java.sun.com/j2se/1.5.0/docs/guide/jar/jar.html#Service Provider">
 * JAR conventions for service providers</a>, where the service
 * interface is <code>JMXConnectorServerProvider</code>.</p>
 *
 * <p>Every implementation must support the RMI connector protocols,
 * specified with the string <code>rmi</code> or
 * <code>iiop</code>.</p>
 *
 * <p>Once a provider is found, the result of the
 * <code>newJMXConnectorServer</code> method is the result of calling
 * {@link
 * JMXConnectorServerProvider#newJMXConnectorServer(JMXServiceURL,
 * Map, MBeanServer) newJMXConnectorServer} on the provider.</p>
 *
 * <p>The <code>Map</code> parameter passed to the
 * <code>JMXConnectorServerProvider</code> is a new read-only
 * <code>Map</code> that contains all the entries that were in the
 * <code>environment</code> parameter to {@link
 * #newJMXConnectorServer(JMXServiceURL,Map,MBeanServer)
 * JMXConnectorServerFactory.newJMXConnectorServer}, if there was one.
 * Additionally, if the
 * <code>jmx.remote.protocol.provider.class.loader</code> key is not
 * present in the <code>environment</code> parameter, it is added to
 * the new read-only <code>Map</code>. The associated value is the
 * calling thread's context class loader.</p>
 *
 * @since 1.5
 * @since.unbundled 1.0
 */
public class JMXConnectorServerFactory {

    /**
     * <p>Name of the attribute that specifies the default class
     * loader.  This class loader is used to deserialize objects in
     * requests received from the client, possibly after consulting an
     * MBean-specific class loader.  The value associated with this
     * attribute is an instance of {@link ClassLoader}.</p>
     */
    public static final String DEFAULT_CLASS_LOADER =
	JMXConnectorFactory.DEFAULT_CLASS_LOADER;

    /**
     * <p>Name of the attribute that specifies the default class
     * loader MBean name.  This class loader is used to deserialize objects in
     * requests received from the client, possibly after consulting an
     * MBean-specific class loader.  The value associated with this
     * attribute is an instance of {@link ObjectName}.</p>
     */
    public static final String DEFAULT_CLASS_LOADER_NAME =
	"jmx.remote.default.class.loader.name";

    /**
     * <p>Name of the attribute that specifies the provider packages
     * that are consulted when looking for the handler for a protocol.
     * The value associated with this attribute is a string with
     * package names separated by vertical bars (<code>|</code>).</p>
     */
    public static final String PROTOCOL_PROVIDER_PACKAGES =
        "jmx.remote.protocol.provider.pkgs";

    /**
     * <p>Name of the attribute that specifies the class
     * loader for loading protocol providers.
     * The value associated with this attribute is an instance
     * of {@link ClassLoader}.</p>
     */
    public static final String PROTOCOL_PROVIDER_CLASS_LOADER =
        "jmx.remote.protocol.provider.class.loader";

    private static final String PROTOCOL_PROVIDER_DEFAULT_PACKAGE =
	"com.sun.jmx.remote.protocol";
    
    private static final ClassLogger logger =
	new ClassLogger("javax.management.remote.misc","JMXConnectorServerFactory");
    
    /** There are no instances of this class.  */
    private JMXConnectorServerFactory() {
    }

    private static JMXConnectorServer 
	getConnectorServerAsService(ClassLoader loader,
				    JMXServiceURL url,
				    Map map,
				    MBeanServer mbs)
	throws IllegalArgumentException, JMXProviderException {
	Iterator providers = 
	    JMXConnectorFactory.getProviderIterator(JMXConnectorServerProvider.class,
						    loader);
	JMXConnectorServerProvider provider = null;
	JMXConnectorServer connection = null;
	while(providers.hasNext()) {
	    provider = 
		(JMXConnectorServerProvider) providers.next();
	    try {
		connection = provider.newJMXConnectorServer(url, map, mbs);
		return connection;
	    } catch (JMXProviderException e) {
		throw e;
	    }
	    catch (Exception e) {
		if (logger.traceOn())
		    logger.trace("getConnectorAsService",
				 "URL[" + url +
				 "] Service provider exception " + e);
		continue;
	    }
	}
	return null;
    }
    
    /**
     * <p>Creates a connector server at the given address.  The
     * resultant server is not started until its {@link
     * JMXConnectorServer#start() start} method is called.</p>
     *
     * @param serviceURL the address of the new connector server.  The
     * actual address of the new connector server, as returned by its
     * {@link JMXConnectorServer#getAddress() getAddress} method, will
     * not necessarily be exactly the same.  For example, it might
     * include a port number if the original address did not.
     *
     * @param environment a set of attributes to control the new
     * connector server's behaviour.  This parameter can be null.
     * Keys in this map must be Strings.  The appropriate type of each
     * associated value depends on the attribute.  The contents of
     * <code>environment</code> are not changed by this call.
     *
     * @param mbeanServer the MBean server that this connector server
     * is attached to.  Null if this connector server will be attached
     * to an MBean server by being registered in it.
     *
     * @return a <code>JMXConnectorServer</code> representing the new
     * connector server.  Each successful call to this method produces
     * a different object.
     *
     * @exception NullPointerException if <code>serviceURL</code> is null.
     *
     * @exception IOException if the connector server cannot be made
     * because of a communication problem.
     *
     * @exception MalformedURLException if there is no provider for the
     * protocol in <code>serviceURL</code>.
     *
     * @exception JMXProviderException if there is a provider for the
     * protocol in <code>serviceURL</code> but it cannot be used for
     * some reason.
     */
    public static JMXConnectorServer
	newJMXConnectorServer(JMXServiceURL serviceURL,
			      Map<String,?> environment,
			      MBeanServer mbeanServer)
            throws IOException {
	if (environment == null)
	    environment = new HashMap();
	else {
	    EnvHelp.checkAttributes(environment);
	    environment = new HashMap(environment);
	}

	final Class targetInterface = JMXConnectorServerProvider.class;
	final ClassLoader loader = 
	    JMXConnectorFactory.resolveClassLoader(environment);
	final String protocol = serviceURL.getProtocol();
	final String providerClassName = "ServerProvider";
	
	JMXConnectorServerProvider provider =
	    (JMXConnectorServerProvider)
	    JMXConnectorFactory.getProvider(serviceURL, environment,
					    providerClassName,
					    targetInterface, loader);	
	if(provider == null) {
	    // Loader is null when context class loader is set to null
	    // and no loader has been provided in map.
	    // com.sun.jmx.remote.util.Service class extracted from j2se
	    // provider search algorithm doesn't handle well null classloader.
	    if(loader != null) {
		JMXConnectorServer connection = 
		    getConnectorServerAsService(loader,
						serviceURL,
						environment,
						mbeanServer);
		if(connection != null)
		    return connection;
	    }
	
	    provider = (JMXConnectorServerProvider)
		JMXConnectorFactory.getProvider(protocol, 
						PROTOCOL_PROVIDER_DEFAULT_PACKAGE,
						JMXConnectorFactory.class.getClassLoader(),
						providerClassName,
						targetInterface);
	}
	
	if(provider == null)
	    throw new MalformedURLException("Unsupported protocol: " + 
					    protocol);
	
	environment = Collections.unmodifiableMap(environment);
        
        return provider.newJMXConnectorServer(serviceURL, environment,
					      mbeanServer);
    }
}