/* * @(#)MBeanServerFactory.java 1.55 04/02/23 * * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. */ package javax.management; // java import import java.security.AccessController; import java.security.Permission; import java.security.PrivilegedAction; import java.util.ArrayList; import java.util.Iterator; // RI import import javax.management.loading.ClassLoaderRepository; import com.sun.jmx.defaults.ServiceName; import com.sun.jmx.defaults.JmxProperties; import com.sun.jmx.mbeanserver.GetPropertyAction; import com.sun.jmx.trace.Trace; /** *

Provides MBean server references. There are no instances of * this class.

* *

Since JMX 1.2 this class makes it possible to replace the default * MBeanServer implementation. This is done using the * {@link javax.management.MBeanServerBuilder} class. * The class of the initial MBeanServerBuilder to be * instantiated can be specified through the * javax.management.builder.initial system property. * The specified class must be a public subclass of * {@link javax.management.MBeanServerBuilder}, and must have a public * empty constructor. *

By default, if no value for that property is specified, an instance of * {@link * javax.management.MBeanServerBuilder javax.management.MBeanServerBuilder} * is created. Otherwise, the MBeanServerFactory attempts to load the * specified class using * {@link java.lang.Thread#getContextClassLoader() * Thread.currentThread().getContextClassLoader()}, or if that is null, * {@link java.lang.Class#forName(java.lang.String) Class.forName()}. Then * it creates an initial instance of that Class using * {@link java.lang.Class#newInstance()}. If any checked exception * is raised during this process (e.g. * {@link java.lang.ClassNotFoundException}, * {@link java.lang.InstantiationException}) the MBeanServerFactory * will propagate this exception from within a RuntimeException.

* *

The javax.management.builder.initial system property is * consulted every time a new MBeanServer needs to be created, and the * class pointed to by that property is loaded. If that class is different * from that of the current MBeanServerBuilder, then a new MBeanServerBuilder * is created. Otherwise, the MBeanServerFactory may create a new * MBeanServerBuilder or reuse the current one.

* *

If the class pointed to by the property cannot be * loaded, or does not correspond to a valid subclass of MBeanServerBuilder * then an exception is propagated, and no MBeanServer can be created until * the javax.management.builder.initial system property is reset to * valid value.

* *

The MBeanServerBuilder makes it possible to wrap the MBeanServers * returned by the default MBeanServerBuilder implementation, for the purpose * of e.g. adding an additional security layer.

* * @since 1.5 */ public class MBeanServerFactory { /* * There are no instances of this class so don't generate the * default public constructor. */ private MBeanServerFactory() { } /** * The builder that will be used to construct MBeanServers. * * @since.unbundled JMX 1.2 **/ private static MBeanServerBuilder builder = null; /** * Provide a new {@link javax.management.MBeanServerBuilder}. * @param builder The new MBeanServerBuilder that will be used to * create {@link javax.management.MBeanServer}s. * @exception IllegalArgumentException if the given builder is null. * * @exception SecurityException if there is a SecurityManager and * the caller's permissions do not include or imply {@link * MBeanServerPermission}("setMBeanServerBuilder"). * * @since.unbundled JMX 1.2 **/ // public static synchronized void // setMBeanServerBuilder(MBeanServerBuilder builder) { // checkPermission("setMBeanServerBuilder"); // MBeanServerFactory.builder = builder; // } /** * Get the current {@link javax.management.MBeanServerBuilder}. * * @return the current {@link javax.management.MBeanServerBuilder}. * * @exception SecurityException if there is a SecurityManager and * the caller's permissions do not include or imply {@link * MBeanServerPermission}("getMBeanServerBuilder"). * * @since.unbundled JMX 1.2 **/ // public static synchronized MBeanServerBuilder getMBeanServerBuilder() { // checkPermission("getMBeanServerBuilder"); // return builder; // } /** * Remove internal MBeanServerFactory references to a created * MBeanServer. This allows the garbage collector to remove the * MBeanServer object. * * @param mbeanServer the MBeanServer object to remove. * * @exception java.lang.IllegalArgumentException if * mbeanServer was not generated by one of the * createMBeanServer methods, or if * releaseMBeanServer was already called on it. * * @exception SecurityException if there is a SecurityManager and * the caller's permissions do not include or imply {@link * MBeanServerPermission}("releaseMBeanServer"). */ public static void releaseMBeanServer(MBeanServer mbeanServer) { checkPermission("releaseMBeanServer"); removeMBeanServer(mbeanServer); } /** *

Return a new object implementing the MBeanServer interface * with a standard default domain name. The default domain name * is used as the domain part in the ObjectName of MBeans when the * domain is specified by the user is null.

* *

The standard default domain name is * DefaultDomain.

* *

The MBeanServer reference is internally kept. This will * allow findMBeanServer to return a reference to * this MBeanServer object.

* *

This method is equivalent to createMBeanServer(null). * * @return the newly created MBeanServer. * * @exception SecurityException if there is a SecurityManager and the * caller's permissions do not include or imply {@link * MBeanServerPermission}("createMBeanServer"). * * @exception JMRuntimeException if the property * javax.management.builder.initial exists but the * class it names cannot be instantiated through a public * no-argument constructor; or if the instantiated builder returns * null from its {@link MBeanServerBuilder#newMBeanServerDelegate * newMBeanServerDelegate} or {@link * MBeanServerBuilder#newMBeanServer newMBeanServer} methods. * * @exception ClassCastException if the property * javax.management.builder.initial exists and can be * instantiated but is not assignment compatible with {@link * MBeanServerBuilder}. */ public static MBeanServer createMBeanServer() { return createMBeanServer(null); } /** *

Return a new object implementing the {@link MBeanServer} * interface with the specified default domain name. The given * domain name is used as the domain part in the ObjectName of * MBeans when the domain is specified by the user is null.

* *

The MBeanServer reference is internally kept. This will * allow findMBeanServer to return a reference to * this MBeanServer object.

* * @param domain the default domain name for the created * MBeanServer. This is the value that will be returned by {@link * MBeanServer#getDefaultDomain}. * * @return the newly created MBeanServer. * * @exception SecurityException if there is a SecurityManager and * the caller's permissions do not include or imply {@link * MBeanServerPermission}("createMBeanServer"). * * @exception JMRuntimeException if the property * javax.management.builder.initial exists but the * class it names cannot be instantiated through a public * no-argument constructor; or if the instantiated builder returns * null from its {@link MBeanServerBuilder#newMBeanServerDelegate * newMBeanServerDelegate} or {@link * MBeanServerBuilder#newMBeanServer newMBeanServer} methods. * * @exception ClassCastException if the property * javax.management.builder.initial exists and can be * instantiated but is not assignment compatible with {@link * MBeanServerBuilder}. */ public static MBeanServer createMBeanServer(String domain) { checkPermission("createMBeanServer"); final MBeanServer mBeanServer = newMBeanServer(domain); addMBeanServer(mBeanServer); return mBeanServer; } /** *

Return a new object implementing the MBeanServer interface * with a standard default domain name, without keeping an * internal reference to this new object. The default domain name * is used as the domain part in the ObjectName of MBeans when the * domain is specified by the user is null.

* *

The standard default domain name is * DefaultDomain.

* *

No reference is kept. findMBeanServer will not * be able to return a reference to this MBeanServer object, but * the garbage collector will be able to remove the MBeanServer * object when it is no longer referenced.

* *

This method is equivalent to newMBeanServer(null).

* * @return the newly created MBeanServer. * * @exception SecurityException if there is a SecurityManager and the * caller's permissions do not include or imply {@link * MBeanServerPermission}("newMBeanServer"). * * @exception JMRuntimeException if the property * javax.management.builder.initial exists but the * class it names cannot be instantiated through a public * no-argument constructor; or if the instantiated builder returns * null from its {@link MBeanServerBuilder#newMBeanServerDelegate * newMBeanServerDelegate} or {@link * MBeanServerBuilder#newMBeanServer newMBeanServer} methods. * * @exception ClassCastException if the property * javax.management.builder.initial exists and can be * instantiated but is not assignment compatible with {@link * MBeanServerBuilder}. */ public static MBeanServer newMBeanServer() { return newMBeanServer(null); } /** *

Return a new object implementing the MBeanServer interface * with the specified default domain name, without keeping an * internal reference to this new object. The given domain name * is used as the domain part in the ObjectName of MBeans when the * domain is specified by the user is null.

* *

No reference is kept. findMBeanServer will not * be able to return a reference to this MBeanServer object, but * the garbage collector will be able to remove the MBeanServer * object when it is no longer referenced.

* * @param domain the default domain name for the created * MBeanServer. This is the value that will be returned by {@link * MBeanServer#getDefaultDomain}. * * @return the newly created MBeanServer. * * @exception SecurityException if there is a SecurityManager and the * caller's permissions do not include or imply {@link * MBeanServerPermission}("newMBeanServer"). * * @exception JMRuntimeException if the property * javax.management.builder.initial exists but the * class it names cannot be instantiated through a public * no-argument constructor; or if the instantiated builder returns * null from its {@link MBeanServerBuilder#newMBeanServerDelegate * newMBeanServerDelegate} or {@link * MBeanServerBuilder#newMBeanServer newMBeanServer} methods. * * @exception ClassCastException if the property * javax.management.builder.initial exists and can be * instantiated but is not assignment compatible with {@link * MBeanServerBuilder}. */ public static MBeanServer newMBeanServer(String domain) { checkPermission("newMBeanServer"); // Get the builder. Creates a new one if necessary. // final MBeanServerBuilder mbsBuilder = getNewMBeanServerBuilder(); // Returned value cannot be null. NullPointerException if violated. synchronized(mbsBuilder) { final MBeanServerDelegate delegate = mbsBuilder.newMBeanServerDelegate(); if (delegate == null) { final String msg = "MBeanServerBuilder.newMBeanServerDelegate() " + "returned null"; throw new JMRuntimeException(msg); } final MBeanServer mbeanServer = mbsBuilder.newMBeanServer(domain,null,delegate); if (mbeanServer == null) { final String msg = "MBeanServerBuilder.newMBeanServer() returned null"; throw new JMRuntimeException(msg); } return mbeanServer; } } /** *

Return a list of registered MBeanServer objects. A * registered MBeanServer object is one that was created by one of * the createMBeanServer methods and not subsequently * released with releaseMBeanServer.

* * @param agentId The agent identifier of the MBeanServer to * retrieve. If this parameter is null, all registered * MBeanServers in this JVM are returned. Otherwise, only * MBeanServers whose id is equal to agentId are * returned. The id of an MBeanServer is the * MBeanServerId attribute of its delegate MBean. * * @return A list of MBeanServer objects. * * @exception SecurityException if there is a SecurityManager and the * caller's permissions do not include or imply {@link * MBeanServerPermission}("findMBeanServer"). */ public synchronized static ArrayList findMBeanServer(String agentId) { checkPermission("findMBeanServer"); if (agentId == null) return (ArrayList) mBeanServerList.clone(); ArrayList result = new ArrayList(); for (Iterator i = mBeanServerList.iterator(); i.hasNext(); ) { MBeanServer mbs = (MBeanServer) i.next(); String name = mBeanServerName(mbs); if (agentId.equals(name)) result.add(mbs); } return result; } /** * Return the ClassLoaderRepository used by the given MBeanServer. * This method is equivalent to {@link MBeanServer#getClassLoaderRepository() server.getClassLoaderRepository()}. * @param server The MBeanServer under examination. Since JMX 1.2, * if server is null, the result is a * {@link NullPointerException}. This behavior differs from what * was implemented in JMX 1.1 - where the possibility to use * null was deprecated. * @return The Class Loader Repository used by the given MBeanServer. * @exception SecurityException if there is a SecurityManager and * the caller's permissions do not include or imply {@link * MBeanPermission}("getClassLoaderRepository"). * * @exception NullPointerException if server is null. * * @since.unbundled JMX 1.1 **/ public static ClassLoaderRepository getClassLoaderRepository( MBeanServer server) { return server.getClassLoaderRepository(); } private static final ObjectName delegateName; static { ObjectName name; try { name = new ObjectName(ServiceName.DELEGATE); } catch (JMException e) { /* This can only happen if ServiceName.DELEGATE is an invalid ObjectName, which means serious brokenness! */ name = null; trace("", "internal error creating delegate ObjectName: " + e); } delegateName = name; } private static String mBeanServerName(MBeanServer mbs) { try { return (String) mbs.getAttribute(delegateName, "MBeanServerId"); } catch (JMException e) { return null; } } private static void checkPermission(String action) throws SecurityException { SecurityManager sm = System.getSecurityManager(); if (sm != null) { Permission perm = new MBeanServerPermission(action); sm.checkPermission(perm); } } private static synchronized void addMBeanServer(MBeanServer mbs) { mBeanServerList.add(mbs); } private static synchronized void removeMBeanServer(MBeanServer mbs) { boolean removed = mBeanServerList.remove(mbs); if (!removed) { trace("removeMBeanServer", "MBeanServer was not in list!"); throw new IllegalArgumentException("MBeanServer was not in list!"); } } private static final ArrayList mBeanServerList = new ArrayList(); /** * Load the builder class through the context class loader. * @param builderClassName The name of the builder class. **/ private static Class loadBuilderClass(String builderClassName) throws ClassNotFoundException { final ClassLoader loader = Thread.currentThread().getContextClassLoader(); if (loader != null) { // Try with context class loader return loader.loadClass(builderClassName); } // No context class loader? Try with Class.forName() return Class.forName(builderClassName); } /** * Creates the initial builder according to the * javax.management.builder.initial System property - if specified. * If any checked exception needs to be thrown, it is embedded in * a JMRuntimeException. **/ private static MBeanServerBuilder newBuilder(Class builderClass) { try { final Object builder = builderClass.newInstance(); return (MBeanServerBuilder)builder; } catch (RuntimeException x) { throw x; } catch (Exception x) { final String msg = "Failed to instantiate a MBeanServerBuilder from " + builderClass + ": " + x; throw new JMRuntimeException(msg, x); } } /** * Instantiate a new builder according to the * javax.management.builder.initial System property - if needed. **/ private static synchronized void checkMBeanServerBuilder() { try { PrivilegedAction act = new GetPropertyAction(JmxProperties.JMX_INITIAL_BUILDER); String builderClassName = (String) AccessController.doPrivileged(act); try { final Class newBuilderClass; if (builderClassName == null || builderClassName.length() == 0) newBuilderClass = MBeanServerBuilder.class; else newBuilderClass = loadBuilderClass(builderClassName); // Check whether a new builder needs to be created if (builder != null) { final Class builderClass = builder.getClass(); if (newBuilderClass == builderClass) return; // no need to create a new builder... } // Create a new builder builder = newBuilder(newBuilderClass); } catch (ClassNotFoundException x) { final String msg = "Failed to load MBeanServerBuilder class " + builderClassName + ": " + x; throw new JMRuntimeException(msg, x); } } catch (RuntimeException x) { debug("checkMBeanServerBuilder", "Failed to instantiate MBeanServerBuilder: " + x + "\n\t\tCheck the value of the " + JmxProperties.JMX_INITIAL_BUILDER + " property." ); throw x; } } /** * Get the current {@link javax.management.MBeanServerBuilder}, * as specified by the current value of the * javax.management.builder.initial property. * * This method consults the property and instantiates a new builder * if needed. * * @return the new current {@link javax.management.MBeanServerBuilder}. * * @exception SecurityException if there is a SecurityManager and * the caller's permissions do not make it possible to instantiate * a new builder. * @exception JMRuntimeException if the builder instantiation * fails with a checked exception - * {@link java.lang.ClassNotFoundException} etc... * * @since.unbundled JMX 1.2 **/ private static synchronized MBeanServerBuilder getNewMBeanServerBuilder() { checkMBeanServerBuilder(); return builder; } /** Private Stuff **/ private static void trace(String method, String message) { if (Trace.isSelected(Trace.LEVEL_TRACE, Trace.INFO_MBEANSERVER)) { Trace.send(Trace.LEVEL_TRACE, Trace.INFO_MBEANSERVER, MBeanServerFactory.class.getName(), method, message); } } /** Private Stuff **/ private static void debug(String method, String message) { if (Trace.isSelected(Trace.LEVEL_DEBUG, Trace.INFO_MBEANSERVER)) { Trace.send(Trace.LEVEL_DEBUG, Trace.INFO_MBEANSERVER, MBeanServerFactory.class.getName(), method, message); } } /** Private Stuff **/ private static void error(String method, String message) { if (Trace.isSelected(Trace.LEVEL_ERROR, Trace.INFO_MBEANSERVER)) { Trace.send(Trace.LEVEL_ERROR, Trace.INFO_MBEANSERVER, MBeanServerFactory.class.getName(), method, message); } } }