src/java.management/share/classes/javax/management/remote/JMXConnectorServerFactory.java
changeset 47216 71c04702a3d5
parent 45933 880c6fdf23e7
child 52469 190b77982361
equal deleted inserted replaced
47215:4ebc2e2fb97c 47216:71c04702a3d5
       
     1 /*
       
     2  * Copyright (c) 2003, 2017, Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.  Oracle designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Oracle in the LICENSE file that accompanied this code.
       
    10  *
       
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    14  * version 2 for more details (a copy is included in the LICENSE file that
       
    15  * accompanied this code).
       
    16  *
       
    17  * You should have received a copy of the GNU General Public License version
       
    18  * 2 along with this work; if not, write to the Free Software Foundation,
       
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    20  *
       
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    22  * or visit www.oracle.com if you need additional information or have any
       
    23  * questions.
       
    24  */
       
    25 
       
    26 package javax.management.remote;
       
    27 
       
    28 
       
    29 import com.sun.jmx.remote.util.ClassLogger;
       
    30 import com.sun.jmx.remote.util.EnvHelp;
       
    31 
       
    32 import java.io.IOException;
       
    33 import java.io.UncheckedIOException;
       
    34 import java.net.MalformedURLException;
       
    35 import java.util.Collections;
       
    36 import java.util.HashMap;
       
    37 import java.util.Map;
       
    38 import java.util.ServiceLoader.Provider;
       
    39 import java.util.function.Predicate;
       
    40 
       
    41 import javax.management.MBeanServer;
       
    42 import javax.management.remote.JMXConnectorFactory.ConnectorFactory;
       
    43 
       
    44 /**
       
    45  * <p>Factory to create JMX API connector servers.  There
       
    46  * are no instances of this class.</p>
       
    47  *
       
    48  * <p>Each connector server is created by an instance of {@link
       
    49  * JMXConnectorServerProvider}.  This instance is found as follows.  Suppose
       
    50  * the given {@link JMXServiceURL} looks like
       
    51  * <code>"service:jmx:<em>protocol</em>:<em>remainder</em>"</code>.
       
    52  * Then the factory will attempt to find the appropriate {@link
       
    53  * JMXConnectorServerProvider} for <code><em>protocol</em></code>.  Each
       
    54  * occurrence of the character <code>+</code> or <code>-</code> in
       
    55  * <code><em>protocol</em></code> is replaced by <code>.</code> or
       
    56  * <code>_</code>, respectively.</p>
       
    57  *
       
    58  * <p>A <em>provider package list</em> is searched for as follows:</p>
       
    59  *
       
    60  * <ol>
       
    61  *
       
    62  * <li>If the <code>environment</code> parameter to {@link
       
    63  * #newJMXConnectorServer(JMXServiceURL,Map,MBeanServer)
       
    64  * newJMXConnectorServer} contains the key
       
    65  * <code>jmx.remote.protocol.provider.pkgs</code> then the associated
       
    66  * value is the provider package list.
       
    67  *
       
    68  * <li>Otherwise, if the system property
       
    69  * <code>jmx.remote.protocol.provider.pkgs</code> exists, then its value
       
    70  * is the provider package list.
       
    71  *
       
    72  * <li>Otherwise, there is no provider package list.
       
    73  *
       
    74  * </ol>
       
    75  *
       
    76  * <p>The provider package list is a string that is interpreted as a
       
    77  * list of non-empty Java package names separated by vertical bars
       
    78  * (<code>|</code>).  If the string is empty, then so is the provider
       
    79  * package list.  If the provider package list is not a String, or if
       
    80  * it contains an element that is an empty string, a {@link
       
    81  * JMXProviderException} is thrown.</p>
       
    82  *
       
    83  * <p>If the provider package list exists and is not empty, then for
       
    84  * each element <code><em>pkg</em></code> of the list, the factory
       
    85  * will attempt to load the class
       
    86  *
       
    87  * <blockquote>
       
    88  * <code><em>pkg</em>.<em>protocol</em>.ServerProvider</code>
       
    89  * </blockquote>
       
    90 
       
    91  * <p>If the <code>environment</code> parameter to {@link
       
    92  * #newJMXConnectorServer(JMXServiceURL, Map, MBeanServer)
       
    93  * newJMXConnectorServer} contains the key
       
    94  * <code>jmx.remote.protocol.provider.class.loader</code> then the
       
    95  * associated value is the class loader to use to load the provider.
       
    96  * If the associated value is not an instance of {@link
       
    97  * java.lang.ClassLoader}, an {@link
       
    98  * java.lang.IllegalArgumentException} is thrown.</p>
       
    99  *
       
   100  * <p>If the <code>jmx.remote.protocol.provider.class.loader</code>
       
   101  * key is not present in the <code>environment</code> parameter, the
       
   102  * calling thread's context class loader is used.</p>
       
   103  *
       
   104  * <p>If the attempt to load this class produces a {@link
       
   105  * ClassNotFoundException}, the search for a handler continues with
       
   106  * the next element of the list.</p>
       
   107  *
       
   108  * <p>Otherwise, a problem with the provider found is signalled by a
       
   109  * {@link JMXProviderException} whose {@link
       
   110  * JMXProviderException#getCause() <em>cause</em>} indicates the
       
   111  * underlying exception, as follows:</p>
       
   112  *
       
   113  * <ul>
       
   114  *
       
   115  * <li>if the attempt to load the class produces an exception other
       
   116  * than <code>ClassNotFoundException</code>, that is the
       
   117  * <em>cause</em>;
       
   118  *
       
   119  * <li>if {@link Class#newInstance()} for the class produces an
       
   120  * exception, that is the <em>cause</em>.
       
   121  *
       
   122  * </ul>
       
   123  *
       
   124  * <p>If no provider is found by the above steps, including the
       
   125  * default case where there is no provider package list, then the
       
   126  * implementation will use its own provider for
       
   127  * <code><em>protocol</em></code>, or it will throw a
       
   128  * <code>MalformedURLException</code> if there is none.  An
       
   129  * implementation may choose to find providers by other means.  For
       
   130  * example, it may support <a
       
   131  * href="{@docRoot}/java/util/ServiceLoader.html#developing-service-providers">service providers</a>,
       
   132  * where the service interface is <code>JMXConnectorServerProvider</code>.</p>
       
   133  *
       
   134  * <p>Every implementation must support the RMI connector protocol with
       
   135  * the default RMI transport, specified with string <code>rmi</code>.
       
   136  * </p>
       
   137  *
       
   138  * <p>Once a provider is found, the result of the
       
   139  * <code>newJMXConnectorServer</code> method is the result of calling
       
   140  * {@link
       
   141  * JMXConnectorServerProvider#newJMXConnectorServer(JMXServiceURL,
       
   142  * Map, MBeanServer) newJMXConnectorServer} on the provider.</p>
       
   143  *
       
   144  * <p>The <code>Map</code> parameter passed to the
       
   145  * <code>JMXConnectorServerProvider</code> is a new read-only
       
   146  * <code>Map</code> that contains all the entries that were in the
       
   147  * <code>environment</code> parameter to {@link
       
   148  * #newJMXConnectorServer(JMXServiceURL,Map,MBeanServer)
       
   149  * JMXConnectorServerFactory.newJMXConnectorServer}, if there was one.
       
   150  * Additionally, if the
       
   151  * <code>jmx.remote.protocol.provider.class.loader</code> key is not
       
   152  * present in the <code>environment</code> parameter, it is added to
       
   153  * the new read-only <code>Map</code>. The associated value is the
       
   154  * calling thread's context class loader.</p>
       
   155  *
       
   156  * @since 1.5
       
   157  */
       
   158 public class JMXConnectorServerFactory {
       
   159 
       
   160     /**
       
   161      * <p>Name of the attribute that specifies the default class
       
   162      * loader.  This class loader is used to deserialize objects in
       
   163      * requests received from the client, possibly after consulting an
       
   164      * MBean-specific class loader.  The value associated with this
       
   165      * attribute is an instance of {@link ClassLoader}.</p>
       
   166      */
       
   167     public static final String DEFAULT_CLASS_LOADER =
       
   168         JMXConnectorFactory.DEFAULT_CLASS_LOADER;
       
   169 
       
   170     /**
       
   171      * <p>Name of the attribute that specifies the default class
       
   172      * loader MBean name.  This class loader is used to deserialize objects in
       
   173      * requests received from the client, possibly after consulting an
       
   174      * MBean-specific class loader.  The value associated with this
       
   175      * attribute is an instance of {@link javax.management.ObjectName
       
   176      * ObjectName}.</p>
       
   177      */
       
   178     public static final String DEFAULT_CLASS_LOADER_NAME =
       
   179         "jmx.remote.default.class.loader.name";
       
   180 
       
   181     /**
       
   182      * <p>Name of the attribute that specifies the provider packages
       
   183      * that are consulted when looking for the handler for a protocol.
       
   184      * The value associated with this attribute is a string with
       
   185      * package names separated by vertical bars (<code>|</code>).</p>
       
   186      */
       
   187     public static final String PROTOCOL_PROVIDER_PACKAGES =
       
   188         "jmx.remote.protocol.provider.pkgs";
       
   189 
       
   190     /**
       
   191      * <p>Name of the attribute that specifies the class
       
   192      * loader for loading protocol providers.
       
   193      * The value associated with this attribute is an instance
       
   194      * of {@link ClassLoader}.</p>
       
   195      */
       
   196     public static final String PROTOCOL_PROVIDER_CLASS_LOADER =
       
   197         "jmx.remote.protocol.provider.class.loader";
       
   198 
       
   199     private static final String PROTOCOL_PROVIDER_DEFAULT_PACKAGE =
       
   200         "com.sun.jmx.remote.protocol";
       
   201 
       
   202     private static final ClassLogger logger =
       
   203         new ClassLogger("javax.management.remote.misc","JMXConnectorServerFactory");
       
   204 
       
   205     /** There are no instances of this class.  */
       
   206     private JMXConnectorServerFactory() {
       
   207     }
       
   208 
       
   209     private static JMXConnectorServer
       
   210         getConnectorServerAsService(ClassLoader loader, JMXServiceURL url,
       
   211                                     Map<String, ?> map, MBeanServer mbs,
       
   212                                     Predicate<Provider<?>> filter)
       
   213         throws IOException {
       
   214         final ConnectorFactory<JMXConnectorServerProvider,JMXConnectorServer>
       
   215               factory = (p) -> p.newJMXConnectorServer(url, map, mbs);
       
   216         return JMXConnectorFactory.getConnectorAsService(
       
   217                                      JMXConnectorServerProvider.class,
       
   218                                      loader, url, filter, factory);
       
   219     }
       
   220 
       
   221     /**
       
   222      * <p>Creates a connector server at the given address.  The
       
   223      * resultant server is not started until its {@link
       
   224      * JMXConnectorServer#start() start} method is called.</p>
       
   225      *
       
   226      * @param serviceURL the address of the new connector server.  The
       
   227      * actual address of the new connector server, as returned by its
       
   228      * {@link JMXConnectorServer#getAddress() getAddress} method, will
       
   229      * not necessarily be exactly the same.  For example, it might
       
   230      * include a port number if the original address did not.
       
   231      *
       
   232      * @param environment a set of attributes to control the new
       
   233      * connector server's behavior.  This parameter can be null.
       
   234      * Keys in this map must be Strings.  The appropriate type of each
       
   235      * associated value depends on the attribute.  The contents of
       
   236      * <code>environment</code> are not changed by this call.
       
   237      *
       
   238      * @param mbeanServer the MBean server that this connector server
       
   239      * is attached to.  Null if this connector server will be attached
       
   240      * to an MBean server by being registered in it.
       
   241      *
       
   242      * @return a <code>JMXConnectorServer</code> representing the new
       
   243      * connector server.  Each successful call to this method produces
       
   244      * a different object.
       
   245      *
       
   246      * @exception NullPointerException if <code>serviceURL</code> is null.
       
   247      *
       
   248      * @exception IOException if the connector server cannot be made
       
   249      * because of a communication problem.
       
   250      *
       
   251      * @exception MalformedURLException if there is no provider for the
       
   252      * protocol in <code>serviceURL</code>.
       
   253      *
       
   254      * @exception JMXProviderException if there is a provider for the
       
   255      * protocol in <code>serviceURL</code> but it cannot be used for
       
   256      * some reason.
       
   257      */
       
   258     public static JMXConnectorServer
       
   259         newJMXConnectorServer(JMXServiceURL serviceURL,
       
   260                               Map<String,?> environment,
       
   261                               MBeanServer mbeanServer)
       
   262             throws IOException {
       
   263         Map<String, Object> envcopy;
       
   264         if (environment == null)
       
   265             envcopy = new HashMap<String, Object>();
       
   266         else {
       
   267             EnvHelp.checkAttributes(environment);
       
   268             envcopy = new HashMap<String, Object>(environment);
       
   269         }
       
   270 
       
   271         final Class<JMXConnectorServerProvider> targetInterface =
       
   272                 JMXConnectorServerProvider.class;
       
   273         final ClassLoader loader =
       
   274             JMXConnectorFactory.resolveClassLoader(envcopy);
       
   275         final String protocol = serviceURL.getProtocol();
       
   276         final String providerClassName = "ServerProvider";
       
   277 
       
   278         JMXConnectorServerProvider provider =
       
   279             JMXConnectorFactory.getProvider(serviceURL,
       
   280                                             envcopy,
       
   281                                             providerClassName,
       
   282                                             targetInterface,
       
   283                                             loader);
       
   284 
       
   285         IOException exception = null;
       
   286         JMXConnectorServer connection = null;
       
   287         if (provider == null) {
       
   288             Predicate<Provider<?>> systemProvider =
       
   289                     JMXConnectorFactory::isSystemProvider;
       
   290             // Loader is null when context class loader is set to null
       
   291             // and no loader has been provided in map.
       
   292             // com.sun.jmx.remote.util.Service class extracted from j2se
       
   293             // provider search algorithm doesn't handle well null classloader.
       
   294             if (loader != null) {
       
   295                 try {
       
   296                     connection =
       
   297                         getConnectorServerAsService(loader,
       
   298                                                     serviceURL,
       
   299                                                     envcopy,
       
   300                                                     mbeanServer,
       
   301                                                     systemProvider.negate());
       
   302                     if (connection != null)
       
   303                         return connection;
       
   304                 } catch (JMXProviderException e) {
       
   305                     throw e;
       
   306                 } catch (IOException e) {
       
   307                     exception = e;
       
   308                 }
       
   309             }
       
   310             connection = getConnectorServerAsService(
       
   311                             JMXConnectorFactory.class.getClassLoader(),
       
   312                             serviceURL,
       
   313                             Collections.unmodifiableMap(envcopy),
       
   314                             mbeanServer,
       
   315                             systemProvider);
       
   316             if (connection != null) return connection;
       
   317         }
       
   318 
       
   319         if (provider == null) {
       
   320             MalformedURLException e =
       
   321                 new MalformedURLException("Unsupported protocol: " + protocol);
       
   322             if (exception == null) {
       
   323                 throw e;
       
   324             } else {
       
   325                 throw EnvHelp.initCause(e, exception);
       
   326             }
       
   327         }
       
   328 
       
   329         envcopy = Collections.unmodifiableMap(envcopy);
       
   330 
       
   331         return provider.newJMXConnectorServer(serviceURL,
       
   332                                               envcopy,
       
   333                                               mbeanServer);
       
   334     }
       
   335 }