< prev index next >

jdk/src/java.management/share/classes/javax/management/remote/JMXConnectorFactory.java

Print this page




  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


 315             envcopy = newHashMap();
 316         else {
 317             EnvHelp.checkAttributes(environment);
 318             envcopy = newHashMap(environment);
 319         }
 320 
 321         final ClassLoader loader = resolveClassLoader(envcopy);
 322         final Class<JMXConnectorProvider> targetInterface =
 323                 JMXConnectorProvider.class;
 324         final String protocol = serviceURL.getProtocol();
 325         final String providerClassName = "ClientProvider";
 326         final JMXServiceURL providerURL = serviceURL;
 327 
 328         JMXConnectorProvider provider = getProvider(providerURL, envcopy,
 329                                                providerClassName,
 330                                                targetInterface,
 331                                                loader);
 332 
 333         IOException exception = null;
 334         if (provider == null) {


 335             // Loader is null when context class loader is set to null
 336             // and no loader has been provided in map.
 337             // com.sun.jmx.remote.util.Service class extracted from j2se
 338             // provider search algorithm doesn't handle well null classloader.

 339             if (loader != null) {
 340                 try {
 341                     JMXConnector connection =
 342                         getConnectorAsService(loader, providerURL, envcopy);
 343                     if (connection != null)
 344                         return connection;

 345                 } catch (JMXProviderException e) {
 346                     throw e;
 347                 } catch (IOException e) {
 348                     exception = e;
 349                 }
 350             }
 351             provider = getProvider(protocol, PROTOCOL_PROVIDER_DEFAULT_PACKAGE,
 352                             JMXConnectorFactory.class.getClassLoader(),
 353                             providerClassName, targetInterface);



 354         }
 355 
 356         if (provider == null) {
 357             MalformedURLException e =
 358                 new MalformedURLException("Unsupported protocol: " + protocol);
 359             if (exception == null) {
 360                 throw e;
 361             } else {
 362                 throw EnvHelp.initCause(e, exception);
 363             }
 364         }
 365 
 366         final Map<String,Object> fixedenv =
 367                 Collections.unmodifiableMap(envcopy);
 368 
 369         return provider.newJMXConnector(serviceURL, fixedenv);
 370     }
 371 
 372     private static String resolvePkgs(Map<String, ?> env)
 373             throws JMXProviderException {


 420         final String protocol = serviceURL.getProtocol();
 421 
 422         final String pkgs = resolvePkgs(environment);
 423 
 424         T instance = null;
 425 
 426         if (pkgs != null) {
 427             instance =
 428                 getProvider(protocol, pkgs, loader, providerClassName,
 429                             targetInterface);
 430 
 431             if (instance != null) {
 432                 boolean needsWrap = (loader != instance.getClass().getClassLoader());
 433                 environment.put(PROTOCOL_PROVIDER_CLASS_LOADER, needsWrap ? wrap(loader) : loader);
 434             }
 435         }
 436 
 437         return instance;
 438     }
 439 
 440     static <T> Iterator<T> getProviderIterator(final Class<T> providerClass,
 441                                                final ClassLoader loader) {
 442        ServiceLoader<T> serviceLoader =
 443                 ServiceLoader.load(providerClass, loader);
 444        return serviceLoader.iterator();
 445     }
 446 
 447     private static ClassLoader wrap(final ClassLoader parent) {
 448         return parent != null ? AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
 449             @Override
 450             public ClassLoader run() {
 451                 return new ClassLoader(parent) {
 452                     @Override
 453                     protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
 454                         ReflectUtil.checkPackageAccess(name);
 455                         return super.loadClass(name, resolve);
 456                     }
 457                 };
 458             }
 459         }) : null;
 460     }
 461 





















































 462     private static JMXConnector getConnectorAsService(ClassLoader loader,
 463                                                       JMXServiceURL url,
 464                                                       Map<String, ?> map)

 465         throws IOException {
 466 
 467         Iterator<JMXConnectorProvider> providers =
 468                 getProviderIterator(JMXConnectorProvider.class, loader);
 469         JMXConnector connection;
 470         IOException exception = null;
 471         while (providers.hasNext()) {
 472             JMXConnectorProvider provider = providers.next();












































































 473             try {
 474                 connection = provider.newJMXConnector(url, map);
 475                 return connection;
 476             } catch (JMXProviderException e) {
 477                 throw e;
 478             } catch (Exception e) {
 479                 if (logger.traceOn())
 480                     logger.trace("getConnectorAsService",
 481                                  "URL[" + url +
 482                                  "] Service provider exception: " + e);
 483                 if (!(e instanceof MalformedURLException)) {
 484                     if (exception == null) {
 485                         if (e instanceof IOException) {
 486                             exception = (IOException) e;
 487                         } else {
 488                             exception = EnvHelp.initCause(
 489                                 new IOException(e.getMessage()), e);
 490                         }
 491                     }
 492                 }
 493                 continue;
 494             }
 495         }
 496         if (exception == null)
 497             return null;
 498         else
 499             throw exception;




























































































 500     }
 501 
 502     static <T> T getProvider(String protocol,
 503                               String pkgs,
 504                               ClassLoader loader,
 505                               String providerClassName,
 506                               Class<T> targetInterface)
 507             throws IOException {
 508 
 509         StringTokenizer tokenizer = new StringTokenizer(pkgs, "|");
 510 
 511         while (tokenizer.hasMoreTokens()) {
 512             String pkg = tokenizer.nextToken();
 513             String className = (pkg + "." + protocol2package(protocol) +
 514                                 "." + providerClassName);
 515             Class<?> providerClass;
 516             try {
 517                 providerClass = Class.forName(className, true, loader);
 518             } catch (ClassNotFoundException e) {
 519                 //Add trace.




  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.io.UncheckedIOException;
  31 import java.lang.reflect.Module;
  32 import java.net.MalformedURLException;
  33 import java.util.Collections;
  34 import java.util.HashMap;
  35 import java.util.Map;

  36 import java.util.ServiceLoader;
  37 import java.util.ServiceLoader.Provider;
  38 import java.util.StringTokenizer;
  39 import java.util.function.Predicate;
  40 import java.util.stream.Stream;
  41 import java.security.AccessController;
  42 import java.security.PrivilegedAction;
  43 
  44 import com.sun.jmx.remote.util.ClassLogger;
  45 import com.sun.jmx.remote.util.EnvHelp;
  46 import sun.reflect.misc.ReflectUtil;
  47 
  48 
  49 /**
  50  * <p>Factory to create JMX API connector clients.  There
  51  * are no instances of this class.</p>
  52  *
  53  * <p>Connections are usually made using the {@link
  54  * #connect(JMXServiceURL) connect} method of this class.  More
  55  * advanced applications can separate the creation of the connector
  56  * client, using {@link #newJMXConnector(JMXServiceURL, Map)
  57  * newJMXConnector} and the establishment of the connection itself, using
  58  * {@link JMXConnector#connect(Map)}.</p>
  59  *
  60  * <p>Each client is created by an instance of {@link


 319             envcopy = newHashMap();
 320         else {
 321             EnvHelp.checkAttributes(environment);
 322             envcopy = newHashMap(environment);
 323         }
 324 
 325         final ClassLoader loader = resolveClassLoader(envcopy);
 326         final Class<JMXConnectorProvider> targetInterface =
 327                 JMXConnectorProvider.class;
 328         final String protocol = serviceURL.getProtocol();
 329         final String providerClassName = "ClientProvider";
 330         final JMXServiceURL providerURL = serviceURL;
 331 
 332         JMXConnectorProvider provider = getProvider(providerURL, envcopy,
 333                                                providerClassName,
 334                                                targetInterface,
 335                                                loader);
 336 
 337         IOException exception = null;
 338         if (provider == null) {
 339             Predicate<Provider<?>> systemProvider =
 340                     JMXConnectorFactory::isSystemProvider;
 341             // Loader is null when context class loader is set to null
 342             // and no loader has been provided in map.
 343             // com.sun.jmx.remote.util.Service class extracted from j2se
 344             // provider search algorithm doesn't handle well null classloader.
 345             JMXConnector connection = null;
 346             if (loader != null) {
 347                 try {
 348                     connection = getConnectorAsService(loader,
 349                                                        providerURL,
 350                                                        envcopy,
 351                                                        systemProvider.negate());
 352                     if (connection != null) return connection;
 353                 } catch (JMXProviderException e) {
 354                     throw e;
 355                 } catch (IOException e) {
 356                     exception = e;
 357                 }
 358             }
 359             connection = getConnectorAsService(
 360                              JMXConnectorFactory.class.getClassLoader(),
 361                              providerURL,
 362                              Collections.unmodifiableMap(envcopy),
 363                              systemProvider);
 364             if (connection != null) return connection;
 365         }
 366 
 367         if (provider == null) {
 368             MalformedURLException e =
 369                 new MalformedURLException("Unsupported protocol: " + protocol);
 370             if (exception == null) {
 371                 throw e;
 372             } else {
 373                 throw EnvHelp.initCause(e, exception);
 374             }
 375         }
 376 
 377         final Map<String,Object> fixedenv =
 378                 Collections.unmodifiableMap(envcopy);
 379 
 380         return provider.newJMXConnector(serviceURL, fixedenv);
 381     }
 382 
 383     private static String resolvePkgs(Map<String, ?> env)
 384             throws JMXProviderException {


 431         final String protocol = serviceURL.getProtocol();
 432 
 433         final String pkgs = resolvePkgs(environment);
 434 
 435         T instance = null;
 436 
 437         if (pkgs != null) {
 438             instance =
 439                 getProvider(protocol, pkgs, loader, providerClassName,
 440                             targetInterface);
 441 
 442             if (instance != null) {
 443                 boolean needsWrap = (loader != instance.getClass().getClassLoader());
 444                 environment.put(PROTOCOL_PROVIDER_CLASS_LOADER, needsWrap ? wrap(loader) : loader);
 445             }
 446         }
 447 
 448         return instance;
 449     }
 450 







 451     private static ClassLoader wrap(final ClassLoader parent) {
 452         return parent != null ? AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
 453             @Override
 454             public ClassLoader run() {
 455                 return new ClassLoader(parent) {
 456                     @Override
 457                     protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
 458                         ReflectUtil.checkPackageAccess(name);
 459                         return super.loadClass(name, resolve);
 460                     }
 461                 };
 462             }
 463         }) : null;
 464     }
 465 
 466     /**
 467      * Checks whether the given provider is our system provider for
 468      * the RMI connector.
 469      * If providers for additional protocols are added in the future
 470      * then the name of their modules may need to be added here.
 471      * System providers will be loaded only if no other provider is found.
 472      * @param provider the provider to test.
 473      * @return true if this provider is a default system provider.
 474      */
 475     static boolean isSystemProvider(Provider<?> provider) {
 476         Module providerModule = provider.type().getModule();
 477         return providerModule.isNamed()
 478            && providerModule.getName().equals("java.management.rmi");
 479     }
 480 
 481     /**
 482      * Creates a JMXConnector from the first JMXConnectorProvider service
 483      * supporting the given url that can be loaded from the given loader.
 484      * <p>
 485      * Parses the list of JMXConnectorProvider services that can be loaded
 486      * from the given loader, only retaining those that satisfy the given filter.
 487      * Then for each provider, attempts to create a new JMXConnector.
 488      * The first JMXConnector successfully created is returned.
 489      * <p>
 490      * The filter predicate is usually used to either exclude system providers
 491      * or only retain system providers (see isSystemProvider(...) above).
 492      *
 493      * @param loader The ClassLoader to use when looking up an implementation
 494      *        of the service. If null, then only installed services will be
 495      *        considered.
 496      *
 497      * @param url The JMXServiceURL of the connector for which a provider is
 498      *        requested.
 499      *
 500      * @param filter A filter used to exclude or return provider
 501      *        implementations. Typically the filter will either exclude
 502      *        system services (system default implementations) or only
 503      *        retain those.
 504      *        This can allow to first look for custom implementations (e.g.
 505      *        deployed on the CLASSPATH with META-INF/services) and
 506      *        then only default to system implementations.
 507      *
 508      * @throws IOException if no connector could not be instantiated, and
 509      *         at least one provider threw an exception that wasn't a
 510      *         {@code MalformedURLException} or a {@code JMProviderException}.
 511      *
 512      * @throws JMXProviderException if a provider for the protocol in
 513      *         <code>url</code> was found, but couldn't create the connector
 514      *         some reason.
 515      *
 516      * @return an instance of JMXConnector if a provider was found from
 517      *         which one could be instantiated, {@code null} otherwise.
 518      */
 519     private static JMXConnector getConnectorAsService(ClassLoader loader,
 520                                                       JMXServiceURL url,
 521                                                       Map<String, ?> map,
 522                                                       Predicate<Provider<?>> filter)
 523         throws IOException {
 524 
 525         final ConnectorFactory<JMXConnectorProvider, JMXConnector> factory =
 526                 (p) -> p.newJMXConnector(url, map);
 527         return getConnectorAsService(JMXConnectorProvider.class, loader, url,
 528                                      filter, factory);
 529     }
 530 
 531 
 532     /**
 533      * A factory function that can create a connector from a provider.
 534      * The pair (P,C) will be either one of:
 535      * a. (JMXConnectorProvider, JMXConnector) or
 536      * b. (JMXConnectorServerProvider, JMXConnectorServer)
 537      */
 538     @FunctionalInterface
 539     static interface ConnectorFactory<P,C> {
 540         public C apply(P provider) throws Exception;
 541     }
 542 
 543     /**
 544      * An instance of ProviderFinder is used to traverse a
 545      * {@code Stream<Provider<P>>} and find the first implementation of P
 546      * that supports creating a connector C from the given JMXServiceURL.
 547      * <p>
 548      * The pair (P,C) will be either one of: <br>
 549      * a. (JMXConnectorProvider, JMXConnector) or <br>
 550      * b. (JMXConnectorServerProvider, JMXConnectorServer)
 551      * <p>
 552      * The first connector successfully created while traversing the stream
 553      * is stored in the ProviderFinder instance. After that, the
 554      * ProviderFinder::test method, if called, will always return false, skipping
 555      * the remaining providers.
 556      * <p>
 557      * An instance of ProviderFinder is always expected to be used in conjunction
 558      * with Stream::findFirst, so that the stream traversal is stopped as soon
 559      * as a matching provider is found.
 560      * <p>
 561      * At the end of the stream traversal, the ProviderFinder::get method can be
 562      * used to obtain the connector instance (an instance of C) that was created.
 563      * If no connector could be created, and an exception was encountered while
 564      * traversing the stream and attempting to create the connector, then that
 565      * exception will be thrown by ProviderFinder::get, wrapped, if needed,
 566      * inside an IOException.
 567      * <p>
 568      * If any JMXProviderException is encountered while traversing the stream and
 569      * attempting to create the connector, that exception will be wrapped in an
 570      * UncheckedIOException and thrown immediately within the stream, thus
 571      * interrupting the traversal.
 572      * <p>
 573      * If no matching provider was found (no provider found or attempting
 574      * factory.apply always returned null or threw a MalformedURLException,
 575      * indicating the provider didn't support the protocol asked for by
 576      * the JMXServiceURL), then ProviderFinder::get will simply return null.
 577      */
 578     private static final class ProviderFinder<P,C> implements Predicate<Provider<P>> {
 579 
 580         final ConnectorFactory<P,C> factory;
 581         final JMXServiceURL  url;
 582         private IOException  exception = null;
 583         private C connection = null;
 584 
 585         ProviderFinder(ConnectorFactory<P,C> factory, JMXServiceURL url) {
 586             this.factory = factory;
 587             this.url = url;
 588         }
 589 
 590         /**
 591          * Returns {@code true} for the first provider {@code sp} that can
 592          * be used to obtain an instance of {@code C} from the given
 593          * {@code factory}.
 594          *
 595          * @param sp a candidate provider for instantiating {@code C}.
 596          *
 597          * @throws UncheckedIOException if {@code sp} throws a
 598          *         JMXProviderException. The JMXProviderException is set as the
 599          *         root cause.
 600          *
 601          * @return {@code true} for the first provider {@code sp} for which
 602          *         {@code C} could be instantiated, {@code false} otherwise.
 603          */
 604         public boolean test(Provider<P> sp) {
 605             if (connection == null) {
 606                 P provider = sp.get();
 607                 try {
 608                     connection = factory.apply(provider);
 609                     return connection != null;
 610                 } catch (JMXProviderException e) {
 611                     throw new UncheckedIOException(e);
 612                 } catch (Exception e) {
 613                     if (logger.traceOn())
 614                         logger.trace("getConnectorAsService",
 615                              "URL[" + url +
 616                              "] Service provider exception: " + e);
 617                     if (!(e instanceof MalformedURLException)) {
 618                         if (exception == null) {
 619                             if (e instanceof IOException) {
 620                                 exception = (IOException) e;
 621                             } else {
 622                                 exception = EnvHelp.initCause(
 623                                     new IOException(e.getMessage()), e);
 624                             }
 625                         }
 626                     }

 627                 }
 628             }
 629             return false;
 630         }
 631 
 632         /**
 633          * Returns an instance of {@code C} if a provider was found from
 634          * which {@code C} could be instantiated.
 635          *
 636          * @throws IOException if {@code C} could not be instantiated, and
 637          *         at least one provider threw an exception that wasn't a
 638          *         {@code MalformedURLException} or a {@code JMProviderException}.
 639          *
 640          * @return an instance of {@code C} if a provider was found from
 641          *         which {@code C} could be instantiated, {@code null} otherwise.
 642          */
 643         C get() throws IOException {
 644             if (connection != null) return connection;
 645             else if (exception != null) throw exception;
 646             else return null;
 647         }
 648     }
 649 
 650     /**
 651      * Creates a connector from a provider loaded from the ServiceLoader.
 652      * <p>
 653      * The pair (P,C) will be either one of: <br>
 654      * a. (JMXConnectorProvider, JMXConnector) or <br>
 655      * b. (JMXConnectorServerProvider, JMXConnectorServer)
 656      *
 657      * @param providerClass The service type for which an implementation
 658      *        should be looked up from the {@code ServiceLoader}. This will
 659      *        be either {@code JMXConnectorProvider.class} or
 660      *        {@code JMXConnectorServerProvider.class}
 661      *
 662      * @param loader The ClassLoader to use when looking up an implementation
 663      *        of the service. If null, then only installed services will be
 664      *        considered.
 665      *
 666      * @param url The JMXServiceURL of the connector for which a provider is
 667      *        requested.
 668      *
 669      * @param filter A filter used to exclude or return provider
 670      *        implementations. Typically the filter will either exclude
 671      *        system services (system default implementations) or only
 672      *        retain those.
 673      *        This can allow to first look for custom implementations (e.g.
 674      *        deployed on the CLASSPATH with META-INF/services) and
 675      *        then only default to system implementations.
 676      *
 677      * @param factory A functional factory that can attempt to create an
 678      *        instance of connector {@code C} from a provider {@code P}.
 679      *        Typically, this is a simple wrapper over {@code
 680      *        JMXConnectorProvider::newJMXConnector} or {@code
 681      *        JMXConnectorProviderServer::newJMXConnectorServer}.
 682      *
 683      * @throws IOException if {@code C} could not be instantiated, and
 684      *         at least one provider {@code P} threw an exception that wasn't a
 685      *         {@code MalformedURLException} or a {@code JMProviderException}.
 686      *
 687      * @throws JMXProviderException if a provider {@code P} for the protocol in
 688      *         <code>url</code> was found, but couldn't create the connector
 689      *         {@code C} for some reason.
 690      *
 691      * @return an instance of {@code C} if a provider {@code P} was found from
 692      *         which one could be instantiated, {@code null} otherwise.
 693      */
 694     static <P,C> C getConnectorAsService(Class<P> providerClass,
 695                                          ClassLoader loader,
 696                                          JMXServiceURL url,
 697                                          Predicate<Provider<?>> filter,
 698                                          ConnectorFactory<P,C> factory)
 699         throws IOException {
 700 
 701         // sanity check
 702         if (JMXConnectorProvider.class != providerClass
 703             && JMXConnectorServerProvider.class != providerClass) {
 704             // should never happen
 705             throw new InternalError("Unsupported service interface: "
 706                                     + providerClass.getName());
 707         }
 708 
 709         ServiceLoader<P> serviceLoader = loader == null
 710                 ? ServiceLoader.loadInstalled(providerClass)
 711                 : ServiceLoader.load(providerClass, loader);
 712         Stream<Provider<P>> stream = serviceLoader.stream().filter(filter);
 713         ProviderFinder<P,C> finder = new ProviderFinder<>(factory, url);
 714 
 715         try {
 716             stream.filter(finder).findFirst();
 717             return finder.get();
 718         } catch (UncheckedIOException e) {
 719             if (e.getCause() instanceof JMXProviderException) {
 720                 throw (JMXProviderException) e.getCause();
 721             } else {
 722                 throw e;
 723             }
 724         }
 725     }
 726 
 727     static <T> T getProvider(String protocol,
 728                               String pkgs,
 729                               ClassLoader loader,
 730                               String providerClassName,
 731                               Class<T> targetInterface)
 732             throws IOException {
 733 
 734         StringTokenizer tokenizer = new StringTokenizer(pkgs, "|");
 735 
 736         while (tokenizer.hasMoreTokens()) {
 737             String pkg = tokenizer.nextToken();
 738             String className = (pkg + "." + protocol2package(protocol) +
 739                                 "." + providerClassName);
 740             Class<?> providerClass;
 741             try {
 742                 providerClass = Class.forName(className, true, loader);
 743             } catch (ClassNotFoundException e) {
 744                 //Add trace.


< prev index next >