559 lines · 21.1 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; import com.sun.jmx.mbeanserver.Util;import java.io.IOException;import java.net.MalformedURLException;import java.util.Collections;import java.util.HashMap;import java.util.Map;import java.util.Iterator;import java.util.ServiceLoader;import java.util.StringTokenizer;import java.security.AccessController;import java.security.PrivilegedAction; import com.sun.jmx.remote.util.ClassLogger;import com.sun.jmx.remote.util.EnvHelp;  /** * <p>Factory to create JMX API connector clients.  There * are no instances of this class.</p> * * <p>Connections are usually made using the {@link * #connect(JMXServiceURL) connect} method of this class.  More * advanced applications can separate the creation of the connector * client, using {@link #newJMXConnector(JMXServiceURL, Map) * newJMXConnector} and the establishment of the connection itself, using * {@link JMXConnector#connect(Map)}.</p> * * <p>Each client is created by an instance of {@link * JMXConnectorProvider}.  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 * JMXConnectorProvider} 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 * #newJMXConnector(JMXServiceURL, Map) newJMXConnector} 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>.ClientProvider</code> * </blockquote>  * <p>If the <code>environment</code> parameter to {@link * #newJMXConnector(JMXServiceURL, Map) newJMXConnector} 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="{@docRoot}/../technotes/guides/jar/jar.html#Service Provider"> * JAR conventions for service providers</a>, where the service * interface is <code>JMXConnectorProvider</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>newJMXConnector</code> method is the result of calling {@link * JMXConnectorProvider#newJMXConnector(JMXServiceURL,Map) newJMXConnector} * on the provider.</p> * * <p>The <code>Map</code> parameter passed to the * <code>JMXConnectorProvider</code> is a new read-only * <code>Map</code> that contains all the entries that were in the * <code>environment</code> parameter to {@link * #newJMXConnector(JMXServiceURL,Map) * JMXConnectorFactory.newJMXConnector}, 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 */public class JMXConnectorFactory {     /**     * <p>Name of the attribute that specifies the default class     * loader. This class loader is used to deserialize return values and     * exceptions from remote <code>MBeanServerConnection</code>     * calls.  The value associated with this attribute is an instance     * of {@link ClassLoader}.</p>     */    public static final String DEFAULT_CLASS_LOADER =        "jmx.remote.default.class.loader";     /**     * <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", "JMXConnectorFactory");     /** There are no instances of this class.  */    private JMXConnectorFactory() {    }     /**     * <p>Creates a connection to the connector server at the given     * address.</p>     *     * <p>This method is equivalent to {@link     * #connect(JMXServiceURL,Map) connect(serviceURL, null)}.</p>     *     * @param serviceURL the address of the connector server to     * connect to.     *     * @return a <code>JMXConnector</code> whose {@link     * JMXConnector#connect connect} method has been called.     *     * @exception NullPointerException if <code>serviceURL</code> is null.     *     * @exception IOException if the connector client or the     * connection cannot be made because of a communication problem.     *     * @exception SecurityException if the connection cannot be made     * for security reasons.     */    public static JMXConnector connect(JMXServiceURL serviceURL)            throws IOException {        return connect(serviceURL, null);    }     /**     * <p>Creates a connection to the connector server at the given     * address.</p>     *     * <p>This method is equivalent to:</p>     *     * <pre>     * JMXConnector conn = JMXConnectorFactory.newJMXConnector(serviceURL,     *                                                         environment);     * conn.connect(environment);     * </pre>     *     * @param serviceURL the address of the connector server to connect to.     *     * @param environment a set of attributes to determine how the     * connection is made.  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.     *     * @return a <code>JMXConnector</code> representing the newly-made     * connection.  Each successful call to this method produces a     * different object.     *     * @exception NullPointerException if <code>serviceURL</code> is null.     *     * @exception IOException if the connector client or the     * connection cannot be made because of a communication problem.     *     * @exception SecurityException if the connection cannot be made     * for security reasons.     */    public static JMXConnector connect(JMXServiceURL serviceURL,                                       Map<String,?> environment)            throws IOException {        if (serviceURL == null)            throw new NullPointerException("Null JMXServiceURL");        JMXConnector conn = newJMXConnector(serviceURL, environment);        conn.connect(environment);        return conn;    }     private static <K,V> Map<K,V> newHashMap() {        return new HashMap<K,V>();    }     private static <K> Map<K,Object> newHashMap(Map<K,?> map) {        return new HashMap<K,Object>(map);    }     /**     * <p>Creates a connector client for the connector server at the     * given address.  The resultant client is not connected until its     * {@link JMXConnector#connect(Map) connect} method is called.</p>     *     * @param serviceURL the address of the connector server to connect to.     *     * @param environment a set of attributes to determine how the     * connection is made.  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.     *     * @return a <code>JMXConnector</code> representing the new     * connector client.  Each successful call to this method produces     * a different object.     *     * @exception NullPointerException if <code>serviceURL</code> is null.     *     * @exception IOException if the connector client 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 JMXConnector newJMXConnector(JMXServiceURL serviceURL,                                               Map<String,?> environment)            throws IOException {         final Map<String,Object> envcopy;        if (environment == null)            envcopy = newHashMap();        else {            EnvHelp.checkAttributes(environment);            envcopy = newHashMap(environment);        }         final ClassLoader loader = resolveClassLoader(envcopy);        final Class<JMXConnectorProvider> targetInterface =                JMXConnectorProvider.class;        final String protocol = serviceURL.getProtocol();        final String providerClassName = "ClientProvider";        final JMXServiceURL providerURL = serviceURL;         JMXConnectorProvider provider = getProvider(providerURL, envcopy,                                               providerClassName,                                               targetInterface,                                               loader);         IOException exception = null;        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) {                try {                    JMXConnector connection =                        getConnectorAsService(loader, providerURL, envcopy);                    if (connection != null)                        return connection;                } catch (JMXProviderException e) {                    throw e;                } catch (IOException e) {                    exception = e;                }            }            provider = getProvider(protocol, PROTOCOL_PROVIDER_DEFAULT_PACKAGE,                            JMXConnectorFactory.class.getClassLoader(),                            providerClassName, targetInterface);        }         if (provider == null) {            MalformedURLException e =                new MalformedURLException("Unsupported protocol: " + protocol);            if (exception == null) {                throw e;            } else {                throw EnvHelp.initCause(e, exception);            }        }         final Map<String,Object> fixedenv =                Collections.unmodifiableMap(envcopy);         return provider.newJMXConnector(serviceURL, fixedenv);    }     private static String resolvePkgs(Map<String, ?> env)            throws JMXProviderException {         Object pkgsObject = null;         if (env != null)            pkgsObject = env.get(PROTOCOL_PROVIDER_PACKAGES);         if (pkgsObject == null)            pkgsObject =                AccessController.doPrivileged(new PrivilegedAction<String>() {                    public String run() {                        return System.getProperty(PROTOCOL_PROVIDER_PACKAGES);                    }                });         if (pkgsObject == null)            return null;         if (!(pkgsObject instanceof String)) {            final String msg = "Value of " + PROTOCOL_PROVIDER_PACKAGES +                " parameter is not a String: " +                pkgsObject.getClass().getName();            throw new JMXProviderException(msg);        }         final String pkgs = (String) pkgsObject;        if (pkgs.trim().equals(""))            return null;         // pkgs may not contain an empty element        if (pkgs.startsWith("|") || pkgs.endsWith("|") ||            pkgs.indexOf("||") >= 0) {            final String msg = "Value of " + PROTOCOL_PROVIDER_PACKAGES +                " contains an empty element: " + pkgs;            throw new JMXProviderException(msg);        }         return pkgs;    }     static <T> T getProvider(JMXServiceURL serviceURL,                             Map<String, Object> environment,                             String providerClassName,                             Class<T> targetInterface,                             ClassLoader loader)            throws IOException {         final String protocol = serviceURL.getProtocol();         final String pkgs = resolvePkgs(environment);         T instance = null;         if (pkgs != null) {            environment.put(PROTOCOL_PROVIDER_CLASS_LOADER, loader);             instance =                getProvider(protocol, pkgs, loader, providerClassName,                            targetInterface);        }         return instance;    }     static <T> Iterator<T> getProviderIterator(final Class<T> providerClass,                                               final ClassLoader loader) {       ServiceLoader<T> serviceLoader =                ServiceLoader.load(providerClass, loader);       return serviceLoader.iterator();    }     private static JMXConnector getConnectorAsService(ClassLoader loader,                                                      JMXServiceURL url,                                                      Map<String, ?> map)        throws IOException {         Iterator<JMXConnectorProvider> providers =                getProviderIterator(JMXConnectorProvider.class, loader);        JMXConnector connection;        IOException exception = null;        while (providers.hasNext()) {            JMXConnectorProvider provider = providers.next();            try {                connection = provider.newJMXConnector(url, map);                return connection;            } catch (JMXProviderException e) {                throw e;            } catch (Exception e) {                if (logger.traceOn())                    logger.trace("getConnectorAsService",                                 "URL[" + url +                                 "] Service provider exception: " + e);                if (!(e instanceof MalformedURLException)) {                    if (exception == null) {                        if (e instanceof IOException) {                            exception = (IOException) e;                        } else {                            exception = EnvHelp.initCause(                                new IOException(e.getMessage()), e);                        }                    }                }                continue;            }        }        if (exception == null)            return null;        else            throw exception;    }     static <T> T getProvider(String protocol,                              String pkgs,                              ClassLoader loader,                              String providerClassName,                              Class<T> targetInterface)            throws IOException {         StringTokenizer tokenizer = new StringTokenizer(pkgs, "|");         while (tokenizer.hasMoreTokens()) {            String pkg = tokenizer.nextToken();            String className = (pkg + "." + protocol2package(protocol) +                                "." + providerClassName);            Class<?> providerClass;            try {                providerClass = Class.forName(className, true, loader);            } catch (ClassNotFoundException e) {                //Add trace.

Lines 1–500 of 559.