jdk/src/java.management/share/classes/javax/management/remote/JMXConnectorFactory.java
changeset 25859 3317bb8137f4
parent 24368 2b4801b94265
child 32639 339de1317e84
equal deleted inserted replaced
25858:836adbf7a2cd 25859:3317bb8137f4
       
     1 /*
       
     2  * Copyright (c) 2002, 2014, 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 import com.sun.jmx.mbeanserver.Util;
       
    29 import java.io.IOException;
       
    30 import java.net.MalformedURLException;
       
    31 import java.util.Collections;
       
    32 import java.util.HashMap;
       
    33 import java.util.Map;
       
    34 import java.util.Iterator;
       
    35 import java.util.ServiceLoader;
       
    36 import java.util.StringTokenizer;
       
    37 import java.security.AccessController;
       
    38 import java.security.PrivilegedAction;
       
    39 
       
    40 import com.sun.jmx.remote.util.ClassLogger;
       
    41 import com.sun.jmx.remote.util.EnvHelp;
       
    42 import sun.reflect.misc.ReflectUtil;
       
    43 
       
    44 
       
    45 /**
       
    46  * <p>Factory to create JMX API connector clients.  There
       
    47  * are no instances of this class.</p>
       
    48  *
       
    49  * <p>Connections are usually made using the {@link
       
    50  * #connect(JMXServiceURL) connect} method of this class.  More
       
    51  * advanced applications can separate the creation of the connector
       
    52  * client, using {@link #newJMXConnector(JMXServiceURL, Map)
       
    53  * newJMXConnector} and the establishment of the connection itself, using
       
    54  * {@link JMXConnector#connect(Map)}.</p>
       
    55  *
       
    56  * <p>Each client is created by an instance of {@link
       
    57  * JMXConnectorProvider}.  This instance is found as follows.  Suppose
       
    58  * the given {@link JMXServiceURL} looks like
       
    59  * <code>"service:jmx:<em>protocol</em>:<em>remainder</em>"</code>.
       
    60  * Then the factory will attempt to find the appropriate {@link
       
    61  * JMXConnectorProvider} for <code><em>protocol</em></code>.  Each
       
    62  * occurrence of the character <code>+</code> or <code>-</code> in
       
    63  * <code><em>protocol</em></code> is replaced by <code>.</code> or
       
    64  * <code>_</code>, respectively.</p>
       
    65  *
       
    66  * <p>A <em>provider package list</em> is searched for as follows:</p>
       
    67  *
       
    68  * <ol>
       
    69  *
       
    70  * <li>If the <code>environment</code> parameter to {@link
       
    71  * #newJMXConnector(JMXServiceURL, Map) newJMXConnector} contains the
       
    72  * key <code>jmx.remote.protocol.provider.pkgs</code> then the
       
    73  * associated value is the provider package list.
       
    74  *
       
    75  * <li>Otherwise, if the system property
       
    76  * <code>jmx.remote.protocol.provider.pkgs</code> exists, then its value
       
    77  * is the provider package list.
       
    78  *
       
    79  * <li>Otherwise, there is no provider package list.
       
    80  *
       
    81  * </ol>
       
    82  *
       
    83  * <p>The provider package list is a string that is interpreted as a
       
    84  * list of non-empty Java package names separated by vertical bars
       
    85  * (<code>|</code>).  If the string is empty, then so is the provider
       
    86  * package list.  If the provider package list is not a String, or if
       
    87  * it contains an element that is an empty string, a {@link
       
    88  * JMXProviderException} is thrown.</p>
       
    89  *
       
    90  * <p>If the provider package list exists and is not empty, then for
       
    91  * each element <code><em>pkg</em></code> of the list, the factory
       
    92  * will attempt to load the class
       
    93  *
       
    94  * <blockquote>
       
    95  * <code><em>pkg</em>.<em>protocol</em>.ClientProvider</code>
       
    96  * </blockquote>
       
    97 
       
    98  * <p>If the <code>environment</code> parameter to {@link
       
    99  * #newJMXConnector(JMXServiceURL, Map) newJMXConnector} contains the
       
   100  * key <code>jmx.remote.protocol.provider.class.loader</code> then the
       
   101  * associated value is the class loader to use to load the provider.
       
   102  * If the associated value is not an instance of {@link
       
   103  * java.lang.ClassLoader}, an {@link
       
   104  * java.lang.IllegalArgumentException} is thrown.</p>
       
   105  *
       
   106  * <p>If the <code>jmx.remote.protocol.provider.class.loader</code>
       
   107  * key is not present in the <code>environment</code> parameter, the
       
   108  * calling thread's context class loader is used.</p>
       
   109  *
       
   110  * <p>If the attempt to load this class produces a {@link
       
   111  * ClassNotFoundException}, the search for a handler continues with
       
   112  * the next element of the list.</p>
       
   113  *
       
   114  * <p>Otherwise, a problem with the provider found is signalled by a
       
   115  * {@link JMXProviderException} whose {@link
       
   116  * JMXProviderException#getCause() <em>cause</em>} indicates the underlying
       
   117  * exception, as follows:</p>
       
   118  *
       
   119  * <ul>
       
   120  *
       
   121  * <li>if the attempt to load the class produces an exception other
       
   122  * than <code>ClassNotFoundException</code>, that is the
       
   123  * <em>cause</em>;
       
   124  *
       
   125  * <li>if {@link Class#newInstance()} for the class produces an
       
   126  * exception, that is the <em>cause</em>.
       
   127  *
       
   128  * </ul>
       
   129  *
       
   130  * <p>If no provider is found by the above steps, including the
       
   131  * default case where there is no provider package list, then the
       
   132  * implementation will use its own provider for
       
   133  * <code><em>protocol</em></code>, or it will throw a
       
   134  * <code>MalformedURLException</code> if there is none.  An
       
   135  * implementation may choose to find providers by other means.  For
       
   136  * example, it may support the <a
       
   137  * href="{@docRoot}/../technotes/guides/jar/jar.html#Service%20Provider">
       
   138  * JAR conventions for service providers</a>, where the service
       
   139  * interface is <code>JMXConnectorProvider</code>.</p>
       
   140  *
       
   141  * <p>Every implementation must support the RMI connector protocol with
       
   142  * the default RMI transport, specified with string <code>rmi</code>.
       
   143  * An implementation may optionally support the RMI connector protocol
       
   144  * with the RMI/IIOP transport, specified with the string
       
   145  * <code>iiop</code>.</p>
       
   146  *
       
   147  * <p>Once a provider is found, the result of the
       
   148  * <code>newJMXConnector</code> method is the result of calling {@link
       
   149  * JMXConnectorProvider#newJMXConnector(JMXServiceURL,Map) newJMXConnector}
       
   150  * on the provider.</p>
       
   151  *
       
   152  * <p>The <code>Map</code> parameter passed to the
       
   153  * <code>JMXConnectorProvider</code> is a new read-only
       
   154  * <code>Map</code> that contains all the entries that were in the
       
   155  * <code>environment</code> parameter to {@link
       
   156  * #newJMXConnector(JMXServiceURL,Map)
       
   157  * JMXConnectorFactory.newJMXConnector}, if there was one.
       
   158  * Additionally, if the
       
   159  * <code>jmx.remote.protocol.provider.class.loader</code> key is not
       
   160  * present in the <code>environment</code> parameter, it is added to
       
   161  * the new read-only <code>Map</code>.  The associated value is the
       
   162  * calling thread's context class loader.</p>
       
   163  *
       
   164  * @since 1.5
       
   165  */
       
   166 public class JMXConnectorFactory {
       
   167 
       
   168     /**
       
   169      * <p>Name of the attribute that specifies the default class
       
   170      * loader. This class loader is used to deserialize return values and
       
   171      * exceptions from remote <code>MBeanServerConnection</code>
       
   172      * calls.  The value associated with this attribute is an instance
       
   173      * of {@link ClassLoader}.</p>
       
   174      */
       
   175     public static final String DEFAULT_CLASS_LOADER =
       
   176         "jmx.remote.default.class.loader";
       
   177 
       
   178     /**
       
   179      * <p>Name of the attribute that specifies the provider packages
       
   180      * that are consulted when looking for the handler for a protocol.
       
   181      * The value associated with this attribute is a string with
       
   182      * package names separated by vertical bars (<code>|</code>).</p>
       
   183      */
       
   184     public static final String PROTOCOL_PROVIDER_PACKAGES =
       
   185         "jmx.remote.protocol.provider.pkgs";
       
   186 
       
   187     /**
       
   188      * <p>Name of the attribute that specifies the class
       
   189      * loader for loading protocol providers.
       
   190      * The value associated with this attribute is an instance
       
   191      * of {@link ClassLoader}.</p>
       
   192      */
       
   193     public static final String PROTOCOL_PROVIDER_CLASS_LOADER =
       
   194         "jmx.remote.protocol.provider.class.loader";
       
   195 
       
   196     private static final String PROTOCOL_PROVIDER_DEFAULT_PACKAGE =
       
   197         "com.sun.jmx.remote.protocol";
       
   198 
       
   199     private static final ClassLogger logger =
       
   200         new ClassLogger("javax.management.remote.misc", "JMXConnectorFactory");
       
   201 
       
   202     /** There are no instances of this class.  */
       
   203     private JMXConnectorFactory() {
       
   204     }
       
   205 
       
   206     /**
       
   207      * <p>Creates a connection to the connector server at the given
       
   208      * address.</p>
       
   209      *
       
   210      * <p>This method is equivalent to {@link
       
   211      * #connect(JMXServiceURL,Map) connect(serviceURL, null)}.</p>
       
   212      *
       
   213      * @param serviceURL the address of the connector server to
       
   214      * connect to.
       
   215      *
       
   216      * @return a <code>JMXConnector</code> whose {@link
       
   217      * JMXConnector#connect connect} method has been called.
       
   218      *
       
   219      * @exception NullPointerException if <code>serviceURL</code> is null.
       
   220      *
       
   221      * @exception IOException if the connector client or the
       
   222      * connection cannot be made because of a communication problem.
       
   223      *
       
   224      * @exception SecurityException if the connection cannot be made
       
   225      * for security reasons.
       
   226      */
       
   227     public static JMXConnector connect(JMXServiceURL serviceURL)
       
   228             throws IOException {
       
   229         return connect(serviceURL, null);
       
   230     }
       
   231 
       
   232     /**
       
   233      * <p>Creates a connection to the connector server at the given
       
   234      * address.</p>
       
   235      *
       
   236      * <p>This method is equivalent to:</p>
       
   237      *
       
   238      * <pre>
       
   239      * JMXConnector conn = JMXConnectorFactory.newJMXConnector(serviceURL,
       
   240      *                                                         environment);
       
   241      * conn.connect(environment);
       
   242      * </pre>
       
   243      *
       
   244      * @param serviceURL the address of the connector server to connect to.
       
   245      *
       
   246      * @param environment a set of attributes to determine how the
       
   247      * connection is made.  This parameter can be null.  Keys in this
       
   248      * map must be Strings.  The appropriate type of each associated
       
   249      * value depends on the attribute.  The contents of
       
   250      * <code>environment</code> are not changed by this call.
       
   251      *
       
   252      * @return a <code>JMXConnector</code> representing the newly-made
       
   253      * connection.  Each successful call to this method produces a
       
   254      * different object.
       
   255      *
       
   256      * @exception NullPointerException if <code>serviceURL</code> is null.
       
   257      *
       
   258      * @exception IOException if the connector client or the
       
   259      * connection cannot be made because of a communication problem.
       
   260      *
       
   261      * @exception SecurityException if the connection cannot be made
       
   262      * for security reasons.
       
   263      */
       
   264     public static JMXConnector connect(JMXServiceURL serviceURL,
       
   265                                        Map<String,?> environment)
       
   266             throws IOException {
       
   267         if (serviceURL == null)
       
   268             throw new NullPointerException("Null JMXServiceURL");
       
   269         JMXConnector conn = newJMXConnector(serviceURL, environment);
       
   270         conn.connect(environment);
       
   271         return conn;
       
   272     }
       
   273 
       
   274     private static <K,V> Map<K,V> newHashMap() {
       
   275         return new HashMap<K,V>();
       
   276     }
       
   277 
       
   278     private static <K> Map<K,Object> newHashMap(Map<K,?> map) {
       
   279         return new HashMap<K,Object>(map);
       
   280     }
       
   281 
       
   282     /**
       
   283      * <p>Creates a connector client for the connector server at the
       
   284      * given address.  The resultant client is not connected until its
       
   285      * {@link JMXConnector#connect(Map) connect} method is called.</p>
       
   286      *
       
   287      * @param serviceURL the address of the connector server to connect to.
       
   288      *
       
   289      * @param environment a set of attributes to determine how the
       
   290      * connection is made.  This parameter can be null.  Keys in this
       
   291      * map must be Strings.  The appropriate type of each associated
       
   292      * value depends on the attribute.  The contents of
       
   293      * <code>environment</code> are not changed by this call.
       
   294      *
       
   295      * @return a <code>JMXConnector</code> representing the new
       
   296      * connector client.  Each successful call to this method produces
       
   297      * a different object.
       
   298      *
       
   299      * @exception NullPointerException if <code>serviceURL</code> is null.
       
   300      *
       
   301      * @exception IOException if the connector client cannot be made
       
   302      * because of a communication problem.
       
   303      *
       
   304      * @exception MalformedURLException if there is no provider for the
       
   305      * protocol in <code>serviceURL</code>.
       
   306      *
       
   307      * @exception JMXProviderException if there is a provider for the
       
   308      * protocol in <code>serviceURL</code> but it cannot be used for
       
   309      * some reason.
       
   310      */
       
   311     public static JMXConnector newJMXConnector(JMXServiceURL serviceURL,
       
   312                                                Map<String,?> environment)
       
   313             throws IOException {
       
   314 
       
   315         final Map<String,Object> envcopy;
       
   316         if (environment == null)
       
   317             envcopy = newHashMap();
       
   318         else {
       
   319             EnvHelp.checkAttributes(environment);
       
   320             envcopy = newHashMap(environment);
       
   321         }
       
   322 
       
   323         final ClassLoader loader = resolveClassLoader(envcopy);
       
   324         final Class<JMXConnectorProvider> targetInterface =
       
   325                 JMXConnectorProvider.class;
       
   326         final String protocol = serviceURL.getProtocol();
       
   327         final String providerClassName = "ClientProvider";
       
   328         final JMXServiceURL providerURL = serviceURL;
       
   329 
       
   330         JMXConnectorProvider provider = getProvider(providerURL, envcopy,
       
   331                                                providerClassName,
       
   332                                                targetInterface,
       
   333                                                loader);
       
   334 
       
   335         IOException exception = null;
       
   336         if (provider == null) {
       
   337             // Loader is null when context class loader is set to null
       
   338             // and no loader has been provided in map.
       
   339             // com.sun.jmx.remote.util.Service class extracted from j2se
       
   340             // provider search algorithm doesn't handle well null classloader.
       
   341             if (loader != null) {
       
   342                 try {
       
   343                     JMXConnector connection =
       
   344                         getConnectorAsService(loader, providerURL, envcopy);
       
   345                     if (connection != null)
       
   346                         return connection;
       
   347                 } catch (JMXProviderException e) {
       
   348                     throw e;
       
   349                 } catch (IOException e) {
       
   350                     exception = e;
       
   351                 }
       
   352             }
       
   353             provider = getProvider(protocol, PROTOCOL_PROVIDER_DEFAULT_PACKAGE,
       
   354                             JMXConnectorFactory.class.getClassLoader(),
       
   355                             providerClassName, targetInterface);
       
   356         }
       
   357 
       
   358         if (provider == null) {
       
   359             MalformedURLException e =
       
   360                 new MalformedURLException("Unsupported protocol: " + protocol);
       
   361             if (exception == null) {
       
   362                 throw e;
       
   363             } else {
       
   364                 throw EnvHelp.initCause(e, exception);
       
   365             }
       
   366         }
       
   367 
       
   368         final Map<String,Object> fixedenv =
       
   369                 Collections.unmodifiableMap(envcopy);
       
   370 
       
   371         return provider.newJMXConnector(serviceURL, fixedenv);
       
   372     }
       
   373 
       
   374     private static String resolvePkgs(Map<String, ?> env)
       
   375             throws JMXProviderException {
       
   376 
       
   377         Object pkgsObject = null;
       
   378 
       
   379         if (env != null)
       
   380             pkgsObject = env.get(PROTOCOL_PROVIDER_PACKAGES);
       
   381 
       
   382         if (pkgsObject == null)
       
   383             pkgsObject =
       
   384                 AccessController.doPrivileged(new PrivilegedAction<String>() {
       
   385                     public String run() {
       
   386                         return System.getProperty(PROTOCOL_PROVIDER_PACKAGES);
       
   387                     }
       
   388                 });
       
   389 
       
   390         if (pkgsObject == null)
       
   391             return null;
       
   392 
       
   393         if (!(pkgsObject instanceof String)) {
       
   394             final String msg = "Value of " + PROTOCOL_PROVIDER_PACKAGES +
       
   395                 " parameter is not a String: " +
       
   396                 pkgsObject.getClass().getName();
       
   397             throw new JMXProviderException(msg);
       
   398         }
       
   399 
       
   400         final String pkgs = (String) pkgsObject;
       
   401         if (pkgs.trim().equals(""))
       
   402             return null;
       
   403 
       
   404         // pkgs may not contain an empty element
       
   405         if (pkgs.startsWith("|") || pkgs.endsWith("|") ||
       
   406             pkgs.indexOf("||") >= 0) {
       
   407             final String msg = "Value of " + PROTOCOL_PROVIDER_PACKAGES +
       
   408                 " contains an empty element: " + pkgs;
       
   409             throw new JMXProviderException(msg);
       
   410         }
       
   411 
       
   412         return pkgs;
       
   413     }
       
   414 
       
   415     static <T> T getProvider(JMXServiceURL serviceURL,
       
   416                              final Map<String, Object> environment,
       
   417                              String providerClassName,
       
   418                              Class<T> targetInterface,
       
   419                              final ClassLoader loader)
       
   420             throws IOException {
       
   421 
       
   422         final String protocol = serviceURL.getProtocol();
       
   423 
       
   424         final String pkgs = resolvePkgs(environment);
       
   425 
       
   426         T instance = null;
       
   427 
       
   428         if (pkgs != null) {
       
   429             instance =
       
   430                 getProvider(protocol, pkgs, loader, providerClassName,
       
   431                             targetInterface);
       
   432 
       
   433             if (instance != null) {
       
   434                 boolean needsWrap = (loader != instance.getClass().getClassLoader());
       
   435                 environment.put(PROTOCOL_PROVIDER_CLASS_LOADER, needsWrap ? wrap(loader) : loader);
       
   436             }
       
   437         }
       
   438 
       
   439         return instance;
       
   440     }
       
   441 
       
   442     static <T> Iterator<T> getProviderIterator(final Class<T> providerClass,
       
   443                                                final ClassLoader loader) {
       
   444        ServiceLoader<T> serviceLoader =
       
   445                 ServiceLoader.load(providerClass, loader);
       
   446        return serviceLoader.iterator();
       
   447     }
       
   448 
       
   449     private static ClassLoader wrap(final ClassLoader parent) {
       
   450         return parent != null ? AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
       
   451             @Override
       
   452             public ClassLoader run() {
       
   453                 return new ClassLoader(parent) {
       
   454                     @Override
       
   455                     protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
       
   456                         ReflectUtil.checkPackageAccess(name);
       
   457                         return super.loadClass(name, resolve);
       
   458                     }
       
   459                 };
       
   460             }
       
   461         }) : null;
       
   462     }
       
   463 
       
   464     private static JMXConnector getConnectorAsService(ClassLoader loader,
       
   465                                                       JMXServiceURL url,
       
   466                                                       Map<String, ?> map)
       
   467         throws IOException {
       
   468 
       
   469         Iterator<JMXConnectorProvider> providers =
       
   470                 getProviderIterator(JMXConnectorProvider.class, loader);
       
   471         JMXConnector connection;
       
   472         IOException exception = null;
       
   473         while (providers.hasNext()) {
       
   474             JMXConnectorProvider provider = providers.next();
       
   475             try {
       
   476                 connection = provider.newJMXConnector(url, map);
       
   477                 return connection;
       
   478             } catch (JMXProviderException e) {
       
   479                 throw e;
       
   480             } catch (Exception e) {
       
   481                 if (logger.traceOn())
       
   482                     logger.trace("getConnectorAsService",
       
   483                                  "URL[" + url +
       
   484                                  "] Service provider exception: " + e);
       
   485                 if (!(e instanceof MalformedURLException)) {
       
   486                     if (exception == null) {
       
   487                         if (e instanceof IOException) {
       
   488                             exception = (IOException) e;
       
   489                         } else {
       
   490                             exception = EnvHelp.initCause(
       
   491                                 new IOException(e.getMessage()), e);
       
   492                         }
       
   493                     }
       
   494                 }
       
   495                 continue;
       
   496             }
       
   497         }
       
   498         if (exception == null)
       
   499             return null;
       
   500         else
       
   501             throw exception;
       
   502     }
       
   503 
       
   504     static <T> T getProvider(String protocol,
       
   505                               String pkgs,
       
   506                               ClassLoader loader,
       
   507                               String providerClassName,
       
   508                               Class<T> targetInterface)
       
   509             throws IOException {
       
   510 
       
   511         StringTokenizer tokenizer = new StringTokenizer(pkgs, "|");
       
   512 
       
   513         while (tokenizer.hasMoreTokens()) {
       
   514             String pkg = tokenizer.nextToken();
       
   515             String className = (pkg + "." + protocol2package(protocol) +
       
   516                                 "." + providerClassName);
       
   517             Class<?> providerClass;
       
   518             try {
       
   519                 providerClass = Class.forName(className, true, loader);
       
   520             } catch (ClassNotFoundException e) {
       
   521                 //Add trace.
       
   522                 continue;
       
   523             }
       
   524 
       
   525             if (!targetInterface.isAssignableFrom(providerClass)) {
       
   526                 final String msg =
       
   527                     "Provider class does not implement " +
       
   528                     targetInterface.getName() + ": " +
       
   529                     providerClass.getName();
       
   530                 throw new JMXProviderException(msg);
       
   531             }
       
   532 
       
   533             // We have just proved that this cast is correct
       
   534             Class<? extends T> providerClassT = Util.cast(providerClass);
       
   535             try {
       
   536                 return providerClassT.newInstance();
       
   537             } catch (Exception e) {
       
   538                 final String msg =
       
   539                     "Exception when instantiating provider [" + className +
       
   540                     "]";
       
   541                 throw new JMXProviderException(msg, e);
       
   542             }
       
   543         }
       
   544 
       
   545         return null;
       
   546     }
       
   547 
       
   548     static ClassLoader resolveClassLoader(Map<String, ?> environment) {
       
   549         ClassLoader loader = null;
       
   550 
       
   551         if (environment != null) {
       
   552             try {
       
   553                 loader = (ClassLoader)
       
   554                     environment.get(PROTOCOL_PROVIDER_CLASS_LOADER);
       
   555             } catch (ClassCastException e) {
       
   556                 final String msg =
       
   557                     "The ClassLoader supplied in the environment map using " +
       
   558                     "the " + PROTOCOL_PROVIDER_CLASS_LOADER +
       
   559                     " attribute is not an instance of java.lang.ClassLoader";
       
   560                 throw new IllegalArgumentException(msg);
       
   561             }
       
   562         }
       
   563 
       
   564         if (loader == null) {
       
   565             loader = Thread.currentThread().getContextClassLoader();
       
   566         }
       
   567 
       
   568         return loader;
       
   569     }
       
   570 
       
   571     private static String protocol2package(String protocol) {
       
   572         return protocol.replace('+', '.').replace('-', '_');
       
   573     }
       
   574 }