1/* 2 * Copyright (c) 1999, 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 26package javax.management; 27 28import com.sun.jmx.defaults.JmxProperties; 29import static com.sun.jmx.defaults.JmxProperties.JMX_INITIAL_BUILDER; 30import static com.sun.jmx.defaults.JmxProperties.MBEANSERVER_LOGGER; 31import com.sun.jmx.mbeanserver.GetPropertyAction; 32import java.security.AccessController; 33import java.security.Permission; 34import java.util.ArrayList; 35import java.lang.System.Logger.Level; 36import javax.management.loading.ClassLoaderRepository; 37import 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 */ 87public 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.log(Level.TRACE, 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.log(Level.TRACE, 425 "MBeanServer was not in list!"); 426 throw new IllegalArgumentException("MBeanServer was not in list!"); 427 } 428 } 429 430 private static final ArrayList<MBeanServer> mBeanServerList = 431 new ArrayList<MBeanServer>(); 432 433 /** 434 * Load the builder class through the context class loader. 435 * @param builderClassName The name of the builder class. 436 **/ 437 private static Class<?> loadBuilderClass(String builderClassName) 438 throws ClassNotFoundException { 439 final ClassLoader loader = 440 Thread.currentThread().getContextClassLoader(); 441 442 if (loader != null) { 443 // Try with context class loader 444 return loader.loadClass(builderClassName); 445 } 446 447 // No context class loader? Try with Class.forName() 448 return ReflectUtil.forName(builderClassName); 449 } 450 451 /** 452 * Creates the initial builder according to the 453 * javax.management.builder.initial System property - if specified. 454 * If any checked exception needs to be thrown, it is embedded in 455 * a JMRuntimeException. 456 **/ 457 private static MBeanServerBuilder newBuilder(Class<?> builderClass) { 458 try { 459 @SuppressWarnings("deprecation") 460 final Object abuilder = builderClass.newInstance(); 461 return (MBeanServerBuilder)abuilder; 462 } catch (RuntimeException x) { 463 throw x; 464 } catch (Exception x) { 465 final String msg = 466 "Failed to instantiate a MBeanServerBuilder from " + 467 builderClass + ": " + x; 468 throw new JMRuntimeException(msg, x); 469 } 470 } 471 472 /** 473 * Instantiate a new builder according to the 474 * javax.management.builder.initial System property - if needed. 475 **/ 476 private static synchronized void checkMBeanServerBuilder() { 477 try { 478 GetPropertyAction act = 479 new GetPropertyAction(JMX_INITIAL_BUILDER); 480 String builderClassName = AccessController.doPrivileged(act); 481 482 try { 483 final Class<?> newBuilderClass; 484 if (builderClassName == null || builderClassName.length() == 0) 485 newBuilderClass = MBeanServerBuilder.class; 486 else 487 newBuilderClass = loadBuilderClass(builderClassName); 488 489 // Check whether a new builder needs to be created 490 if (builder != null) { 491 final Class<?> builderClass = builder.getClass(); 492 if (newBuilderClass == builderClass) 493 return; // no need to create a new builder... 494 } 495 496 // Create a new builder 497 builder = newBuilder(newBuilderClass); 498 } catch (ClassNotFoundException x) { 499 final String msg = 500 "Failed to load MBeanServerBuilder class " + 501 builderClassName + ": " + x; 502 throw new JMRuntimeException(msg, x); 503 } 504 } catch (RuntimeException x) { 505 if (MBEANSERVER_LOGGER.isLoggable(Level.DEBUG)) { 506 StringBuilder strb = new StringBuilder() 507 .append("Failed to instantiate MBeanServerBuilder: ").append(x) 508 .append("\n\t\tCheck the value of the ") 509 .append(JMX_INITIAL_BUILDER).append(" property."); 510 MBEANSERVER_LOGGER.log(Level.DEBUG, strb::toString); 511 } 512 throw x; 513 } 514 } 515 516 /** 517 * Get the current {@link javax.management.MBeanServerBuilder}, 518 * as specified by the current value of the 519 * javax.management.builder.initial property. 520 * 521 * This method consults the property and instantiates a new builder 522 * if needed. 523 * 524 * @return the new current {@link javax.management.MBeanServerBuilder}. 525 * 526 * @exception SecurityException if there is a SecurityManager and 527 * the caller's permissions do not make it possible to instantiate 528 * a new builder. 529 * @exception JMRuntimeException if the builder instantiation 530 * fails with a checked exception - 531 * {@link java.lang.ClassNotFoundException} etc... 532 * 533 **/ 534 private static synchronized MBeanServerBuilder getNewMBeanServerBuilder() { 535 checkMBeanServerBuilder(); 536 return builder; 537 } 538 539} 540