You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2070 lines
82 KiB
2070 lines
82 KiB
/*
|
|
* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
|
|
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*/
|
|
|
|
package com.sun.jmx.interceptor;
|
|
|
|
|
|
// JMX RI
|
|
import 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 import
|
|
import 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 {
|
|
// Perform query.
|
|
//
|
|
return queryMBeansImpl(name, query);
|
|
}
|
|
}
|
|
|
|
private Set<ObjectInstance> queryMBeansImpl(ObjectName name,
|
|
QueryExp query) {
|
|
// Query the MBeans on the repository
|
|
//
|
|
Set<NamedObject> list = repository.query(name, query);
|
|
|
|
return (objectInstancesFromFilteredNamedObjects(list, query));
|
|
}
|
|
|
|
public Set<ObjectName> queryNames(ObjectName name, QueryExp query) {
|
|
Set<ObjectName> queryList;
|
|
SecurityManager sm = System.getSecurityManager();
|
|
if (sm != null) {
|
|
// Check if the caller has the right to invoke 'queryNames'
|
|
//
|
|
checkMBeanPermission((String) null, null, null, "queryNames");
|
|
|
|
// Perform query without "query".
|
|
//
|
|
Set<ObjectInstance> list = queryMBeansImpl(name, null);
|
|
|
|
// Check if the caller has the right to invoke 'queryNames'
|
|
// 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(), "queryNames");
|
|
allowedList.add(oi);
|
|
} catch (SecurityException e) {
|
|
// OK: Do not add this ObjectInstance to the list
|
|
}
|
|
}
|
|
|
|
// Apply query to allowed MBeans only.
|
|
//
|
|
Set<ObjectInstance> queryObjectInstanceList =
|
|
filterListOfObjectInstances(allowedList, query);
|
|
queryList = new HashSet<ObjectName>(queryObjectInstanceList.size());
|
|
for (ObjectInstance oi : queryObjectInstanceList) {
|
|
queryList.add(oi.getObjectName());
|
|
}
|
|
} else {
|
|
// Perform query.
|
|
//
|
|
queryList = queryNamesImpl(name, query);
|
|
}
|
|
return queryList;
|
|
}
|
|
|
|
private Set<ObjectName> queryNamesImpl(ObjectName name, QueryExp query) {
|
|
// Query the MBeans on the repository
|
|
//
|
|
Set<NamedObject> list = repository.query(name, query);
|
|
|
|
return (objectNamesFromFilteredNamedObjects(list, query));
|
|
}
|
|
|
|
public boolean isRegistered(ObjectName name) {
|
|
if (name == null) {
|
|
throw new RuntimeOperationsException(
|
|
new IllegalArgumentException("Object name cannot be null"),
|
|
"Object name cannot be null");
|
|
}
|
|
|
|
name = nonDefaultDomain(name);
|
|
|
|
/* No Permission check */
|
|
// isRegistered is always unchecked as per JMX spec.
|
|
|
|
return (repository.contains(name));
|
|
}
|
|
|
|
public String[] getDomains() {
|
|
SecurityManager sm = System.getSecurityManager();
|
|
if (sm != null) {
|
|
// Check if the caller has the right to invoke 'getDomains'
|
|
//
|
|
checkMBeanPermission((String) null, null, null, "getDomains");
|
|
|
|
// Return domains
|
|
//
|
|
String[] domains = repository.getDomains();
|
|
|
|
// Check if the caller has the right to invoke 'getDomains'
|
|
// on each specific domain in the list.
|
|
//
|
|
List<String> result = new ArrayList<String>(domains.length);
|
|
for (int i = 0; i < domains.length; i++) {
|
|
try {
|
|
ObjectName dom = Util.newObjectName(domains[i] + ":x=x");
|
|
checkMBeanPermission((String) null, null, dom, "getDomains");
|
|
result.add(domains[i]);
|
|
} catch (SecurityException e) {
|
|
// OK: Do not add this domain to the list
|
|
}
|
|
}
|
|
|
|
// Make an array from result.
|
|
//
|
|
return result.toArray(new String[result.size()]);
|
|
} else {
|
|
return repository.getDomains();
|
|
}
|
|
}
|
|
|
|
public Integer getMBeanCount() {
|
|
return (repository.getCount());
|
|
}
|
|
|
|
public Object getAttribute(ObjectName name, String attribute)
|
|
throws MBeanException, AttributeNotFoundException,
|
|
InstanceNotFoundException, ReflectionException {
|
|
|
|
if (name == null) {
|
|
throw new RuntimeOperationsException(new
|
|
IllegalArgumentException("Object name cannot be null"),
|
|
"Exception occurred trying to invoke the getter on the MBean");
|
|
}
|
|
if (attribute == null) {
|
|
throw new RuntimeOperationsException(new
|
|
IllegalArgumentException("Attribute cannot be null"),
|
|
"Exception occurred trying to invoke the getter on the MBean");
|
|
}
|
|
|
|
name = nonDefaultDomain(name);
|
|
|
|
if (MBEANSERVER_LOGGER.isLoggable(Level.FINER)) {
|
|
MBEANSERVER_LOGGER.logp(Level.FINER,
|
|
DefaultMBeanServerInterceptor.class.getName(),
|
|
"getAttribute",
|
|
"Attribute = " + attribute + ", ObjectName = " + name);
|
|
}
|
|
|
|
final DynamicMBean instance = getMBean(name);
|
|
checkMBeanPermission(instance, attribute, name, "getAttribute");
|
|
|
|
try {
|
|
return instance.getAttribute(attribute);
|
|
} catch (AttributeNotFoundException e) {
|
|
throw e;
|
|
} catch (Throwable t) {
|
|
rethrowMaybeMBeanException(t);
|
|
throw new AssertionError(); // not reached
|
|
}
|
|
}
|
|
|
|
public AttributeList getAttributes(ObjectName name, String[] attributes)
|
|
throws InstanceNotFoundException, ReflectionException {
|
|
|
|
if (name == null) {
|
|
throw new RuntimeOperationsException(new
|
|
IllegalArgumentException("ObjectName name cannot be null"),
|
|
"Exception occurred trying to invoke the getter on the MBean");
|
|
}
|
|
|
|
if (attributes == null) {
|
|
throw new RuntimeOperationsException(new
|
|
IllegalArgumentException("Attributes cannot be null"),
|
|
"Exception occurred trying to invoke the getter on the MBean");
|
|
}
|
|
|
|
name = nonDefaultDomain(name);
|
|
|
|
if (MBEANSERVER_LOGGER.isLoggable(Level.FINER)) {
|
|
MBEANSERVER_LOGGER.logp(Level.FINER,
|
|
DefaultMBeanServerInterceptor.class.getName(),
|
|
"getAttributes", "ObjectName = " + name);
|
|
}
|
|
|
|
final DynamicMBean instance = getMBean(name);
|
|
final String[] allowedAttributes;
|
|
final SecurityManager sm = System.getSecurityManager();
|
|
if (sm == null)
|
|
allowedAttributes = attributes;
|
|
else {
|
|
final String classname = getClassName(instance);
|
|
|
|
// Check if the caller has the right to invoke 'getAttribute'
|
|
//
|
|
checkMBeanPermission(classname, null, name, "getAttribute");
|
|
|
|
// Check if the caller has the right to invoke 'getAttribute'
|
|
// on each specific attribute
|
|
//
|
|
List<String> allowedList =
|
|
new ArrayList<String>(attributes.length);
|
|
for (String attr : attributes) {
|
|
try {
|
|
checkMBeanPermission(classname, attr, name, "getAttribute");
|
|
allowedList.add(attr);
|
|
} catch (SecurityException e) {
|
|
// OK: Do not add this attribute to the list
|
|
}
|
|
}
|
|
allowedAttributes =
|
|
allowedList.toArray(new String[allowedList.size()]);
|
|
}
|
|
|
|
try {
|
|
return instance.getAttributes(allowedAttributes);
|
|
} catch (Throwable t) {
|
|
rethrow(t);
|
|
throw new AssertionError();
|
|
}
|
|
}
|
|
|
|
public void setAttribute(ObjectName name, Attribute attribute)
|
|
throws InstanceNotFoundException, AttributeNotFoundException,
|
|
InvalidAttributeValueException, MBeanException,
|
|
ReflectionException {
|
|
|
|
if (name == null) {
|
|
throw new RuntimeOperationsException(new
|
|
IllegalArgumentException("ObjectName name cannot be null"),
|
|
"Exception occurred trying to invoke the setter on the MBean");
|
|
}
|
|
|
|
if (attribute == null) {
|
|
throw new RuntimeOperationsException(new
|
|
IllegalArgumentException("Attribute cannot be null"),
|
|
"Exception occurred trying to invoke the setter on the MBean");
|
|
}
|
|
|
|
name = nonDefaultDomain(name);
|
|
|
|
if (MBEANSERVER_LOGGER.isLoggable(Level.FINER)) {
|
|
MBEANSERVER_LOGGER.logp(Level.FINER,
|
|
DefaultMBeanServerInterceptor.class.getName(),
|
|
"setAttribute", "ObjectName = " + name +
|
|
", Attribute = " + attribute.getName());
|
|
}
|
|
|
|
DynamicMBean instance = getMBean(name);
|
|
checkMBeanPermission(instance, attribute.getName(), name, "setAttribute");
|
|
|
|
try {
|
|
instance.setAttribute(attribute);
|
|
} catch (AttributeNotFoundException e) {
|
|
throw e;
|
|
} catch (InvalidAttributeValueException e) {
|
|
throw e;
|
|
} catch (Throwable t) {
|
|
rethrowMaybeMBeanException(t);
|
|
throw new AssertionError();
|
|
}
|
|
}
|
|
|
|
public AttributeList setAttributes(ObjectName name,
|
|
AttributeList attributes)
|
|
throws InstanceNotFoundException, ReflectionException {
|
|
|
|
if (name == null) {
|
|
throw new RuntimeOperationsException(new
|
|
IllegalArgumentException("ObjectName name cannot be null"),
|
|
"Exception occurred trying to invoke the setter on the MBean");
|
|
}
|
|
|
|
if (attributes == null) {
|
|
throw new RuntimeOperationsException(new
|
|
IllegalArgumentException("AttributeList cannot be null"),
|
|
"Exception occurred trying to invoke the setter on the MBean");
|
|
}
|
|
|
|
name = nonDefaultDomain(name);
|
|
|
|
final DynamicMBean instance = getMBean(name);
|
|
final AttributeList allowedAttributes;
|
|
final SecurityManager sm = System.getSecurityManager();
|
|
if (sm == null)
|
|
allowedAttributes = attributes;
|
|
else {
|
|
String classname = getClassName(instance);
|
|
|
|
// Check if the caller has the right to invoke 'setAttribute'
|
|
//
|
|
checkMBeanPermission(classname, null, name, "setAttribute");
|
|
|
|
// Check if the caller has the right to invoke 'setAttribute'
|
|
// on each specific attribute
|
|
//
|
|
allowedAttributes = new AttributeList(attributes.size());
|
|
for (Attribute attribute : attributes.asList()) {
|
|
try {
|
|
checkMBeanPermission(classname, attribute.getName(),
|
|
name, "setAttribute");
|
|
allowedAttributes.add(attribute);
|
|
} catch (SecurityException e) {
|
|
// OK: Do not add this attribute to the list
|
|
}
|
|
}
|
|
}
|
|
try {
|
|
return instance.setAttributes(allowedAttributes);
|
|
} catch (Throwable t) {
|
|
rethrow(t);
|
|
throw new AssertionError();
|
|
}
|
|
}
|
|
|
|
public Object invoke(ObjectName name, String operationName,
|
|
Object params[], String signature[])
|
|
throws InstanceNotFoundException, MBeanException,
|
|
ReflectionException {
|
|
|
|
name = nonDefaultDomain(name);
|
|
|
|
DynamicMBean instance = getMBean(name);
|
|
checkMBeanPermission(instance, operationName, name, "invoke");
|
|
try {
|
|
return instance.invoke(operationName, params, signature);
|
|
} catch (Throwable t) {
|
|
rethrowMaybeMBeanException(t);
|
|
throw new AssertionError();
|
|
}
|
|
}
|
|
|
|
/* Centralize some of the tedious exception wrapping demanded by the JMX
|
|
spec. */
|
|
private static void rethrow(Throwable t)
|
|
throws ReflectionException {
|
|
try {
|
|
throw t;
|
|
} catch (ReflectionException e) {
|
|
throw e;
|
|
} catch (RuntimeOperationsException e) {
|
|
throw e;
|
|
} catch (RuntimeErrorException e) {
|
|
throw e;
|
|
} catch (RuntimeException e) {
|
|
throw new RuntimeMBeanException(e, e.toString());
|
|
} catch (Error e) {
|
|
throw new RuntimeErrorException(e, e.toString());
|
|
} catch (Throwable t2) {
|
|
// should not happen
|
|
throw new RuntimeException("Unexpected exception", t2);
|
|
}
|
|
}
|
|
|
|
private static void rethrowMaybeMBeanException(Throwable t)
|
|
throws ReflectionException, MBeanException {
|
|
if (t instanceof MBeanException)
|
|
throw (MBeanException) t;
|
|
rethrow(t);
|
|
}
|
|
|
|
/**
|
|
* Register <code>object</code> in the repository, with the
|
|
* given <code>name</code>.
|
|
* This method is called by the various createMBean() flavours
|
|
* and by registerMBean() after all MBean compliance tests
|
|
* have been performed.
|
|
* <p>
|
|
* This method does not performed any kind of test compliance,
|
|
* and the caller should make sure that the given <code>object</code>
|
|
* is MBean compliant.
|
|
* <p>
|
|
* This methods performed all the basic steps needed for object
|
|
* registration:
|
|
* <ul>
|
|
* <li>If the <code>object</code> implements the MBeanRegistration
|
|
* interface, it invokes preRegister() on the object.</li>
|
|
* <li>Then the object is added to the repository with the given
|
|
* <code>name</code>.</li>
|
|
* <li>Finally, if the <code>object</code> implements the
|
|
* MBeanRegistration interface, it invokes postRegister()
|
|
* on the object.</li>
|
|
* </ul>
|
|
* @param object A reference to a MBean compliant object.
|
|
* @param name The ObjectName of the <code>object</code> MBean.
|
|
* @return the actual ObjectName with which the object was registered.
|
|
* @exception InstanceAlreadyExistsException if an object is already
|
|
* registered with that name.
|
|
* @exception MBeanRegistrationException if an exception occurs during
|
|
* registration.
|
|
**/
|
|
private ObjectInstance registerObject(String classname,
|
|
Object object, ObjectName name)
|
|
throws InstanceAlreadyExistsException,
|
|
MBeanRegistrationException,
|
|
NotCompliantMBeanException {
|
|
|
|
if (object == null) {
|
|
final RuntimeException wrapped =
|
|
new IllegalArgumentException("Cannot add null object");
|
|
throw new RuntimeOperationsException(wrapped,
|
|
"Exception occurred trying to register the MBean");
|
|
}
|
|
|
|
DynamicMBean mbean = Introspector.makeDynamicMBean(object);
|
|
|
|
return registerDynamicMBean(classname, mbean, name);
|
|
}
|
|
|
|
private ObjectInstance registerDynamicMBean(String classname,
|
|
DynamicMBean mbean,
|
|
ObjectName name)
|
|
throws InstanceAlreadyExistsException,
|
|
MBeanRegistrationException,
|
|
NotCompliantMBeanException {
|
|
|
|
|
|
name = nonDefaultDomain(name);
|
|
|
|
if (MBEANSERVER_LOGGER.isLoggable(Level.FINER)) {
|
|
MBEANSERVER_LOGGER.logp(Level.FINER,
|
|
DefaultMBeanServerInterceptor.class.getName(),
|
|
"registerMBean", "ObjectName = " + name);
|
|
}
|
|
|
|
ObjectName logicalName = preRegister(mbean, server, name);
|
|
|
|
// preRegister returned successfully, so from this point on we
|
|
// must call postRegister(false) if there is any problem.
|
|
boolean registered = false;
|
|
boolean registerFailed = false;
|
|
ResourceContext context = null;
|
|
|
|
try {
|
|
if (mbean instanceof DynamicMBean2) {
|
|
try {
|
|
((DynamicMBean2) mbean).preRegister2(server, logicalName);
|
|
registerFailed = true; // until we succeed
|
|
} catch (Exception e) {
|
|
if (e instanceof RuntimeException)
|
|
throw (RuntimeException) e;
|
|
if (e instanceof InstanceAlreadyExistsException)
|
|
throw (InstanceAlreadyExistsException) e;
|
|
throw new RuntimeException(e);
|
|
}
|
|
}
|
|
|
|
if (logicalName != name && logicalName != null) {
|
|
logicalName =
|
|
ObjectName.getInstance(nonDefaultDomain(logicalName));
|
|
}
|
|
|
|
checkMBeanPermission(classname, null, logicalName, "registerMBean");
|
|
|
|
if (logicalName == null) {
|
|
final RuntimeException wrapped =
|
|
new IllegalArgumentException("No object name specified");
|
|
throw new RuntimeOperationsException(wrapped,
|
|
"Exception occurred trying to register the MBean");
|
|
}
|
|
|
|
final Object resource = getResource(mbean);
|
|
|
|
// Register the MBean with the repository.
|
|
// Returns the resource context that was used.
|
|
// The returned context does nothing for regular MBeans.
|
|
// For ClassLoader MBeans the context makes it possible to register these
|
|
// objects with the appropriate framework artifacts, such as
|
|
// the CLR, from within the repository lock.
|
|
// In case of success, we also need to call context.done() at the
|
|
// end of this method.
|
|
//
|
|
context = registerWithRepository(resource, mbean, logicalName);
|
|
|
|
|
|
registerFailed = false;
|
|
registered = true;
|
|
|
|
} finally {
|
|
try {
|
|
postRegister(logicalName, mbean, registered, registerFailed);
|
|
} finally {
|
|
if (registered && context!=null) context.done();
|
|
}
|
|
}
|
|
return new ObjectInstance(logicalName, classname);
|
|
}
|
|
|
|
private static void throwMBeanRegistrationException(Throwable t, String where)
|
|
throws MBeanRegistrationException {
|
|
if (t instanceof RuntimeException) {
|
|
throw new RuntimeMBeanException((RuntimeException)t,
|
|
"RuntimeException thrown " + where);
|
|
} else if (t instanceof Error) {
|
|
throw new RuntimeErrorException((Error)t,
|
|
"Error thrown " + where);
|
|
} else if (t instanceof MBeanRegistrationException) {
|
|
throw (MBeanRegistrationException)t;
|
|
} else if (t instanceof Exception) {
|
|
throw new MBeanRegistrationException((Exception)t,
|
|
"Exception thrown " + where);
|
|
} else // neither Error nor Exception??
|
|
throw new RuntimeException(t);
|
|
}
|
|
|
|
private static ObjectName preRegister(
|
|
DynamicMBean mbean, MBeanServer mbs, ObjectName name)
|
|
throws InstanceAlreadyExistsException, MBeanRegistrationException {
|
|
|
|
ObjectName newName = null;
|
|
|
|
try {
|
|
if (mbean instanceof MBeanRegistration)
|
|
newName = ((MBeanRegistration) mbean).preRegister(mbs, name);
|
|
} catch (Throwable t) {
|
|
throwMBeanRegistrationException(t, "in preRegister method");
|
|
}
|
|
|
|
if (newName != null) return newName;
|
|
else return name;
|
|
}
|
|
|
|
private static void postRegister(
|
|
ObjectName logicalName, DynamicMBean mbean,
|
|
boolean registrationDone, boolean registerFailed) {
|
|
|
|
if (registerFailed && mbean instanceof DynamicMBean2)
|
|
((DynamicMBean2) mbean).registerFailed();
|
|
try {
|
|
if (mbean instanceof MBeanRegistration)
|
|
((MBeanRegistration) mbean).postRegister(registrationDone);
|
|
} catch (RuntimeException e) {
|
|
MBEANSERVER_LOGGER.fine("While registering MBean ["+logicalName+
|
|
"]: " + "Exception thrown by postRegister: " +
|
|
"rethrowing <"+e+">, but keeping the MBean registered");
|
|
throw new RuntimeMBeanException(e,
|
|
"RuntimeException thrown in postRegister method: "+
|
|
"rethrowing <"+e+">, but keeping the MBean registered");
|
|
} catch (Error er) {
|
|
MBEANSERVER_LOGGER.fine("While registering MBean ["+logicalName+
|
|
"]: " + "Error thrown by postRegister: " +
|
|
"rethrowing <"+er+">, but keeping the MBean registered");
|
|
throw new RuntimeErrorException(er,
|
|
"Error thrown in postRegister method: "+
|
|
"rethrowing <"+er+">, but keeping the MBean registered");
|
|
}
|
|
}
|
|
|
|
private static void preDeregisterInvoke(MBeanRegistration moi)
|
|
throws MBeanRegistrationException {
|
|
try {
|
|
moi.preDeregister();
|
|
} catch (Throwable t) {
|
|
throwMBeanRegistrationException(t, "in preDeregister method");
|
|
}
|
|
}
|
|
|
|
private static void postDeregisterInvoke(ObjectName mbean,
|
|
MBeanRegistration moi) {
|
|
try {
|
|
moi.postDeregister();
|
|
} catch (RuntimeException e) {
|
|
MBEANSERVER_LOGGER.fine("While unregistering MBean ["+mbean+
|
|
"]: " + "Exception thrown by postDeregister: " +
|
|
"rethrowing <"+e+">, although the MBean is succesfully " +
|
|
"unregistered");
|
|
throw new RuntimeMBeanException(e,
|
|
"RuntimeException thrown in postDeregister method: "+
|
|
"rethrowing <"+e+
|
|
">, although the MBean is sucessfully unregistered");
|
|
} catch (Error er) {
|
|
MBEANSERVER_LOGGER.fine("While unregistering MBean ["+mbean+
|
|
"]: " + "Error thrown by postDeregister: " +
|
|
"rethrowing <"+er+">, although the MBean is succesfully " +
|
|
"unregistered");
|
|
throw new RuntimeErrorException(er,
|
|
"Error thrown in postDeregister method: "+
|
|
"rethrowing <"+er+
|
|
">, although the MBean is sucessfully unregistered");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Gets a specific MBean controlled by the DefaultMBeanServerInterceptor.
|
|
* The name must have a non-default domain.
|
|
*/
|
|
private DynamicMBean getMBean(ObjectName name)
|
|
throws InstanceNotFoundException {
|
|
|
|
if (name == null) {
|
|
throw new RuntimeOperationsException(new
|
|
IllegalArgumentException("Object name cannot be null"),
|
|
"Exception occurred trying to get an MBean");
|
|
}
|
|
DynamicMBean obj = repository.retrieve(name);
|
|
if (obj == null) {
|
|
if (MBEANSERVER_LOGGER.isLoggable(Level.FINER)) {
|
|
MBEANSERVER_LOGGER.logp(Level.FINER,
|
|
DefaultMBeanServerInterceptor.class.getName(),
|
|
"getMBean", name + " : Found no object");
|
|
}
|
|
throw new InstanceNotFoundException(name.toString());
|
|
}
|
|
return obj;
|
|
}
|
|
|
|
private static Object getResource(DynamicMBean mbean) {
|
|
if (mbean instanceof DynamicMBean2)
|
|
return ((DynamicMBean2) mbean).getResource();
|
|
else
|
|
return mbean;
|
|
}
|
|
|
|
private ObjectName nonDefaultDomain(ObjectName name) {
|
|
if (name == null || name.getDomain().length() > 0)
|
|
return name;
|
|
|
|
/* The ObjectName looks like ":a=b", and that's what its
|
|
toString() will return in this implementation. So
|
|
we can just stick the default domain in front of it
|
|
to get a non-default-domain name. We depend on the
|
|
fact that toString() works like that and that it
|
|
leaves wildcards in place (so we can detect an error
|
|
if one is supplied where it shouldn't be). */
|
|
final String completeName = domain + name;
|
|
|
|
return Util.newObjectName(completeName);
|
|
}
|
|
|
|
public String getDefaultDomain() {
|
|
return domain;
|
|
}
|
|
|
|
/*
|
|
* Notification handling.
|
|
*
|
|
* This is not trivial, because the MBeanServer translates the
|
|
* source of a received notification from a reference to an MBean
|
|
* into the ObjectName of that MBean. While that does make
|
|
* notification sending easier for MBean writers, it comes at a
|
|
* considerable cost. We need to replace the source of a
|
|
* notification, which is basically wrong if there are also
|
|
* listeners registered directly with the MBean (without going
|
|
* through the MBean server). We also need to wrap the listener
|
|
* supplied by the client of the MBeanServer with a listener that
|
|
* performs the substitution before forwarding. This is why we
|
|
* strongly discourage people from putting MBean references in the
|
|
* source of their notifications. Instead they should arrange to
|
|
* put the ObjectName there themselves.
|
|
*
|
|
* However, existing code relies on the substitution, so we are
|
|
* stuck with it.
|
|
*
|
|
* Here's how we handle it. When you add a listener, we make a
|
|
* ListenerWrapper around it. We look that up in the
|
|
* listenerWrappers map, and if there was already a wrapper for
|
|
* that listener with the given ObjectName, we reuse it. This map
|
|
* is a WeakHashMap, so a listener that is no longer registered
|
|
* with any MBean can be garbage collected.
|
|
*
|
|
* We cannot use simpler solutions such as always creating a new
|
|
* wrapper or always registering the same listener with the MBean
|
|
* and using the handback to find the client's original listener.
|
|
* The reason is that we need to support the removeListener
|
|
* variant that removes all (listener,filter,handback) triples on
|
|
* a broadcaster that have a given listener. And we do not have
|
|
* any way to inspect a broadcaster's internal list of triples.
|
|
* So the same client listener must always map to the same
|
|
* listener registered with the broadcaster.
|
|
*
|
|
* Another possible solution would be to map from ObjectName to
|
|
* list of listener wrappers (or IdentityHashMap of listener
|
|
* wrappers), making this list the first time a listener is added
|
|
* on a given MBean, and removing it when the MBean is removed.
|
|
* This is probably more costly in memory, but could be useful if
|
|
* some day we don't want to rely on weak references.
|
|
*/
|
|
public void addNotificationListener(ObjectName name,
|
|
NotificationListener listener,
|
|
NotificationFilter filter,
|
|
Object handback)
|
|
throws InstanceNotFoundException {
|
|
|
|
// ------------------------------
|
|
// ------------------------------
|
|
if (MBEANSERVER_LOGGER.isLoggable(Level.FINER)) {
|
|
MBEANSERVER_LOGGER.logp(Level.FINER,
|
|
DefaultMBeanServerInterceptor.class.getName(),
|
|
"addNotificationListener", "ObjectName = " + name);
|
|
}
|
|
|
|
DynamicMBean instance = getMBean(name);
|
|
checkMBeanPermission(instance, null, name, "addNotificationListener");
|
|
|
|
NotificationBroadcaster broadcaster =
|
|
getNotificationBroadcaster(name, instance,
|
|
NotificationBroadcaster.class);
|
|
|
|
// ------------------
|
|
// Check listener
|
|
// ------------------
|
|
if (listener == null) {
|
|
throw new RuntimeOperationsException(new
|
|
IllegalArgumentException("Null listener"),"Null listener");
|
|
}
|
|
|
|
NotificationListener listenerWrapper =
|
|
getListenerWrapper(listener, name, instance, true);
|
|
broadcaster.addNotificationListener(listenerWrapper, filter, handback);
|
|
}
|
|
|
|
public void addNotificationListener(ObjectName name,
|
|
ObjectName listener,
|
|
NotificationFilter filter,
|
|
Object handback)
|
|
throws InstanceNotFoundException {
|
|
|
|
// ------------------------------
|
|
// ------------------------------
|
|
|
|
// ----------------
|
|
// Get listener object
|
|
// ----------------
|
|
DynamicMBean instance = getMBean(listener);
|
|
Object resource = getResource(instance);
|
|
if (!(resource instanceof NotificationListener)) {
|
|
throw new RuntimeOperationsException(new
|
|
IllegalArgumentException(listener.getCanonicalName()),
|
|
"The MBean " + listener.getCanonicalName() +
|
|
"does not implement the NotificationListener interface") ;
|
|
}
|
|
|
|
// ----------------
|
|
// Add a listener on an MBean
|
|
// ----------------
|
|
if (MBEANSERVER_LOGGER.isLoggable(Level.FINER)) {
|
|
MBEANSERVER_LOGGER.logp(Level.FINER,
|
|
DefaultMBeanServerInterceptor.class.getName(),
|
|
"addNotificationListener",
|
|
"ObjectName = " + name + ", Listener = " + listener);
|
|
}
|
|
server.addNotificationListener(name,(NotificationListener) resource,
|
|
filter, handback) ;
|
|
}
|
|
|
|
public void removeNotificationListener(ObjectName name,
|
|
NotificationListener listener)
|
|
throws InstanceNotFoundException, ListenerNotFoundException {
|
|
removeNotificationListener(name, listener, null, null, true);
|
|
}
|
|
|
|
public void removeNotificationListener(ObjectName name,
|
|
NotificationListener listener,
|
|
NotificationFilter filter,
|
|
Object handback)
|
|
throws InstanceNotFoundException, ListenerNotFoundException {
|
|
removeNotificationListener(name, listener, filter, handback, false);
|
|
}
|
|
|
|
public void removeNotificationListener(ObjectName name,
|
|
ObjectName listener)
|
|
throws InstanceNotFoundException, ListenerNotFoundException {
|
|
NotificationListener instance = getListener(listener);
|
|
|
|
if (MBEANSERVER_LOGGER.isLoggable(Level.FINER)) {
|
|
MBEANSERVER_LOGGER.logp(Level.FINER,
|
|
DefaultMBeanServerInterceptor.class.getName(),
|
|
"removeNotificationListener",
|
|
"ObjectName = " + name + ", Listener = " + listener);
|
|
}
|
|
server.removeNotificationListener(name, instance);
|
|
}
|
|
|
|
public void removeNotificationListener(ObjectName name,
|
|
ObjectName listener,
|
|
NotificationFilter filter,
|
|
Object handback)
|
|
throws InstanceNotFoundException, ListenerNotFoundException {
|
|
|
|
NotificationListener instance = getListener(listener);
|
|
|
|
if (MBEANSERVER_LOGGER.isLoggable(Level.FINER)) {
|
|
MBEANSERVER_LOGGER.logp(Level.FINER,
|
|
DefaultMBeanServerInterceptor.class.getName(),
|
|
"removeNotificationListener",
|
|
"ObjectName = " + name + ", Listener = " + listener);
|
|
}
|
|
server.removeNotificationListener(name, instance, filter, handback);
|
|
}
|
|
|
|
private NotificationListener getListener(ObjectName listener)
|
|
throws ListenerNotFoundException {
|
|
// ----------------
|
|
// Get listener object
|
|
// ----------------
|
|
DynamicMBean instance;
|
|
try {
|
|
instance = getMBean(listener);
|
|
} catch (InstanceNotFoundException e) {
|
|
throw EnvHelp.initCause(
|
|
new ListenerNotFoundException(e.getMessage()), e);
|
|
}
|
|
|
|
Object resource = getResource(instance);
|
|
if (!(resource instanceof NotificationListener)) {
|
|
final RuntimeException exc =
|
|
new IllegalArgumentException(listener.getCanonicalName());
|
|
final String msg =
|
|
"MBean " + listener.getCanonicalName() + " does not " +
|
|
"implement " + NotificationListener.class.getName();
|
|
throw new RuntimeOperationsException(exc, msg);
|
|
}
|
|
return (NotificationListener) resource;
|
|
}
|
|
|
|
private void removeNotificationListener(ObjectName name,
|
|
NotificationListener listener,
|
|
NotificationFilter filter,
|
|
Object handback,
|
|
boolean removeAll)
|
|
throws InstanceNotFoundException, ListenerNotFoundException {
|
|
|
|
if (MBEANSERVER_LOGGER.isLoggable(Level.FINER)) {
|
|
MBEANSERVER_LOGGER.logp(Level.FINER,
|
|
DefaultMBeanServerInterceptor.class.getName(),
|
|
"removeNotificationListener", "ObjectName = " + name);
|
|
}
|
|
|
|
DynamicMBean instance = getMBean(name);
|
|
checkMBeanPermission(instance, null, name, "removeNotificationListener");
|
|
|
|
/* We could simplify the code by assigning broadcaster after
|
|
assigning listenerWrapper, but that would change the error
|
|
behavior when both the broadcaster and the listener are
|
|
erroneous. */
|
|
|
|
Class<? extends NotificationBroadcaster> reqClass =
|
|
removeAll ? NotificationBroadcaster.class : NotificationEmitter.class;
|
|
NotificationBroadcaster broadcaster =
|
|
getNotificationBroadcaster(name, instance, reqClass);
|
|
|
|
NotificationListener listenerWrapper =
|
|
getListenerWrapper(listener, name, instance, false);
|
|
|
|
if (listenerWrapper == null)
|
|
throw new ListenerNotFoundException("Unknown listener");
|
|
|
|
if (removeAll)
|
|
broadcaster.removeNotificationListener(listenerWrapper);
|
|
else {
|
|
NotificationEmitter emitter = (NotificationEmitter) broadcaster;
|
|
emitter.removeNotificationListener(listenerWrapper,
|
|
filter,
|
|
handback);
|
|
}
|
|
}
|
|
|
|
private static <T extends NotificationBroadcaster>
|
|
T getNotificationBroadcaster(ObjectName name, Object instance,
|
|
Class<T> reqClass) {
|
|
if (reqClass.isInstance(instance))
|
|
return reqClass.cast(instance);
|
|
if (instance instanceof DynamicMBean2)
|
|
instance = ((DynamicMBean2) instance).getResource();
|
|
if (reqClass.isInstance(instance))
|
|
return reqClass.cast(instance);
|
|
final RuntimeException exc =
|
|
new IllegalArgumentException(name.getCanonicalName());
|
|
final String msg =
|
|
"MBean " + name.getCanonicalName() + " does not " +
|
|
"implement " + reqClass.getName();
|
|
throw new RuntimeOperationsException(exc, msg);
|
|
}
|
|
|
|
public MBeanInfo getMBeanInfo(ObjectName name)
|
|
throws InstanceNotFoundException, IntrospectionException,
|
|
ReflectionException {
|
|
|
|
// ------------------------------
|
|
// ------------------------------
|
|
|
|
DynamicMBean moi = getMBean(name);
|
|
final MBeanInfo mbi;
|
|
try {
|
|
mbi = moi.getMBeanInfo();
|
|
} catch (RuntimeMBeanException e) {
|
|
throw e;
|
|
} catch (RuntimeErrorException e) {
|
|
throw e;
|
|
} catch (RuntimeException e) {
|
|
throw new RuntimeMBeanException(e,
|
|
"getMBeanInfo threw RuntimeException");
|
|
} catch (Error e) {
|
|
throw new RuntimeErrorException(e, "getMBeanInfo threw Error");
|
|
}
|
|
if (mbi == null)
|
|
throw new JMRuntimeException("MBean " + name +
|
|
"has no MBeanInfo");
|
|
|
|
checkMBeanPermission(mbi.getClassName(), null, name, "getMBeanInfo");
|
|
|
|
return mbi;
|
|
}
|
|
|
|
public boolean isInstanceOf(ObjectName name, String className)
|
|
throws InstanceNotFoundException {
|
|
|
|
final DynamicMBean instance = getMBean(name);
|
|
checkMBeanPermission(instance, null, name, "isInstanceOf");
|
|
|
|
try {
|
|
Object resource = getResource(instance);
|
|
|
|
final String resourceClassName =
|
|
(resource instanceof DynamicMBean) ?
|
|
getClassName((DynamicMBean) resource) :
|
|
resource.getClass().getName();
|
|
|
|
if (resourceClassName.equals(className))
|
|
return true;
|
|
final ClassLoader cl = resource.getClass().getClassLoader();
|
|
|
|
final Class<?> classNameClass = Class.forName(className, false, cl);
|
|
if (classNameClass.isInstance(resource))
|
|
return true;
|
|
|
|
final Class<?> resourceClass = Class.forName(resourceClassName, false, cl);
|
|
return classNameClass.isAssignableFrom(resourceClass);
|
|
} catch (Exception x) {
|
|
/* Could be SecurityException or ClassNotFoundException */
|
|
if (MBEANSERVER_LOGGER.isLoggable(Level.FINEST)) {
|
|
MBEANSERVER_LOGGER.logp(Level.FINEST,
|
|
DefaultMBeanServerInterceptor.class.getName(),
|
|
"isInstanceOf", "Exception calling isInstanceOf", x);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* <p>Return the {@link java.lang.ClassLoader} that was used for
|
|
* loading the class of the named MBean.
|
|
* @param mbeanName The ObjectName of the MBean.
|
|
* @return The ClassLoader used for that MBean.
|
|
* @exception InstanceNotFoundException if the named MBean is not found.
|
|
*/
|
|
public ClassLoader getClassLoaderFor(ObjectName mbeanName)
|
|
throws InstanceNotFoundException {
|
|
|
|
DynamicMBean instance = getMBean(mbeanName);
|
|
checkMBeanPermission(instance, null, mbeanName, "getClassLoaderFor");
|
|
return getResource(instance).getClass().getClassLoader();
|
|
}
|
|
|
|
/**
|
|
* <p>Return the named {@link java.lang.ClassLoader}.
|
|
* @param loaderName The ObjectName of the ClassLoader.
|
|
* @return The named ClassLoader.
|
|
* @exception InstanceNotFoundException if the named ClassLoader
|
|
* is not found.
|
|
*/
|
|
public ClassLoader getClassLoader(ObjectName loaderName)
|
|
throws InstanceNotFoundException {
|
|
|
|
if (loaderName == null) {
|
|
checkMBeanPermission((String) null, null, null, "getClassLoader");
|
|
return server.getClass().getClassLoader();
|
|
}
|
|
|
|
DynamicMBean instance = getMBean(loaderName);
|
|
checkMBeanPermission(instance, null, loaderName, "getClassLoader");
|
|
|
|
Object resource = getResource(instance);
|
|
|
|
/* Check if the given MBean is a ClassLoader */
|
|
if (!(resource instanceof ClassLoader))
|
|
throw new InstanceNotFoundException(loaderName.toString() +
|
|
" is not a classloader");
|
|
|
|
return (ClassLoader) resource;
|
|
}
|
|
|
|
/**
|
|
* Sends an MBeanServerNotifications with the specified type for the
|
|
* MBean with the specified ObjectName
|
|
*/
|
|
private void sendNotification(String NotifType, ObjectName name) {
|
|
|
|
// ------------------------------
|
|
// ------------------------------
|
|
|
|
// ---------------------
|
|
// Create notification
|
|
// ---------------------
|
|
MBeanServerNotification notif = new MBeanServerNotification(
|
|
NotifType,MBeanServerDelegate.DELEGATE_NAME,0,name);
|
|
|
|
if (MBEANSERVER_LOGGER.isLoggable(Level.FINER)) {
|
|
MBEANSERVER_LOGGER.logp(Level.FINER,
|
|
DefaultMBeanServerInterceptor.class.getName(),
|
|
"sendNotification", NotifType + " " + name);
|
|
}
|
|
|
|
delegate.sendNotification(notif);
|
|
}
|
|
|
|
/**
|
|
* Applies the specified queries to the set of NamedObjects.
|
|
*/
|
|
private Set<ObjectName>
|
|
objectNamesFromFilteredNamedObjects(Set<NamedObject> list,
|
|
QueryExp query) {
|
|
Set<ObjectName> result = new HashSet<ObjectName>();
|
|
// No query ...
|
|
if (query == null) {
|
|
for (NamedObject no : list) {
|
|
result.add(no.getName());
|
|
}
|
|
} else {
|
|
// Access the filter
|
|
final MBeanServer oldServer = QueryEval.getMBeanServer();
|
|
query.setMBeanServer(server);
|
|
try {
|
|
for (NamedObject no : list) {
|
|
boolean res;
|
|
try {
|
|
res = query.apply(no.getName());
|
|
} catch (Exception e) {
|
|
res = false;
|
|
}
|
|
if (res) {
|
|
result.add(no.getName());
|
|
}
|
|
}
|
|
} finally {
|
|
/*
|
|
* query.setMBeanServer is probably
|
|
* QueryEval.setMBeanServer so put back the old
|
|
* value. Since that method uses a ThreadLocal
|
|
* variable, this code is only needed for the
|
|
* unusual case where the user creates a custom
|
|
* QueryExp that calls a nested query on another
|
|
* MBeanServer.
|
|
*/
|
|
query.setMBeanServer(oldServer);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Applies the specified queries to the set of NamedObjects.
|
|
*/
|
|
private Set<ObjectInstance>
|
|
objectInstancesFromFilteredNamedObjects(Set<NamedObject> list,
|
|
QueryExp query) {
|
|
Set<ObjectInstance> result = new HashSet<ObjectInstance>();
|
|
// No query ...
|
|
if (query == null) {
|
|
for (NamedObject no : list) {
|
|
final DynamicMBean obj = no.getObject();
|
|
final String className = safeGetClassName(obj);
|
|
result.add(new ObjectInstance(no.getName(), className));
|
|
}
|
|
} else {
|
|
// Access the filter
|
|
MBeanServer oldServer = QueryEval.getMBeanServer();
|
|
query.setMBeanServer(server);
|
|
try {
|
|
for (NamedObject no : list) {
|
|
final DynamicMBean obj = no.getObject();
|
|
boolean res;
|
|
try {
|
|
res = query.apply(no.getName());
|
|
} catch (Exception e) {
|
|
res = false;
|
|
}
|
|
if (res) {
|
|
String className = safeGetClassName(obj);
|
|
result.add(new ObjectInstance(no.getName(), className));
|
|
}
|
|
}
|
|
} finally {
|
|
/*
|
|
* query.setMBeanServer is probably
|
|
* QueryEval.setMBeanServer so put back the old
|
|
* value. Since that method uses a ThreadLocal
|
|
* variable, this code is only needed for the
|
|
* unusual case where the user creates a custom
|
|
* QueryExp that calls a nested query on another
|
|
* MBeanServer.
|
|
*/
|
|
query.setMBeanServer(oldServer);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
private static String safeGetClassName(DynamicMBean mbean) {
|
|
try {
|
|
return getClassName(mbean);
|
|
} catch (Exception e) {
|
|
if (MBEANSERVER_LOGGER.isLoggable(Level.FINEST)) {
|
|
MBEANSERVER_LOGGER.logp(Level.FINEST,
|
|
DefaultMBeanServerInterceptor.class.getName(),
|
|
"safeGetClassName",
|
|
"Exception getting MBean class name", e);
|
|
}
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Applies the specified queries to the set of ObjectInstances.
|
|
*/
|
|
private Set<ObjectInstance>
|
|
filterListOfObjectInstances(Set<ObjectInstance> list,
|
|
QueryExp query) {
|
|
// Null query.
|
|
//
|
|
if (query == null) {
|
|
return list;
|
|
} else {
|
|
Set<ObjectInstance> result = new HashSet<ObjectInstance>();
|
|
// Access the filter.
|
|
//
|
|
for (ObjectInstance oi : list) {
|
|
boolean res = false;
|
|
MBeanServer oldServer = QueryEval.getMBeanServer();
|
|
query.setMBeanServer(server);
|
|
try {
|
|
res = query.apply(oi.getObjectName());
|
|
} catch (Exception e) {
|
|
res = false;
|
|
} finally {
|
|
/*
|
|
* query.setMBeanServer is probably
|
|
* QueryEval.setMBeanServer so put back the old
|
|
* value. Since that method uses a ThreadLocal
|
|
* variable, this code is only needed for the
|
|
* unusual case where the user creates a custom
|
|
* QueryExp that calls a nested query on another
|
|
* MBeanServer.
|
|
*/
|
|
query.setMBeanServer(oldServer);
|
|
}
|
|
if (res) {
|
|
result.add(oi);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Get the existing wrapper for this listener, name, and mbean, if
|
|
* there is one. Otherwise, if "create" is true, create and
|
|
* return one. Otherwise, return null.
|
|
*
|
|
* We use a WeakHashMap so that if the only reference to a user
|
|
* listener is in listenerWrappers, it can be garbage collected.
|
|
* This requires a certain amount of care, because only the key in
|
|
* a WeakHashMap is weak; the value is strong. We need to recover
|
|
* the existing wrapper object (not just an object that is equal
|
|
* to it), so we would like listenerWrappers to map any
|
|
* ListenerWrapper to the canonical ListenerWrapper for that
|
|
* (listener,name,mbean) set. But we do not want this canonical
|
|
* wrapper to be referenced strongly. Therefore we put it inside
|
|
* a WeakReference and that is the value in the WeakHashMap.
|
|
*/
|
|
private NotificationListener getListenerWrapper(NotificationListener l,
|
|
ObjectName name,
|
|
DynamicMBean mbean,
|
|
boolean create) {
|
|
Object resource = getResource(mbean);
|
|
ListenerWrapper wrapper = new ListenerWrapper(l, name, resource);
|
|
synchronized (listenerWrappers) {
|
|
WeakReference<ListenerWrapper> ref = listenerWrappers.get(wrapper);
|
|
if (ref != null) {
|
|
NotificationListener existing = ref.get();
|
|
if (existing != null)
|
|
return existing;
|
|
}
|
|
if (create) {
|
|
ref = new WeakReference<ListenerWrapper>(wrapper);
|
|
listenerWrappers.put(wrapper, ref);
|
|
return wrapper;
|
|
} else
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public Object instantiate(String className) throws ReflectionException,
|
|
MBeanException {
|
|
throw new UnsupportedOperationException("Not supported yet.");
|
|
}
|
|
|
|
public Object instantiate(String className, ObjectName loaderName) throws ReflectionException,
|
|
MBeanException,
|
|
InstanceNotFoundException {
|
|
throw new UnsupportedOperationException("Not supported yet.");
|
|
}
|
|
|
|
public Object instantiate(String className, Object[] params,
|
|
String[] signature) throws ReflectionException, MBeanException {
|
|
throw new UnsupportedOperationException("Not supported yet.");
|
|
}
|
|
|
|
public Object instantiate(String className, ObjectName loaderName,
|
|
Object[] params, String[] signature) throws ReflectionException,
|
|
MBeanException,
|
|
InstanceNotFoundException {
|
|
throw new UnsupportedOperationException("Not supported yet.");
|
|
}
|
|
|
|
public ObjectInputStream deserialize(ObjectName name, byte[] data) throws InstanceNotFoundException,
|
|
OperationsException {
|
|
throw new UnsupportedOperationException("Not supported yet.");
|
|
}
|
|
|
|
public ObjectInputStream deserialize(String className, byte[] data) throws OperationsException,
|
|
ReflectionException {
|
|
throw new UnsupportedOperationException("Not supported yet.");
|
|
}
|
|
|
|
public ObjectInputStream deserialize(String className, ObjectName loaderName,
|
|
byte[] data) throws InstanceNotFoundException, OperationsException,
|
|
ReflectionException {
|
|
throw new UnsupportedOperationException("Not supported yet.");
|
|
}
|
|
|
|
public ClassLoaderRepository getClassLoaderRepository() {
|
|
throw new UnsupportedOperationException("Not supported yet.");
|
|
}
|
|
|
|
private static class ListenerWrapper implements NotificationListener {
|
|
ListenerWrapper(NotificationListener l, ObjectName name,
|
|
Object mbean) {
|
|
this.listener = l;
|
|
this.name = name;
|
|
this.mbean = mbean;
|
|
}
|
|
|
|
public void handleNotification(Notification notification,
|
|
Object handback) {
|
|
if (notification != null) {
|
|
if (notification.getSource() == mbean)
|
|
notification.setSource(name);
|
|
}
|
|
|
|
/*
|
|
* Listeners are not supposed to throw exceptions. If
|
|
* this one does, we could remove it from the MBean. It
|
|
* might indicate that a connector has stopped working,
|
|
* for instance, and there is no point in sending future
|
|
* notifications over that connection. However, this
|
|
* seems rather drastic, so instead we propagate the
|
|
* exception and let the broadcaster handle it.
|
|
*/
|
|
listener.handleNotification(notification, handback);
|
|
}
|
|
|
|
@Override
|
|
public boolean equals(Object o) {
|
|
if (!(o instanceof ListenerWrapper))
|
|
return false;
|
|
ListenerWrapper w = (ListenerWrapper) o;
|
|
return (w.listener == listener && w.mbean == mbean
|
|
&& w.name.equals(name));
|
|
/*
|
|
* We compare all three, in case the same MBean object
|
|
* gets unregistered and then reregistered under a
|
|
* different name, or the same name gets assigned to two
|
|
* different MBean objects at different times. We do the
|
|
* comparisons in this order to avoid the slow
|
|
* ObjectName.equals when possible.
|
|
*/
|
|
}
|
|
|
|
@Override
|
|
public int hashCode() {
|
|
return (System.identityHashCode(listener) ^
|
|
System.identityHashCode(mbean));
|
|
/*
|
|
* We do not include name.hashCode() in the hash because
|
|
* computing it is slow and usually we will not have two
|
|
* instances of ListenerWrapper with the same mbean but
|
|
* different ObjectNames. That can happen if the MBean is
|
|
* unregistered from one name and reregistered with
|
|
* another, and there is no garbage collection between; or
|
|
* if the same object is registered under two names (which
|
|
* is not recommended because MBeanRegistration will
|
|
* break). But even in these unusual cases the hash code
|
|
* does not have to be unique.
|
|
*/
|
|
}
|
|
|
|
private NotificationListener listener;
|
|
private ObjectName name;
|
|
private Object mbean;
|
|
}
|
|
|
|
// SECURITY CHECKS
|
|
//----------------
|
|
|
|
private static String getClassName(DynamicMBean mbean) {
|
|
if (mbean instanceof DynamicMBean2)
|
|
return ((DynamicMBean2) mbean).getClassName();
|
|
else
|
|
return mbean.getMBeanInfo().getClassName();
|
|
}
|
|
|
|
private static void checkMBeanPermission(DynamicMBean mbean,
|
|
String member,
|
|
ObjectName objectName,
|
|
String actions) {
|
|
SecurityManager sm = System.getSecurityManager();
|
|
if (sm != null) {
|
|
checkMBeanPermission(safeGetClassName(mbean),
|
|
member,
|
|
objectName,
|
|
actions);
|
|
}
|
|
}
|
|
|
|
private static void checkMBeanPermission(String classname,
|
|
String member,
|
|
ObjectName objectName,
|
|
String actions) {
|
|
SecurityManager sm = System.getSecurityManager();
|
|
if (sm != null) {
|
|
Permission perm = new MBeanPermission(classname,
|
|
member,
|
|
objectName,
|
|
actions);
|
|
sm.checkPermission(perm);
|
|
}
|
|
}
|
|
|
|
private static void checkMBeanTrustPermission(final Class<?> theClass)
|
|
throws SecurityException {
|
|
SecurityManager sm = System.getSecurityManager();
|
|
if (sm != null) {
|
|
Permission perm = new MBeanTrustPermission("register");
|
|
PrivilegedAction<ProtectionDomain> act =
|
|
new PrivilegedAction<ProtectionDomain>() {
|
|
public ProtectionDomain run() {
|
|
return theClass.getProtectionDomain();
|
|
}
|
|
};
|
|
ProtectionDomain pd = AccessController.doPrivileged(act);
|
|
AccessControlContext acc =
|
|
new AccessControlContext(new ProtectionDomain[] { pd });
|
|
sm.checkPermission(perm, acc);
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------------
|
|
//
|
|
// Dealing with registration of special MBeans in the repository.
|
|
//
|
|
// ------------------------------------------------------------------
|
|
|
|
/**
|
|
* A RegistrationContext that makes it possible to perform additional
|
|
* post registration actions (or post unregistration actions) outside
|
|
* of the repository lock, once postRegister (or postDeregister) has
|
|
* been called.
|
|
* The method {@code done()} will be called in registerMBean or
|
|
* unregisterMBean, at the end.
|
|
*/
|
|
private static interface ResourceContext extends RegistrationContext {
|
|
public void done();
|
|
/** An empty ResourceContext which does nothing **/
|
|
public static final ResourceContext NONE = new ResourceContext() {
|
|
public void done() {}
|
|
public void registering() {}
|
|
public void unregistered() {}
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Adds a MBean in the repository,
|
|
* sends MBeanServerNotification.REGISTRATION_NOTIFICATION,
|
|
* returns ResourceContext for special resources such as ClassLoaders
|
|
* or JMXNamespaces. For regular MBean this method returns
|
|
* ResourceContext.NONE.
|
|
* @return a ResourceContext for special resources such as ClassLoaders
|
|
* or JMXNamespaces.
|
|
*/
|
|
private ResourceContext registerWithRepository(
|
|
final Object resource,
|
|
final DynamicMBean object,
|
|
final ObjectName logicalName)
|
|
throws InstanceAlreadyExistsException,
|
|
MBeanRegistrationException {
|
|
|
|
// Creates a registration context, if needed.
|
|
//
|
|
final ResourceContext context =
|
|
makeResourceContextFor(resource, logicalName);
|
|
|
|
|
|
repository.addMBean(object, logicalName, context);
|
|
// May throw InstanceAlreadyExistsException
|
|
|
|
// ---------------------
|
|
// Send create event
|
|
// ---------------------
|
|
if (MBEANSERVER_LOGGER.isLoggable(Level.FINER)) {
|
|
MBEANSERVER_LOGGER.logp(Level.FINER,
|
|
DefaultMBeanServerInterceptor.class.getName(),
|
|
"addObject", "Send create notification of object " +
|
|
logicalName.getCanonicalName());
|
|
}
|
|
|
|
sendNotification(
|
|
MBeanServerNotification.REGISTRATION_NOTIFICATION,
|
|
logicalName);
|
|
|
|
return context;
|
|
}
|
|
|
|
/**
|
|
* Removes a MBean in the repository,
|
|
* sends MBeanServerNotification.UNREGISTRATION_NOTIFICATION,
|
|
* returns ResourceContext for special resources such as ClassLoaders
|
|
* or JMXNamespaces, or null. For regular MBean this method returns
|
|
* ResourceContext.NONE.
|
|
*
|
|
* @return a ResourceContext for special resources such as ClassLoaders
|
|
* or JMXNamespaces.
|
|
*/
|
|
private ResourceContext unregisterFromRepository(
|
|
final Object resource,
|
|
final DynamicMBean object,
|
|
final ObjectName logicalName)
|
|
throws InstanceNotFoundException {
|
|
|
|
// Creates a registration context, if needed.
|
|
//
|
|
final ResourceContext context =
|
|
makeResourceContextFor(resource, logicalName);
|
|
|
|
|
|
repository.remove(logicalName, context);
|
|
|
|
// ---------------------
|
|
// Send deletion event
|
|
// ---------------------
|
|
if (MBEANSERVER_LOGGER.isLoggable(Level.FINER)) {
|
|
MBEANSERVER_LOGGER.logp(Level.FINER,
|
|
DefaultMBeanServerInterceptor.class.getName(),
|
|
"unregisterMBean", "Send delete notification of object " +
|
|
logicalName.getCanonicalName());
|
|
}
|
|
|
|
sendNotification(MBeanServerNotification.UNREGISTRATION_NOTIFICATION,
|
|
logicalName);
|
|
return context;
|
|
}
|
|
|
|
|
|
/**
|
|
* Registers a ClassLoader with the CLR.
|
|
* This method is called by the ResourceContext from within the
|
|
* repository lock.
|
|
* @param loader The ClassLoader.
|
|
* @param logicalName The ClassLoader MBean ObjectName.
|
|
*/
|
|
private void addClassLoader(ClassLoader loader,
|
|
final ObjectName logicalName) {
|
|
/**
|
|
* Called when the newly registered MBean is a ClassLoader
|
|
* If so, tell the ClassLoaderRepository (CLR) about it. We do
|
|
* this even if the loader is a PrivateClassLoader. In that
|
|
* case, the CLR remembers the loader for use when it is
|
|
* explicitly named (e.g. as the loader in createMBean) but
|
|
* does not add it to the list that is consulted by
|
|
* ClassLoaderRepository.loadClass.
|
|
*/
|
|
final ModifiableClassLoaderRepository clr = getInstantiatorCLR();
|
|
if (clr == null) {
|
|
final RuntimeException wrapped =
|
|
new IllegalArgumentException(
|
|
"Dynamic addition of class loaders" +
|
|
" is not supported");
|
|
throw new RuntimeOperationsException(wrapped,
|
|
"Exception occurred trying to register" +
|
|
" the MBean as a class loader");
|
|
}
|
|
clr.addClassLoader(logicalName, loader);
|
|
}
|
|
|
|
/**
|
|
* Unregisters a ClassLoader from the CLR.
|
|
* This method is called by the ResourceContext from within the
|
|
* repository lock.
|
|
* @param loader The ClassLoader.
|
|
* @param logicalName The ClassLoader MBean ObjectName.
|
|
*/
|
|
private void removeClassLoader(ClassLoader loader,
|
|
final ObjectName logicalName) {
|
|
/**
|
|
* Removes the MBean from the default loader repository.
|
|
*/
|
|
if (loader != server.getClass().getClassLoader()) {
|
|
final ModifiableClassLoaderRepository clr = getInstantiatorCLR();
|
|
if (clr != null) {
|
|
clr.removeClassLoader(logicalName);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Creates a ResourceContext for a ClassLoader MBean.
|
|
* The resource context makes it possible to add the ClassLoader to
|
|
* (ResourceContext.registering) or resp. remove the ClassLoader from
|
|
* (ResourceContext.unregistered) the CLR
|
|
* when the associated MBean is added to or resp. removed from the
|
|
* repository.
|
|
*
|
|
* @param loader The ClassLoader MBean being registered or
|
|
* unregistered.
|
|
* @param logicalName The name of the ClassLoader MBean.
|
|
* @return a ResourceContext that takes in charge the addition or removal
|
|
* of the loader to or from the CLR.
|
|
*/
|
|
private ResourceContext createClassLoaderContext(
|
|
final ClassLoader loader,
|
|
final ObjectName logicalName) {
|
|
return new ResourceContext() {
|
|
|
|
public void registering() {
|
|
addClassLoader(loader, logicalName);
|
|
}
|
|
|
|
public void unregistered() {
|
|
removeClassLoader(loader, logicalName);
|
|
}
|
|
|
|
public void done() {
|
|
}
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Creates a ResourceContext for the given resource.
|
|
* If the resource does not need a ResourceContext, returns
|
|
* ResourceContext.NONE.
|
|
* At this time, only ClassLoaders need a ResourceContext.
|
|
*
|
|
* @param resource The resource being registered or unregistered.
|
|
* @param logicalName The name of the associated MBean.
|
|
* @return
|
|
*/
|
|
private ResourceContext makeResourceContextFor(Object resource,
|
|
ObjectName logicalName) {
|
|
if (resource instanceof ClassLoader) {
|
|
return createClassLoaderContext((ClassLoader) resource,
|
|
logicalName);
|
|
}
|
|
return ResourceContext.NONE;
|
|
}
|
|
|
|
private ModifiableClassLoaderRepository getInstantiatorCLR() {
|
|
return AccessController.doPrivileged(new PrivilegedAction<ModifiableClassLoaderRepository>() {
|
|
@Override
|
|
public ModifiableClassLoaderRepository run() {
|
|
return instantiator != null ? instantiator.getClassLoaderRepository() : null;
|
|
}
|
|
});
|
|
}
|
|
}
|