2064 lines · 82.4 KB · Java
RawDownload
/* * Copyright (c) 2000, 2008, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation.  Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.jmx.interceptor;  // JMX RIimport static com.sun.jmx.defaults.JmxProperties.MBEANSERVER_LOGGER;import com.sun.jmx.mbeanserver.DynamicMBean2;import com.sun.jmx.mbeanserver.Introspector;import com.sun.jmx.mbeanserver.MBeanInstantiator;import com.sun.jmx.mbeanserver.ModifiableClassLoaderRepository;import com.sun.jmx.mbeanserver.NamedObject;import com.sun.jmx.mbeanserver.Repository;import com.sun.jmx.mbeanserver.Repository.RegistrationContext;import com.sun.jmx.mbeanserver.Util;import com.sun.jmx.remote.util.EnvHelp; import java.io.ObjectInputStream;import java.lang.ref.WeakReference;import java.security.AccessControlContext;import java.security.AccessController;import java.security.Permission;import java.security.PrivilegedAction;import java.security.ProtectionDomain;import java.util.ArrayList;import java.util.HashSet;import java.util.List;import java.util.Set;import java.util.WeakHashMap;import java.util.logging.Level; // JMX importimport javax.management.Attribute;import javax.management.AttributeList;import javax.management.AttributeNotFoundException;import javax.management.DynamicMBean;import javax.management.InstanceAlreadyExistsException;import javax.management.InstanceNotFoundException;import javax.management.IntrospectionException;import javax.management.InvalidAttributeValueException;import javax.management.JMRuntimeException;import javax.management.ListenerNotFoundException;import javax.management.MBeanException;import javax.management.MBeanInfo;import javax.management.MBeanPermission;import javax.management.MBeanRegistration;import javax.management.MBeanRegistrationException;import javax.management.MBeanServer;import javax.management.MBeanServerDelegate;import javax.management.MBeanServerNotification;import javax.management.MBeanTrustPermission;import javax.management.NotCompliantMBeanException;import javax.management.Notification;import javax.management.NotificationBroadcaster;import javax.management.NotificationEmitter;import javax.management.NotificationFilter;import javax.management.NotificationListener;import javax.management.ObjectInstance;import javax.management.ObjectName;import javax.management.OperationsException;import javax.management.QueryEval;import javax.management.QueryExp;import javax.management.ReflectionException;import javax.management.RuntimeErrorException;import javax.management.RuntimeMBeanException;import javax.management.RuntimeOperationsException;import javax.management.loading.ClassLoaderRepository; /** * This is the default class for MBean manipulation on the agent side. It * contains the methods necessary for the creation, registration, and * deletion of MBeans as well as the access methods for registered MBeans. * This is the core component of the JMX infrastructure. * <P> * Every MBean which is added to the MBean server becomes manageable: its attributes and operations * become remotely accessible through the connectors/adaptors connected to that MBean server. * A Java object cannot be registered in the MBean server unless it is a JMX compliant MBean. * <P> * When an MBean is registered or unregistered in the MBean server an * {@link javax.management.MBeanServerNotification MBeanServerNotification} * Notification is emitted. To register an object as listener to MBeanServerNotifications * you should call the MBean server method {@link #addNotificationListener addNotificationListener} with <CODE>ObjectName</CODE> * the <CODE>ObjectName</CODE> of the {@link javax.management.MBeanServerDelegate MBeanServerDelegate}. * This <CODE>ObjectName</CODE> is: * <BR> * <CODE>JMImplementation:type=MBeanServerDelegate</CODE>. * * @since 1.5 */public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor {     /** The MBeanInstantiator object used by the     *  DefaultMBeanServerInterceptor */    private final transient MBeanInstantiator instantiator;     /** The MBean server object that is associated to the     *  DefaultMBeanServerInterceptor */    private transient MBeanServer server = null;     /** The MBean server delegate object that is associated to the     *  DefaultMBeanServerInterceptor */    private final transient MBeanServerDelegate delegate;     /** The Repository object used by the DefaultMBeanServerInterceptor */    private final transient Repository repository;     /** Wrappers for client listeners.  */    /* See the comment before addNotificationListener below.  */    private final transient        WeakHashMap<ListenerWrapper, WeakReference<ListenerWrapper>>            listenerWrappers =                new WeakHashMap<ListenerWrapper,                                WeakReference<ListenerWrapper>>();     /** The default domain of the object names */    private final String domain;     /** The sequence number identifying the notifications sent */    // Now sequence number is handled by MBeanServerDelegate.    // private int sequenceNumber=0;     /**     * Creates a DefaultMBeanServerInterceptor with the specified     * repository instance.     * <p>Do not forget to call <code>initialize(outer,delegate)</code>     * before using this object.     * @param outer A pointer to the MBeanServer object that must be     *        passed to the MBeans when invoking their     *        {@link javax.management.MBeanRegistration} interface.     * @param delegate A pointer to the MBeanServerDelegate associated     *        with the new MBeanServer. The new MBeanServer must register     *        this MBean in its MBean repository.     * @param instantiator The MBeanInstantiator that will be used to     *        instantiate MBeans and take care of class loading issues.     * @param repository The repository to use for this MBeanServer.     */    public DefaultMBeanServerInterceptor(MBeanServer         outer,                                         MBeanServerDelegate delegate,                                         MBeanInstantiator   instantiator,                                         Repository          repository) {        if (outer == null) throw new            IllegalArgumentException("outer MBeanServer cannot be null");        if (delegate == null) throw new            IllegalArgumentException("MBeanServerDelegate cannot be null");        if (instantiator == null) throw new            IllegalArgumentException("MBeanInstantiator cannot be null");        if (repository == null) throw new            IllegalArgumentException("Repository cannot be null");         this.server   = outer;        this.delegate = delegate;        this.instantiator = instantiator;        this.repository   = repository;        this.domain       = repository.getDefaultDomain();    }     public ObjectInstance createMBean(String className, ObjectName name)        throws ReflectionException, InstanceAlreadyExistsException,               MBeanRegistrationException, MBeanException,               NotCompliantMBeanException {         return createMBean(className, name, (Object[]) null, (String[]) null);     }     public ObjectInstance createMBean(String className, ObjectName name,                                      ObjectName loaderName)        throws ReflectionException, InstanceAlreadyExistsException,               MBeanRegistrationException, MBeanException,               NotCompliantMBeanException, InstanceNotFoundException {         return createMBean(className, name, loaderName, (Object[]) null,                           (String[]) null);    }     public ObjectInstance createMBean(String className, ObjectName name,                                      Object[] params, String[] signature)        throws ReflectionException, InstanceAlreadyExistsException,               MBeanRegistrationException, MBeanException,               NotCompliantMBeanException  {         try {            return createMBean(className, name, null, true,                               params, signature);        } catch (InstanceNotFoundException e) {            /* Can only happen if loaderName doesn't exist, but we just               passed null, so we shouldn't get this exception.  */            throw EnvHelp.initCause(                new IllegalArgumentException("Unexpected exception: " + e), e);        }    }     public ObjectInstance createMBean(String className, ObjectName name,                                      ObjectName loaderName,                                      Object[] params, String[] signature)        throws ReflectionException, InstanceAlreadyExistsException,               MBeanRegistrationException, MBeanException,               NotCompliantMBeanException, InstanceNotFoundException  {         return createMBean(className, name, loaderName, false,                           params, signature);    }     private ObjectInstance createMBean(String className, ObjectName name,                                       ObjectName loaderName,                                       boolean withDefaultLoaderRepository,                                       Object[] params, String[] signature)        throws ReflectionException, InstanceAlreadyExistsException,               MBeanRegistrationException, MBeanException,               NotCompliantMBeanException, InstanceNotFoundException {         Class<?> theClass;         if (className == null) {            final RuntimeException wrapped =                new IllegalArgumentException("The class name cannot be null");            throw new RuntimeOperationsException(wrapped,                      "Exception occurred during MBean creation");        }         if (name != null) {            if (name.isPattern()) {                final RuntimeException wrapped =                    new IllegalArgumentException("Invalid name->" +                                                 name.toString());                final String msg = "Exception occurred during MBean creation";                throw new RuntimeOperationsException(wrapped, msg);            }             name = nonDefaultDomain(name);        }         checkMBeanPermission(className, null, null, "instantiate");        checkMBeanPermission(className, null, name, "registerMBean");         /* Load the appropriate class. */        if (withDefaultLoaderRepository) {            if (MBEANSERVER_LOGGER.isLoggable(Level.FINER)) {                MBEANSERVER_LOGGER.logp(Level.FINER,                        DefaultMBeanServerInterceptor.class.getName(),                        "createMBean",                        "ClassName = " + className + ", ObjectName = " + name);            }            theClass =                instantiator.findClassWithDefaultLoaderRepository(className);        } else if (loaderName == null) {            if (MBEANSERVER_LOGGER.isLoggable(Level.FINER)) {                MBEANSERVER_LOGGER.logp(Level.FINER,                        DefaultMBeanServerInterceptor.class.getName(),                        "createMBean", "ClassName = " + className +                        ", ObjectName = " + name + ", Loader name = null");            }             theClass = instantiator.findClass(className,                                  server.getClass().getClassLoader());        } else {            loaderName = nonDefaultDomain(loaderName);             if (MBEANSERVER_LOGGER.isLoggable(Level.FINER)) {                MBEANSERVER_LOGGER.logp(Level.FINER,                        DefaultMBeanServerInterceptor.class.getName(),                        "createMBean", "ClassName = " + className +                        ", ObjectName = " + name +                        ", Loader name = " + loaderName);            }             theClass = instantiator.findClass(className, loaderName);        }         checkMBeanTrustPermission(theClass);         // Check that the MBean can be instantiated by the MBeanServer.        Introspector.testCreation(theClass);         // Check the JMX MBean compliance of the class        Introspector.checkCompliance(theClass);         Object moi= instantiator.instantiate(theClass, params,  signature,                                             server.getClass().getClassLoader());         final String infoClassName = getNewMBeanClassName(moi);         return registerObject(infoClassName, moi, name);    }     public ObjectInstance registerMBean(Object object, ObjectName name)        throws InstanceAlreadyExistsException, MBeanRegistrationException,        NotCompliantMBeanException  {         // ------------------------------        // ------------------------------        Class<?> theClass = object.getClass();         Introspector.checkCompliance(theClass);         final String infoClassName = getNewMBeanClassName(object);         checkMBeanPermission(infoClassName, null, name, "registerMBean");        checkMBeanTrustPermission(theClass);         return registerObject(infoClassName, object, name);    }     private static String getNewMBeanClassName(Object mbeanToRegister)            throws NotCompliantMBeanException {        if (mbeanToRegister instanceof DynamicMBean) {            DynamicMBean mbean = (DynamicMBean) mbeanToRegister;            final String name;            try {                name = mbean.getMBeanInfo().getClassName();            } catch (Exception e) {                // Includes case where getMBeanInfo() returns null                NotCompliantMBeanException ncmbe =                    new NotCompliantMBeanException("Bad getMBeanInfo()");                ncmbe.initCause(e);                throw ncmbe;            }            if (name == null) {                final String msg = "MBeanInfo has null class name";                throw new NotCompliantMBeanException(msg);            }            return name;        } else            return mbeanToRegister.getClass().getName();    }     private final Set<ObjectName> beingUnregistered =        new HashSet<ObjectName>();     public void unregisterMBean(ObjectName name)            throws InstanceNotFoundException, MBeanRegistrationException  {         if (name == null) {            final RuntimeException wrapped =                new IllegalArgumentException("Object name cannot be null");            throw new RuntimeOperationsException(wrapped,                      "Exception occurred trying to unregister the MBean");        }         name = nonDefaultDomain(name);         /* The semantics of preDeregister are tricky.  If it throws an           exception, then the unregisterMBean fails.  This allows an           MBean to refuse to be unregistered.  If it returns           successfully, then the unregisterMBean can proceed.  In           this case the preDeregister may have cleaned up some state,           and will not expect to be called a second time.  So if two           threads try to unregister the same MBean at the same time           then one of them must wait for the other one to either (a)           call preDeregister and get an exception or (b) call           preDeregister successfully and unregister the MBean.           Suppose thread T1 is unregistering an MBean and thread T2           is trying to unregister the same MBean, so waiting for T1.           Then a deadlock is possible if the preDeregister for T1           ends up needing a lock held by T2.  Given the semantics           just described, there does not seem to be any way to avoid           this.  This will not happen to code where it is clear for           any given MBean what thread may unregister that MBean.            On the other hand we clearly do not want a thread that is           unregistering MBean A to have to wait for another thread           that is unregistering another MBean B (see bug 6318664).  A           deadlock in this situation could reasonably be considered           gratuitous.  So holding a global lock across the           preDeregister call would be bad.            So we have a set of ObjectNames that some thread is           currently unregistering.  When a thread wants to unregister           a name, it must first check if the name is in the set, and           if so it must wait.  When a thread successfully unregisters           a name it removes the name from the set and notifies any           waiting threads that the set has changed.            This implies that we must be very careful to ensure that           the name is removed from the set and waiters notified, no           matter what code path is taken.  */         synchronized (beingUnregistered) {            while (beingUnregistered.contains(name)) {                try {                    beingUnregistered.wait();                } catch (InterruptedException e) {                    throw new MBeanRegistrationException(e, e.toString());                    // pretend the exception came from preDeregister;                    // in another execution sequence it could have                }            }            beingUnregistered.add(name);        }         try {            exclusiveUnregisterMBean(name);        } finally {            synchronized (beingUnregistered) {                beingUnregistered.remove(name);                beingUnregistered.notifyAll();            }        }    }     private void exclusiveUnregisterMBean(ObjectName name)            throws InstanceNotFoundException, MBeanRegistrationException {         DynamicMBean instance = getMBean(name);        // may throw InstanceNotFoundException         checkMBeanPermission(instance, null, name, "unregisterMBean");         if (instance instanceof MBeanRegistration)            preDeregisterInvoke((MBeanRegistration) instance);         final Object resource = getResource(instance);         // Unregisters the MBean from the repository.        // Returns the resource context that was used.        // The returned context does nothing for regular MBeans.        // For ClassLoader MBeans and JMXNamespace (and JMXDomain)        // MBeans - the context makes it possible to unregister these        // objects from the appropriate framework artifacts, such as        // the CLR or the dispatcher, from within the repository lock.        // In case of success, we also need to call context.done() at the        // end of this method.        //        final ResourceContext context =                unregisterFromRepository(resource, instance, name);         try {            if (instance instanceof MBeanRegistration)                postDeregisterInvoke(name,(MBeanRegistration) instance);        } finally {            context.done();        }    }     public ObjectInstance getObjectInstance(ObjectName name)            throws InstanceNotFoundException {         name = nonDefaultDomain(name);        DynamicMBean instance = getMBean(name);         checkMBeanPermission(instance, null, name, "getObjectInstance");         final String className = getClassName(instance);         return new ObjectInstance(name, className);    }     public Set<ObjectInstance> queryMBeans(ObjectName name, QueryExp query) {        SecurityManager sm = System.getSecurityManager();        if (sm != null) {            // Check if the caller has the right to invoke 'queryMBeans'            //            checkMBeanPermission((String) null, null, null, "queryMBeans");             // Perform query without "query".            //            Set<ObjectInstance> list = queryMBeansImpl(name, null);             // Check if the caller has the right to invoke 'queryMBeans'            // on each specific classname/objectname in the list.            //            Set<ObjectInstance> allowedList =                new HashSet<ObjectInstance>(list.size());            for (ObjectInstance oi : list) {                try {                    checkMBeanPermission(oi.getClassName(), null,                                         oi.getObjectName(), "queryMBeans");                    allowedList.add(oi);                } catch (SecurityException e) {                    // OK: Do not add this ObjectInstance to the list                }            }             // Apply query to allowed MBeans only.            //            return filterListOfObjectInstances(allowedList, query);        } else {

Lines 1–500 of 2064.