/*
* Copyright (c) 1998-2011 Caucho Technology -- all rights reserved
*
* This file is part of Resin(R) Open Source
*
* Each copy or derived work must preserve the copyright notice and this
* notice unmodified.
*
* Resin Open Source is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Resin Open Source 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, or any warranty
* of NON-INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with Resin Open Source; if not, write to the
* Free SoftwareFoundation, Inc.
* 59 Temple Place, Suite 330
* Boston, MA 02111-1307 USA
*
* @author Scott Ferguson
*/
package com.caucho.jmx;
import com.caucho.loader.Environment;
import com.caucho.loader.EnvironmentLocal;
import com.caucho.loader.WeakCloseListener;
import com.caucho.util.L10N;
import javax.management.*;
import javax.management.loading.ClassLoaderRepository;
import java.io.ObjectInputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* The main interface for retrieving and managing JMX objects.
*/
abstract public class AbstractMBeanServer implements MBeanServer {
private static final L10N L = new L10N(AbstractMBeanServer.class);
private static final Logger log
= Logger.getLogger(AbstractMBeanServer.class.getName());
static ObjectName SERVER_DELEGATE_NAME;
// default domain
private String _defaultDomain;
/**
* Creats a new MBeanServer implementation.
*/
public AbstractMBeanServer(String defaultDomain)
{
_defaultDomain = defaultDomain;
Environment.addClassLoaderListener(new WeakCloseListener(this));
}
/**
* Returns the context implementation.
*/
protected MBeanContext createContext()
{
return createContext(Thread.currentThread().getContextClassLoader());
}
/**
* Returns the context implementation.
*/
protected final MBeanContext getCurrentContext()
{
return getCurrentContext(Thread.currentThread().getContextClassLoader());
}
/**
* Returns the context implementation.
*/
protected final MBeanContext getGlobalContext()
{
return createContext(ClassLoader.getSystemClassLoader());
}
/**
* Returns the context implementation, creating if necessary.
*/
abstract protected MBeanContext createContext(ClassLoader loader);
/**
* Returns the context implementation.
*/
abstract protected MBeanContext getCurrentContext(ClassLoader loader);
/**
* Sets the context implementation.
*/
abstract protected void setCurrentContext(MBeanContext context,
ClassLoader loader);
/**
* Returns the context implementation.
*/
abstract protected MBeanContext getContext(ClassLoader loader);
/**
* Removes the context implementation.
*/
protected void removeContext(MBeanContext context, ClassLoader loader)
{
}
/**
* Returns the view implementation.
*/
protected MBeanView getView()
{
return createContext().getView();
}
/**
* Returns the view implementation.
*/
protected MBeanView getGlobalView()
{
return getGlobalContext().getView();
}
/**
* Returns the view implementation.
*/
protected MBeanView getParentView()
{
return null;
}
/**
* Instantiate an MBean object to be registered with the server.
*
* @param className the className to be instantiated.
*
* @return the instantiated object.
*/
public Object instantiate(String className)
throws ReflectionException, MBeanException
{
try {
Class<?> cl = getClassLoaderRepository().loadClass(className);
return cl.newInstance();
} catch (ClassNotFoundException e) {
throw new ReflectionException(e);
} catch (InstantiationException e) {
throw new ReflectionException(e);
} catch (ExceptionInInitializerError e) {
Throwable cause = e.getCause();
if (cause instanceof Exception)
throw new MBeanException((Exception) cause);
else
throw e;
} catch (IllegalAccessException e) {
throw new ReflectionException(e);
}
}
/**
* Instantiate an MBean object to be registered with the server.
*
* @param className the className to be instantiated.
* @param loaderName names the classloader to be used
*
* @return the instantiated object.
*/
public Object instantiate(String className, ObjectName loaderName)
throws ReflectionException, MBeanException, InstanceNotFoundException
{
MBeanWrapper mbean = getMBean(loaderName);
if (mbean == null)
throw new InstanceNotFoundException(String.valueOf(loaderName));
else if (! (mbean.getObject() instanceof ClassLoader))
throw new InstanceNotFoundException(L.l("{0} is not a class loader",
loaderName));
try {
ClassLoader loader = (ClassLoader) mbean.getObject();
Class<?> cl = loader.loadClass(className);
return cl.newInstance();
} catch (ClassNotFoundException e) {
throw new ReflectionException(e);
} catch (InstantiationException e) {
throw new ReflectionException(e);
} catch (IllegalAccessException e) {
throw new ReflectionException(e);
}
}
/**
* Instantiate an MBean object with the given arguments to be
* passed to the constructor.
*
* @param className the className to be instantiated.
* @param params the parameters for the constructor.
* @param signature the signature of the constructor
*
* @return the instantiated object.
*/
public Object instantiate(String className,
Object []params, String []signature)
throws ReflectionException, MBeanException
{
try {
Class<?> cl = getClassLoaderRepository().loadClass(className);
Constructor<?> constructor = getConstructor(cl, signature);
return constructor.newInstance(params);
} catch (ClassNotFoundException e) {
throw new ReflectionException(e);
} catch (InstantiationException e) {
throw new ReflectionException(e);
} catch (InvocationTargetException e) {
throw new MBeanException(e);
} catch (IllegalAccessException e) {
throw new ReflectionException(e);
}
}
/**
* Instantiate an MBean object with the given arguments to be
* passed to the constructor.
*
* @param className the className to be instantiated.
* @param loaderName names the classloader to be used
* @param params the parameters for the constructor.
* @param signature the signature of the constructor
*
* @return the instantiated object.
*/
public Object instantiate(String className, ObjectName loaderName,
Object []params, String []signature)
throws ReflectionException, MBeanException, InstanceNotFoundException
{
MBeanWrapper mbean = getMBean(loaderName);
if (mbean == null)
throw new InstanceNotFoundException(String.valueOf(loaderName));
else if (! (mbean.getObject() instanceof ClassLoader))
throw new InstanceNotFoundException(L.l("{0} is not a class loader",
loaderName));
try {
ClassLoader loader = (ClassLoader) mbean.getObject();
Class<?> cl = loader.loadClass(className);
Constructor<?> constructor = getConstructor(cl, signature);
return constructor.newInstance(params);
} catch (ClassNotFoundException e) {
throw new ReflectionException(e);
} catch (InstantiationException e) {
throw new ReflectionException(e);
} catch (InvocationTargetException e) {
throw new MBeanException(e);
} catch (IllegalAccessException e) {
throw new ReflectionException(e);
}
}
/**
* Returns the class's constructor with the matching sig.
*/
private Constructor<?> getConstructor(Class<?> cl, String []sig)
{
Constructor<?> []constructors = cl.getConstructors();
for (int i = 0; i < constructors.length; i++) {
if (! Modifier.isPublic(constructors[i].getModifiers()))
continue;
if (isMatch(constructors[i].getParameterTypes(), sig))
return constructors[i];
}
return null;
}
/**
* Matches the parameters the sig.
*/
private boolean isMatch(Class<?> []param, String []sig)
{
if (param.length != sig.length)
return false;
for (int i = 0; i < param.length; i++) {
if (! param[i].getName().equals(sig[i]))
return false;
}
return true;
}
/**
* Instantiate and register an MBean.
*
* @param className the className to be instantiated.
* @param name the name of the mbean.
*
* @return the instantiated object.
*/
public ObjectInstance createMBean(String className, ObjectName name)
throws ReflectionException, InstanceAlreadyExistsException,
MBeanException, NotCompliantMBeanException
{
return registerMBean(instantiate(className), name);
}
/**
* Instantiate and register an MBean.
*
* @param className the className to be instantiated.
* @param name the name of the mbean.
* @param loaderName the name of the class loader to user
*
* @return the instantiated object.
*/
public ObjectInstance createMBean(String className, ObjectName name,
ObjectName loaderName)
throws ReflectionException, InstanceAlreadyExistsException,
MBeanException, NotCompliantMBeanException, InstanceNotFoundException
{
return registerMBean(instantiate(className, loaderName), name);
}
/**
* Instantiate and register an MBean.
*
* @param className the className to be instantiated.
* @param name the name of the mbean.
* @param params the parameters for the constructor.
* @param signature the signature of the constructor
*
* @return the instantiated object.
*/
public ObjectInstance createMBean(String className, ObjectName name,
Object []params, String []signature)
throws ReflectionException, InstanceAlreadyExistsException,
MBeanException, NotCompliantMBeanException
{
return registerMBean(instantiate(className, params, signature),
name);
}
/**
* Instantiate and register an MBean.
*
* @param className the className to be instantiated.
* @param name the name of the mbean.
* @param loaderName the loader name for the mbean.
* @param params the parameters for the constructor.
* @param signature the signature of the constructor
*
* @return the instantiated object.
*/
public ObjectInstance createMBean(String className, ObjectName name,
ObjectName loaderName,
Object []params, String []signature)
throws ReflectionException, InstanceAlreadyExistsException,
MBeanException, NotCompliantMBeanException, InstanceNotFoundException
{
return registerMBean(instantiate(className, loaderName, params, signature),
name);
}
/**
* Registers an MBean with the server.
*
* @param object the object to be registered as an MBean
* @param name the name of the mbean.
*
* @return the instantiated object.
*/
public ObjectInstance registerMBean(Object object, ObjectName name)
throws InstanceAlreadyExistsException,
MBeanRegistrationException,
NotCompliantMBeanException
{
if (object == null)
throw new NullPointerException();
MBeanContext context;
context = createContext();
if (context.getMBean(name) != null) {
throw new InstanceAlreadyExistsException(L.l("'{0}' in {1}",
name, context, this));
}
DynamicMBean dynMBean = createMBean(object, name);
if (object instanceof IntrospectionMBean)
object = ((IntrospectionMBean) object).getImplementation();
else if (object instanceof StandardMBean) {
object = ((StandardMBean) object).getImplementation();
}
MBeanWrapper mbean = new MBeanWrapper(context, name, object, dynMBean);
return context.registerMBean(mbean, name);
}
/**
* Creates the dynamic mbean.
*/
private DynamicMBean createMBean(Object obj, ObjectName name)
throws NotCompliantMBeanException
{
if (obj == null)
throw new NotCompliantMBeanException(L.l("{0} mbean is null", name));
else if (obj instanceof DynamicMBean)
return (DynamicMBean) obj;
Class<?> ifc = getMBeanInterface(obj.getClass());
if (ifc == null)
throw new NotCompliantMBeanException(L.l("{0} mbean has no MBean interface for class {1}", name, obj.getClass().getName()));
return new IntrospectionMBean(obj, ifc);
}
/**
* Returns the mbean interface.
*/
private Class<?> getMBeanInterface(Class<?> cl)
{
for (; cl != null; cl = cl.getSuperclass()) {
Class<?> []interfaces = cl.getInterfaces();
String mbeanName = cl.getName() + "MBean";
String mxbeanName = cl.getName() + "MXBean";
int p = mbeanName.lastIndexOf('.');
mbeanName = mbeanName.substring(p);
p = mxbeanName.lastIndexOf('.');
mxbeanName = mxbeanName.substring(p);
for (int i = 0; i < interfaces.length; i++) {
Class<?> ifc = interfaces[i];
if (ifc.getName().endsWith(mbeanName)
|| ifc.getName().endsWith(mxbeanName))
return ifc;
}
}
return null;
}
/**
* Unregisters an MBean from the server.
*
* @param name the name of the mbean.
*/
public void unregisterMBean(ObjectName name)
throws InstanceNotFoundException,
MBeanRegistrationException
{
MBeanContext context = getCurrentContext();
if (context != null) {
context.unregisterMBean(name);
log.finest(name + " unregistered from " + this);
}
// XXX: getDelegate().sendUnregisterNotification(name);
}
/**
* Returns the MBean registered with the given name.
*
* @param name the name of the mbean.
*
* @return the matching mbean object.
*/
public ObjectInstance getObjectInstance(ObjectName name)
throws InstanceNotFoundException
{
MBeanWrapper mbean = getMBean(name);
if (mbean == null)
throw new InstanceNotFoundException(String.valueOf(name));
return mbean.getObjectInstance();
}
/**
* Returns a set of MBeans matching the query.
*
* @param name the name of the mbean to match.
* @param query the queryd to match.
*
* @return the set of matching mbean object.
*/
public Set<ObjectInstance> queryMBeans(ObjectName name, QueryExp query)
{
try {
if (query != null) {
query.setMBeanServer(this);
}
return getView().queryMBeans(name, query);
} catch (Exception e) {
log.log(Level.WARNING, e.toString(), e);
return null;
}
}
/**
* Returns a set of names for MBeans matching the query.
*
* @param name the name of the mbean to match.
* @param query the query to match.
*
* @return the set of matching mbean names.
*/
public Set<ObjectName> queryNames(ObjectName name, QueryExp query)
{
try {
if (query != null) {
query.setMBeanServer(this);
}
return getView().queryNames(name, query);
} catch (Exception e) {
log.log(Level.WARNING, e.toString(), e);
return null;
}
}
/**
* Returns true if the given object is registered with the server.
*
* @param name the name of the mbean to test.
*
* @return true if the object is registered.
*/
public boolean isRegistered(ObjectName name)
{
return getView().getMBean(name) != null;
}
/**
* Returns the number of MBeans registered.
*
* @return the number of registered mbeans.
*/
public Integer getMBeanCount()
{
return new Integer(getView().getMBeanCount());
}
/**
* Returns a specific attribute of a named MBean.
*
* @param name the name of the mbean to test
* @param attribute the name of the attribute to retrieve
*
* @return the attribute value
*/
public Object getAttribute(ObjectName name, String attribute)
throws MBeanException, AttributeNotFoundException,
InstanceNotFoundException, ReflectionException
{
MBeanWrapper mbean = getMBean(name);
if (mbean == null) {
throw new InstanceNotFoundException(String.valueOf(name));
}
return mbean.getAttribute(attribute);
}
/**
* Returns a list of several MBean attributes.
*
* @param name the name of the mbean
* @param attributes the name of the attributes to retrieve
*
* @return the attribute value
*/
public AttributeList getAttributes(ObjectName name, String []attributes)
throws InstanceNotFoundException, ReflectionException
{
MBeanWrapper mbean = getMBean(name);
if (mbean == null)
throw new InstanceNotFoundException(String.valueOf(name));
return mbean.getAttributes(attributes);
}
/**
* Sets an attribute in the MBean.
*
* @param name the name of the mbean
* @param attribute the name/value of the attribute to set.
*/
public void setAttribute(ObjectName name, Attribute attribute)
throws InstanceNotFoundException, AttributeNotFoundException,
InvalidAttributeValueException, MBeanException, ReflectionException
{
MBeanWrapper mbean = getMBean(name);
if (mbean == null)
throw new InstanceNotFoundException(String.valueOf(name));
mbean.setAttribute(attribute);
}
/**
* Set an attributes in the MBean.
*
* @param name the name of the mbean
* @param attributes the name/value list of the attribute to set.
*/
public AttributeList setAttributes(ObjectName name, AttributeList attributes)
throws InstanceNotFoundException, ReflectionException
{
MBeanWrapper mbean = getMBean(name);
if (mbean == null)
throw new InstanceNotFoundException(String.valueOf(name));
return mbean.setAttributes(attributes);
}
/**
* Invokers an operation on an MBean.
*
* @param name the name of the mbean
* @param operationName the name of the method to invoke
* @param params the parameters for the invocation
* @param signature the signature of the operation
*/
public Object invoke(ObjectName name,
String operationName,
Object []params,
String []signature)
throws InstanceNotFoundException, MBeanException, ReflectionException
{
MBeanWrapper mbean = getMBean(name);
if (mbean == null)
throw new InstanceNotFoundException(String.valueOf(name));
return mbean.invoke(operationName, params, signature);
}
/**
* Returns the default domain for naming the MBean
*/
public String getDefaultDomain()
{
return _defaultDomain;
}
/**
* Adds a listener to a registered MBean
*
* @param name the name of the mbean
* @param listener the listener object
* @param filter filters events the listener is interested in
* @param handback context to be returned to the listener
*/
public void addNotificationListener(ObjectName name,
NotificationListener listener,
NotificationFilter filter,
Object handback)
throws InstanceNotFoundException
{
MBeanWrapper mbean = getMBean(name);
if (mbean == null)
throw new InstanceNotFoundException(String.valueOf(name));
mbean.addNotificationListener(listener, filter, handback);
createContext().addNotificationListener(name, listener, filter, handback);
}
/**
* Adds a listener to a registered MBean
*
* @param name the name of the mbean
* @param listenerName the name of the listener
* @param filter filters events the listener is interested in
* @param handback context to be returned to the listener
*/
public void addNotificationListener(ObjectName name,
ObjectName listenerName,
NotificationFilter filter,
Object handback)
throws InstanceNotFoundException
{
MBeanWrapper listenerMBean = getMBean(listenerName);
if (listenerMBean == null)
throw new InstanceNotFoundException(String.valueOf(listenerName));
NotificationListener listener = listenerMBean.getListener();
if (listener == null) {
IllegalArgumentException exn = new IllegalArgumentException(L.l("{0} does not implement NotificationListener.", listenerName));
throw new RuntimeOperationsException(exn);
}
addNotificationListener(name, listener, filter, handback);
}
/**
* Removes a listener from a registered MBean
*
* @param name the name of the mbean
* @param listener the listener object
*/
public void removeNotificationListener(ObjectName name,
NotificationListener listener)
throws InstanceNotFoundException, ListenerNotFoundException
{
MBeanWrapper mbean = getMBean(name);
if (mbean == null)
throw new InstanceNotFoundException(String.valueOf(name));
mbean.removeNotificationListener(listener);
createContext().removeNotificationListener(name, listener);
}
/**
* Removes a listener from a registered MBean
*
* @param name the name of the mbean
* @param listenerName the name of the listener
*/
public void removeNotificationListener(ObjectName name,
ObjectName listenerName)
throws InstanceNotFoundException, ListenerNotFoundException
{
MBeanWrapper listenerMBean = getMBean(listenerName);
if (listenerMBean == null)
throw new InstanceNotFoundException(String.valueOf(listenerName));
NotificationListener listener = listenerMBean.getListener();
if (listener == null) {
IllegalArgumentException exn = new IllegalArgumentException(L.l("{0} does not implement NotificationListener."));
throw new RuntimeOperationsException(exn);
}
removeNotificationListener(name, listener);
}
/**
* Removes a listener from a registered MBean
*
* @param name the name of the mbean
* @param listenerName the name of the listener
* @param filter the notification filter
* @param handback context to the listener
*
* @since JMX 1.2
*/
public void removeNotificationListener(ObjectName name,
ObjectName listenerName,
NotificationFilter filter,
Object handback)
throws InstanceNotFoundException, ListenerNotFoundException
{
MBeanWrapper listenerMBean = getMBean(listenerName);
if (listenerMBean == null)
throw new InstanceNotFoundException(String.valueOf(listenerName));
NotificationListener listener = listenerMBean.getListener();
if (listener == null) {
IllegalArgumentException exn = new IllegalArgumentException(L.l("{0} does not implement NotificationListener."));
throw new RuntimeOperationsException(exn);
}
removeNotificationListener(name, listener, filter, handback);
}
/**
* Removes a listener from a registered MBean
*
* @param name the name of the mbean
* @param listenerName the name of the listener
* @param filter the notification filter
* @param handback context to the listener
*
* @since JMX 1.2
*/
public void removeNotificationListener(ObjectName name,
NotificationListener listener,
NotificationFilter filter,
Object handback)
throws InstanceNotFoundException, ListenerNotFoundException
{
MBeanWrapper mbean = getMBean(name);
if (mbean == null)
throw new InstanceNotFoundException(String.valueOf(name));
createContext().removeNotificationListener(name, listener, filter, handback);
mbean.removeNotificationListener(listener, filter, handback);
}
/**
* Returns the analyzed information for an MBean
*
* @param name the name of the mbean
*
* @return the introspected information
*/
public MBeanInfo getMBeanInfo(ObjectName name)
throws InstanceNotFoundException, IntrospectionException,
ReflectionException
{
MBeanWrapper mbean = getMBean(name);
if (mbean == null)
throw new InstanceNotFoundException(String.valueOf(name));
MBeanInfo info = mbean.getMBeanInfo();
return info;
}
/**
* Returns true if the MBean is an instance of the specified class.
*
* @param name the name of the mbean
* @param className the className to test.
*
* @return true if the bean is an instance
*/
public boolean isInstanceOf(ObjectName name, String className)
throws InstanceNotFoundException
{
MBeanWrapper mbean = getMBean(name);
if (mbean == null)
throw new InstanceNotFoundException(String.valueOf(name));
Object obj = mbean.getObject();
Class<?> cl = obj.getClass();
return isInstanceOf(cl, className);
}
private boolean isInstanceOf(Class<?> cl, String className)
{
if (cl == null)
return false;
if (cl.getName().equals(className))
return true;
if (isInstanceOf(cl.getSuperclass(), className))
return true;
Class<?> []ifs = cl.getInterfaces();
for (int i = 0; i < ifs.length; i++) {
if (isInstanceOf(ifs[i], className))
return true;
}
return false;
}
/**
* Returns the ClassLoader that was used for loading the MBean.
*
* @param mbeanName the name of the mbean
*
* @return the class loader
*
* @since JMX 1.2
*/
public ClassLoader getClassLoaderFor(ObjectName name)
throws InstanceNotFoundException
{
MBeanWrapper mbean = getMBean(name);
if (mbean == null)
throw new InstanceNotFoundException(String.valueOf(name));
return mbean.getContext().getClassLoader();
}
/**
* Returns the named ClassLoader.
*
* @param loaderName the name of the class loader
*
* @return the class loader
*
* @since JMX 1.2
*/
public ClassLoader getClassLoader(ObjectName loaderName)
throws InstanceNotFoundException
{
return null;
}
/**
* Returns the ClassLoaderRepository for this MBeanServer
*
* @since JMX 1.2
*/
public ClassLoaderRepository getClassLoaderRepository()
{
return createContext().getClassLoaderRepository();
}
/**
* Deserializes a byte array in the class loader of the mbean.
*
* @param name the name of the mbean
* @param data the data to deserialize
*
* @return the deserialization stream
*/
public ObjectInputStream deserialize(ObjectName name, byte []data)
throws InstanceNotFoundException, OperationsException
{
throw new UnsupportedOperationException();
}
/**
* Deserializes a byte array in the class loader of the mbean.
*
* @param className the className matches to the loader
* @param data the data to deserialize
*
* @return the deserialization stream
*/
public ObjectInputStream deserialize(String className, byte []data)
throws OperationsException, ReflectionException
{
throw new UnsupportedOperationException();
}
/**
* Deserializes a byte array in the class loader of the mbean.
*
* @param className the className matches to the loader
* @param loaderName the loader to use for deserialization
* @param data the data to deserialize
*
* @return the deserialization stream
*/
public ObjectInputStream deserialize(String className,
ObjectName loaderName,
byte []data)
throws OperationsException, ReflectionException,
InstanceNotFoundException
{
throw new UnsupportedOperationException();
}
/**
* Returns the domains for all registered MBeans
*
* @since JMX 1.2
*/
public String []getDomains()
{
return getView().getDomains();
}
/**
* Finds the MBean implementation.
*/
MBeanWrapper getMBean(ObjectName name)
{
return getView().getMBean(name);
}
/**
* Handles the case where a class loader is dropped.
*/
public void destroy()
{
try {
MBeanServerFactory.releaseMBeanServer(this);
} catch (IllegalArgumentException e) {
log.log(Level.FINEST, e.toString(), e);
} catch (Throwable e) {
log.log(Level.FINER, e.toString(), e);
}
}
/**
* Returns the string form.
*/
@Override
public String toString()
{
if (_defaultDomain != null)
return "MBeanServerImpl[domain=" + _defaultDomain + "]";
else
return "MBeanServerImpl[]";
}
static {
try {
SERVER_DELEGATE_NAME = new ObjectName("JMImplementation:type=MBeanServerDelegate");
} catch (Throwable e) {
e.printStackTrace();
}
}
}