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