1 /*
   2  * Copyright (c) 1999, 2013, 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;
  27 
  28 import com.sun.jmx.defaults.JmxProperties;
  29 import static com.sun.jmx.defaults.JmxProperties.JMX_INITIAL_BUILDER;
  30 import static com.sun.jmx.defaults.JmxProperties.MBEANSERVER_LOGGER;
  31 import com.sun.jmx.mbeanserver.GetPropertyAction;
  32 import java.security.AccessController;
  33 import java.security.Permission;
  34 import java.util.ArrayList;
  35 import java.util.logging.Level;
  36 import javax.management.loading.ClassLoaderRepository;
  37 import sun.reflect.misc.ReflectUtil;
  38 
  39 
  40 /**
  41  * <p>Provides MBean server references.  There are no instances of
  42  * this class.</p>
  43  *
  44  * <p>Since JMX 1.2 this class makes it possible to replace the default
  45  * MBeanServer implementation. This is done using the
  46  * {@link javax.management.MBeanServerBuilder} class.
  47  * The class of the initial MBeanServerBuilder to be
  48  * instantiated can be specified through the
  49  * <b>javax.management.builder.initial</b> system property.
  50  * The specified class must be a public subclass of
  51  * {@link javax.management.MBeanServerBuilder}, and must have a public
  52  * empty constructor.
  53  * <p>By default, if no value for that property is specified, an instance of
  54  * {@link
  55  * javax.management.MBeanServerBuilder javax.management.MBeanServerBuilder}
  56  * is created. Otherwise, the MBeanServerFactory attempts to load the
  57  * specified class using
  58  * {@link java.lang.Thread#getContextClassLoader()
  59  *   Thread.currentThread().getContextClassLoader()}, or if that is null,
  60  * {@link java.lang.Class#forName(java.lang.String) Class.forName()}. Then
  61  * it creates an initial instance of that Class using
  62  * {@link java.lang.Class#newInstance()}. If any checked exception
  63  * is raised during this process (e.g.
  64  * {@link java.lang.ClassNotFoundException},
  65  * {@link java.lang.InstantiationException}) the MBeanServerFactory
  66  * will propagate this exception from within a RuntimeException.</p>
  67  *
  68  * <p>The <b>javax.management.builder.initial</b> system property is
  69  * consulted every time a new MBeanServer needs to be created, and the
  70  * class pointed to by that property is loaded. If that class is different
  71  * from that of the current MBeanServerBuilder, then a new MBeanServerBuilder
  72  * is created. Otherwise, the MBeanServerFactory may create a new
  73  * MBeanServerBuilder or reuse the current one.</p>
  74  *
  75  * <p>If the class pointed to by the property cannot be
  76  * loaded, or does not correspond to a valid subclass of MBeanServerBuilder
  77  * then an exception is propagated, and no MBeanServer can be created until
  78  * the <b>javax.management.builder.initial</b> system property is reset to
  79  * valid value.</p>
  80  *
  81  * <p>The MBeanServerBuilder makes it possible to wrap the MBeanServers
  82  * returned by the default MBeanServerBuilder implementation, for the purpose
  83  * of e.g. adding an additional security layer.</p>
  84  *
  85  * @since 1.5
  86  */
  87 public class MBeanServerFactory {
  88 
  89     /*
  90      * There are no instances of this class so don't generate the
  91      * default public constructor.
  92      */
  93     private MBeanServerFactory() {
  94 
  95     }
  96 
  97     /**
  98      * The builder that will be used to construct MBeanServers.
  99      *
 100      **/
 101     private static MBeanServerBuilder builder = null;
 102 
 103     /**
 104      * Provide a new {@link javax.management.MBeanServerBuilder}.
 105      * @param builder The new MBeanServerBuilder that will be used to
 106      *        create {@link javax.management.MBeanServer}s.
 107      * @exception IllegalArgumentException if the given builder is null.
 108      *
 109      * @exception SecurityException if there is a SecurityManager and
 110      * the caller's permissions do not include or imply <code>{@link
 111      * MBeanServerPermission}("setMBeanServerBuilder")</code>.
 112      *
 113      **/
 114     // public static synchronized void
 115     //    setMBeanServerBuilder(MBeanServerBuilder builder) {
 116     //    checkPermission("setMBeanServerBuilder");
 117     //    MBeanServerFactory.builder = builder;
 118     // }
 119 
 120     /**
 121      * Get the current {@link javax.management.MBeanServerBuilder}.
 122      *
 123      * @return the current {@link javax.management.MBeanServerBuilder}.
 124      *
 125      * @exception SecurityException if there is a SecurityManager and
 126      * the caller's permissions do not include or imply <code>{@link
 127      * MBeanServerPermission}("getMBeanServerBuilder")</code>.
 128      *
 129      **/
 130     // public static synchronized MBeanServerBuilder getMBeanServerBuilder() {
 131     //     checkPermission("getMBeanServerBuilder");
 132     //     return builder;
 133     // }
 134 
 135     /**
 136      * Remove internal MBeanServerFactory references to a created
 137      * MBeanServer. This allows the garbage collector to remove the
 138      * MBeanServer object.
 139      *
 140      * @param mbeanServer the MBeanServer object to remove.
 141      *
 142      * @exception java.lang.IllegalArgumentException if
 143      * <code>mbeanServer</code> was not generated by one of the
 144      * <code>createMBeanServer</code> methods, or if
 145      * <code>releaseMBeanServer</code> was already called on it.
 146      *
 147      * @exception SecurityException if there is a SecurityManager and
 148      * the caller's permissions do not include or imply <code>{@link
 149      * MBeanServerPermission}("releaseMBeanServer")</code>.
 150      */
 151     public static void releaseMBeanServer(MBeanServer mbeanServer) {
 152         checkPermission("releaseMBeanServer");
 153 
 154         removeMBeanServer(mbeanServer);
 155     }
 156 
 157     /**
 158      * <p>Return a new object implementing the MBeanServer interface
 159      * with a standard default domain name.  The default domain name
 160      * is used as the domain part in the ObjectName of MBeans when the
 161      * domain is specified by the user is null.</p>
 162      *
 163      * <p>The standard default domain name is
 164      * <code>DefaultDomain</code>.</p>
 165      *
 166      * <p>The MBeanServer reference is internally kept. This will
 167      * allow <CODE>findMBeanServer</CODE> to return a reference to
 168      * this MBeanServer object.</p>
 169      *
 170      * <p>This method is equivalent to <code>createMBeanServer(null)</code>.
 171      *
 172      * @return the newly created MBeanServer.
 173      *
 174      * @exception SecurityException if there is a SecurityManager and the
 175      * caller's permissions do not include or imply <code>{@link
 176      * MBeanServerPermission}("createMBeanServer")</code>.
 177      *
 178      * @exception JMRuntimeException if the property
 179      * <code>javax.management.builder.initial</code> exists but the
 180      * class it names cannot be instantiated through a public
 181      * no-argument constructor; or if the instantiated builder returns
 182      * null from its {@link MBeanServerBuilder#newMBeanServerDelegate
 183      * newMBeanServerDelegate} or {@link
 184      * MBeanServerBuilder#newMBeanServer newMBeanServer} methods.
 185      *
 186      * @exception ClassCastException if the property
 187      * <code>javax.management.builder.initial</code> exists and can be
 188      * instantiated but is not assignment compatible with {@link
 189      * MBeanServerBuilder}.
 190      */
 191     public static MBeanServer createMBeanServer() {
 192         return createMBeanServer(null);
 193     }
 194 
 195     /**
 196      * <p>Return a new object implementing the {@link MBeanServer}
 197      * interface with the specified default domain name.  The given
 198      * domain name is used as the domain part in the ObjectName of
 199      * MBeans when the domain is specified by the user is null.</p>
 200      *
 201      * <p>The MBeanServer reference is internally kept. This will
 202      * allow <CODE>findMBeanServer</CODE> to return a reference to
 203      * this MBeanServer object.</p>
 204      *
 205      * @param domain the default domain name for the created
 206      * MBeanServer.  This is the value that will be returned by {@link
 207      * MBeanServer#getDefaultDomain}.
 208      *
 209      * @return the newly created MBeanServer.
 210      *
 211      * @exception SecurityException if there is a SecurityManager and
 212      * the caller's permissions do not include or imply <code>{@link
 213      * MBeanServerPermission}("createMBeanServer")</code>.
 214      *
 215      * @exception JMRuntimeException if the property
 216      * <code>javax.management.builder.initial</code> exists but the
 217      * class it names cannot be instantiated through a public
 218      * no-argument constructor; or if the instantiated builder returns
 219      * null from its {@link MBeanServerBuilder#newMBeanServerDelegate
 220      * newMBeanServerDelegate} or {@link
 221      * MBeanServerBuilder#newMBeanServer newMBeanServer} methods.
 222      *
 223      * @exception ClassCastException if the property
 224      * <code>javax.management.builder.initial</code> exists and can be
 225      * instantiated but is not assignment compatible with {@link
 226      * MBeanServerBuilder}.
 227      */
 228     public static MBeanServer createMBeanServer(String domain)  {
 229         checkPermission("createMBeanServer");
 230 
 231         final MBeanServer mBeanServer = newMBeanServer(domain);
 232         addMBeanServer(mBeanServer);
 233         return mBeanServer;
 234     }
 235 
 236     /**
 237      * <p>Return a new object implementing the MBeanServer interface
 238      * with a standard default domain name, without keeping an
 239      * internal reference to this new object.  The default domain name
 240      * is used as the domain part in the ObjectName of MBeans when the
 241      * domain is specified by the user is null.</p>
 242      *
 243      * <p>The standard default domain name is
 244      * <code>DefaultDomain</code>.</p>
 245      *
 246      * <p>No reference is kept. <CODE>findMBeanServer</CODE> will not
 247      * be able to return a reference to this MBeanServer object, but
 248      * the garbage collector will be able to remove the MBeanServer
 249      * object when it is no longer referenced.</p>
 250      *
 251      * <p>This method is equivalent to <code>newMBeanServer(null)</code>.</p>
 252      *
 253      * @return the newly created MBeanServer.
 254      *
 255      * @exception SecurityException if there is a SecurityManager and the
 256      * caller's permissions do not include or imply <code>{@link
 257      * MBeanServerPermission}("newMBeanServer")</code>.
 258      *
 259      * @exception JMRuntimeException if the property
 260      * <code>javax.management.builder.initial</code> exists but the
 261      * class it names cannot be instantiated through a public
 262      * no-argument constructor; or if the instantiated builder returns
 263      * null from its {@link MBeanServerBuilder#newMBeanServerDelegate
 264      * newMBeanServerDelegate} or {@link
 265      * MBeanServerBuilder#newMBeanServer newMBeanServer} methods.
 266      *
 267      * @exception ClassCastException if the property
 268      * <code>javax.management.builder.initial</code> exists and can be
 269      * instantiated but is not assignment compatible with {@link
 270      * MBeanServerBuilder}.
 271      */
 272     public static MBeanServer newMBeanServer() {
 273         return newMBeanServer(null);
 274     }
 275 
 276     /**
 277      * <p>Return a new object implementing the MBeanServer interface
 278      * with the specified default domain name, without keeping an
 279      * internal reference to this new object.  The given domain name
 280      * is used as the domain part in the ObjectName of MBeans when the
 281      * domain is specified by the user is null.</p>
 282      *
 283      * <p>No reference is kept. <CODE>findMBeanServer</CODE> will not
 284      * be able to return a reference to this MBeanServer object, but
 285      * the garbage collector will be able to remove the MBeanServer
 286      * object when it is no longer referenced.</p>
 287      *
 288      * @param domain the default domain name for the created
 289      * MBeanServer.  This is the value that will be returned by {@link
 290      * MBeanServer#getDefaultDomain}.
 291      *
 292      * @return the newly created MBeanServer.
 293      *
 294      * @exception SecurityException if there is a SecurityManager and the
 295      * caller's permissions do not include or imply <code>{@link
 296      * MBeanServerPermission}("newMBeanServer")</code>.
 297      *
 298      * @exception JMRuntimeException if the property
 299      * <code>javax.management.builder.initial</code> exists but the
 300      * class it names cannot be instantiated through a public
 301      * no-argument constructor; or if the instantiated builder returns
 302      * null from its {@link MBeanServerBuilder#newMBeanServerDelegate
 303      * newMBeanServerDelegate} or {@link
 304      * MBeanServerBuilder#newMBeanServer newMBeanServer} methods.
 305      *
 306      * @exception ClassCastException if the property
 307      * <code>javax.management.builder.initial</code> exists and can be
 308      * instantiated but is not assignment compatible with {@link
 309      * MBeanServerBuilder}.
 310      */
 311     public static MBeanServer newMBeanServer(String domain)  {
 312         checkPermission("newMBeanServer");
 313 
 314         // Get the builder. Creates a new one if necessary.
 315         //
 316         final MBeanServerBuilder mbsBuilder = getNewMBeanServerBuilder();
 317         // Returned value cannot be null.  NullPointerException if violated.
 318 
 319         synchronized(mbsBuilder) {
 320             final MBeanServerDelegate delegate  =
 321                     mbsBuilder.newMBeanServerDelegate();
 322             if (delegate == null) {
 323                 final String msg =
 324                         "MBeanServerBuilder.newMBeanServerDelegate() " +
 325                         "returned null";
 326                 throw new JMRuntimeException(msg);
 327             }
 328             final MBeanServer mbeanServer =
 329                     mbsBuilder.newMBeanServer(domain,null,delegate);
 330             if (mbeanServer == null) {
 331                 final String msg =
 332                         "MBeanServerBuilder.newMBeanServer() returned null";
 333                 throw new JMRuntimeException(msg);
 334             }
 335             return mbeanServer;
 336         }
 337     }
 338 
 339     /**
 340      * <p>Return a list of registered MBeanServer objects.  A
 341      * registered MBeanServer object is one that was created by one of
 342      * the <code>createMBeanServer</code> methods and not subsequently
 343      * released with <code>releaseMBeanServer</code>.</p>
 344      *
 345      * @param agentId The agent identifier of the MBeanServer to
 346      * retrieve.  If this parameter is null, all registered
 347      * MBeanServers in this JVM are returned.  Otherwise, only
 348      * MBeanServers whose id is equal to <code>agentId</code> are
 349      * returned.  The id of an MBeanServer is the
 350      * <code>MBeanServerId</code> attribute of its delegate MBean.
 351      *
 352      * @return A list of MBeanServer objects.
 353      *
 354      * @exception SecurityException if there is a SecurityManager and the
 355      * caller's permissions do not include or imply <code>{@link
 356      * MBeanServerPermission}("findMBeanServer")</code>.
 357      */
 358     public synchronized static
 359             ArrayList<MBeanServer> findMBeanServer(String agentId) {
 360 
 361         checkPermission("findMBeanServer");
 362 
 363         if (agentId == null)
 364             return new ArrayList<MBeanServer>(mBeanServerList);
 365 
 366         ArrayList<MBeanServer> result = new ArrayList<MBeanServer>();
 367         for (MBeanServer mbs : mBeanServerList) {
 368             String name = mBeanServerId(mbs);
 369             if (agentId.equals(name))
 370                 result.add(mbs);
 371         }
 372         return result;
 373     }
 374 
 375     /**
 376      * Return the ClassLoaderRepository used by the given MBeanServer.
 377      * This method is equivalent to {@link
 378      * MBeanServer#getClassLoaderRepository() server.getClassLoaderRepository()}.
 379      * @param server The MBeanServer under examination. Since JMX 1.2,
 380      * if <code>server</code> is <code>null</code>, the result is a
 381      * {@link NullPointerException}.  This behavior differs from what
 382      * was implemented in JMX 1.1 - where the possibility to use
 383      * <code>null</code> was deprecated.
 384      * @return The Class Loader Repository used by the given MBeanServer.
 385      * @exception SecurityException if there is a SecurityManager and
 386      * the caller's permissions do not include or imply <code>{@link
 387      * MBeanPermission}("getClassLoaderRepository")</code>.
 388      *
 389      * @exception NullPointerException if <code>server</code> is null.
 390      *
 391      **/
 392     public static ClassLoaderRepository getClassLoaderRepository(
 393             MBeanServer server) {
 394         return server.getClassLoaderRepository();
 395     }
 396 
 397     private static String mBeanServerId(MBeanServer mbs) {
 398         try {
 399             return (String) mbs.getAttribute(MBeanServerDelegate.DELEGATE_NAME,
 400                     "MBeanServerId");
 401         } catch (JMException e) {
 402             JmxProperties.MISC_LOGGER.finest(
 403                     "Ignoring exception while getting MBeanServerId: "+e);
 404             return null;
 405         }
 406     }
 407 
 408     private static void checkPermission(String action)
 409     throws SecurityException {
 410         SecurityManager sm = System.getSecurityManager();
 411         if (sm != null) {
 412             Permission perm = new MBeanServerPermission(action);
 413             sm.checkPermission(perm);
 414         }
 415     }
 416 
 417     private static synchronized void addMBeanServer(MBeanServer mbs) {
 418         mBeanServerList.add(mbs);
 419     }
 420 
 421     private static synchronized void removeMBeanServer(MBeanServer mbs) {
 422         boolean removed = mBeanServerList.remove(mbs);
 423         if (!removed) {
 424             MBEANSERVER_LOGGER.logp(Level.FINER,
 425                     MBeanServerFactory.class.getName(),
 426                     "removeMBeanServer(MBeanServer)",
 427                     "MBeanServer was not in list!");
 428             throw new IllegalArgumentException("MBeanServer was not in list!");
 429         }
 430     }
 431 
 432     private static final ArrayList<MBeanServer> mBeanServerList =
 433             new ArrayList<MBeanServer>();
 434 
 435     /**
 436      * Load the builder class through the context class loader.
 437      * @param builderClassName The name of the builder class.
 438      **/
 439     private static Class<?> loadBuilderClass(String builderClassName)
 440     throws ClassNotFoundException {
 441         final ClassLoader loader =
 442                 Thread.currentThread().getContextClassLoader();
 443 
 444         if (loader != null) {
 445             // Try with context class loader
 446             return loader.loadClass(builderClassName);
 447         }
 448 
 449         // No context class loader? Try with Class.forName()
 450         return ReflectUtil.forName(builderClassName);
 451     }
 452 
 453     /**
 454      * Creates the initial builder according to the
 455      * javax.management.builder.initial System property - if specified.
 456      * If any checked exception needs to be thrown, it is embedded in
 457      * a JMRuntimeException.
 458      **/
 459     private static MBeanServerBuilder newBuilder(Class<?> builderClass) {
 460         try {
 461             final Object abuilder = builderClass.newInstance();
 462             return (MBeanServerBuilder)abuilder;
 463         } catch (RuntimeException x) {
 464             throw x;
 465         } catch (Exception x) {
 466             final String msg =
 467                     "Failed to instantiate a MBeanServerBuilder from " +
 468                     builderClass + ": " + x;
 469             throw new JMRuntimeException(msg, x);
 470         }
 471     }
 472 
 473     /**
 474      * Instantiate a new builder according to the
 475      * javax.management.builder.initial System property - if needed.
 476      **/
 477     private static synchronized void checkMBeanServerBuilder() {
 478         try {
 479             GetPropertyAction act =
 480                     new GetPropertyAction(JMX_INITIAL_BUILDER);
 481             String builderClassName = AccessController.doPrivileged(act);
 482 
 483             try {
 484                 final Class<?> newBuilderClass;
 485                 if (builderClassName == null || builderClassName.length() == 0)
 486                     newBuilderClass = MBeanServerBuilder.class;
 487                 else
 488                     newBuilderClass = loadBuilderClass(builderClassName);
 489 
 490                 // Check whether a new builder needs to be created
 491                 if (builder != null) {
 492                     final Class<?> builderClass = builder.getClass();
 493                     if (newBuilderClass == builderClass)
 494                         return; // no need to create a new builder...
 495                 }
 496 
 497                 // Create a new builder
 498                 builder = newBuilder(newBuilderClass);
 499             } catch (ClassNotFoundException x) {
 500                 final String msg =
 501                         "Failed to load MBeanServerBuilder class " +
 502                         builderClassName + ": " + x;
 503                 throw new JMRuntimeException(msg, x);
 504             }
 505         } catch (RuntimeException x) {
 506             if (MBEANSERVER_LOGGER.isLoggable(Level.FINEST)) {
 507                 StringBuilder strb = new StringBuilder()
 508                 .append("Failed to instantiate MBeanServerBuilder: ").append(x)
 509                 .append("\n\t\tCheck the value of the ")
 510                 .append(JMX_INITIAL_BUILDER).append(" property.");
 511                 MBEANSERVER_LOGGER.logp(Level.FINEST,
 512                         MBeanServerFactory.class.getName(),
 513                         "checkMBeanServerBuilder",
 514                         strb.toString());
 515             }
 516             throw x;
 517         }
 518     }
 519 
 520     /**
 521      * Get the current {@link javax.management.MBeanServerBuilder},
 522      * as specified by the current value of the
 523      * javax.management.builder.initial property.
 524      *
 525      * This method consults the property and instantiates a new builder
 526      * if needed.
 527      *
 528      * @return the new current {@link javax.management.MBeanServerBuilder}.
 529      *
 530      * @exception SecurityException if there is a SecurityManager and
 531      * the caller's permissions do not make it possible to instantiate
 532      * a new builder.
 533      * @exception JMRuntimeException if the builder instantiation
 534      *   fails with a checked exception -
 535      *   {@link java.lang.ClassNotFoundException} etc...
 536      *
 537      **/
 538     private static synchronized MBeanServerBuilder getNewMBeanServerBuilder() {
 539         checkMBeanServerBuilder();
 540         return builder;
 541     }
 542 
 543 }