org.eclipse.jetty.jmx.ObjectMBean Maven / Gradle / Ivy
The newest version!
//
// ========================================================================
// Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.jmx;
import java.lang.annotation.Annotation;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import javax.management.Attribute;
import javax.management.AttributeList;
import javax.management.AttributeNotFoundException;
import javax.management.DynamicMBean;
import javax.management.InvalidAttributeValueException;
import javax.management.MBeanAttributeInfo;
import javax.management.MBeanConstructorInfo;
import javax.management.MBeanException;
import javax.management.MBeanInfo;
import javax.management.MBeanNotificationInfo;
import javax.management.MBeanOperationInfo;
import javax.management.MBeanParameterInfo;
import javax.management.ObjectName;
import javax.management.ReflectionException;
import javax.management.modelmbean.ModelMBean;
import org.eclipse.jetty.util.Loader;
import org.eclipse.jetty.util.TypeUtil;
import org.eclipse.jetty.util.annotation.ManagedAttribute;
import org.eclipse.jetty.util.annotation.ManagedObject;
import org.eclipse.jetty.util.annotation.ManagedOperation;
import org.eclipse.jetty.util.annotation.Name;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
/* ------------------------------------------------------------ */
/** ObjectMBean.
* A dynamic MBean that can wrap an arbitary Object instance.
* the attributes and methods exposed by this bean are controlled by
* the merge of property bundles discovered by names related to all
* superclasses and all superinterfaces.
*
* Attributes and methods exported may be "Object" and must exist on the
* wrapped object, or "MBean" and must exist on a subclass of OBjectMBean
* or "MObject" which exists on the wrapped object, but whose values are
* converted to MBean object names.
*
*/
public class ObjectMBean implements DynamicMBean
{
private static final Logger LOG = Log.getLogger(ObjectMBean.class);
private static Class>[] OBJ_ARG = new Class[]{Object.class};
protected Object _managed;
private MBeanInfo _info;
private Map _getters=new HashMap();
private Map _setters=new HashMap();
private Map _methods=new HashMap();
// set of attributes mined from influence hierarchy
private Set _attributes = new HashSet();
// set of attributes that are automatically converted to ObjectName
// as they represent other managed beans which can be linked to
private Set _convert=new HashSet();
private ClassLoader _loader;
private MBeanContainer _mbeanContainer;
private static String OBJECT_NAME_CLASS = ObjectName.class.getName();
private static String OBJECT_NAME_ARRAY_CLASS = ObjectName[].class.getName();
/* ------------------------------------------------------------ */
/**
* Create MBean for Object. Attempts to create an MBean for the object by searching the package
* and class name space. For example an object of the type
*
*
* class com.acme.MyClass extends com.acme.util.BaseClass implements com.acme.Iface
*
*
* Then this method would look for the following classes:
*
* - com.acme.jmx.MyClassMBean
*
- com.acme.util.jmx.BaseClassMBean
*
- org.eclipse.jetty.jmx.ObjectMBean
*
*
* @param o The object
* @return A new instance of an MBean for the object or null.
*/
public static Object mbeanFor(Object o)
{
try
{
Class> oClass = o.getClass();
Object mbean = null;
while ( mbean == null && oClass != null )
{
String pName = oClass.getPackage().getName();
String cName = oClass.getName().substring(pName.length() + 1);
String mName = pName + ".jmx." + cName + "MBean";
try
{
Class> mClass = (Object.class.equals(oClass))?oClass=ObjectMBean.class:Loader.loadClass(oClass,mName,true);
LOG.debug("ObjectMbean: mbeanFor {} mClass={}", o, mClass);
try
{
Constructor> constructor = mClass.getConstructor(OBJ_ARG);
mbean=constructor.newInstance(new Object[]{o});
}
catch(Exception e)
{
LOG.ignore(e);
if (ModelMBean.class.isAssignableFrom(mClass))
{
mbean=mClass.newInstance();
((ModelMBean)mbean).setManagedResource(o, "objectReference");
}
}
LOG.debug("mbeanFor {} is {}", o, mbean);
return mbean;
}
catch (ClassNotFoundException e)
{
// The code below was modified to fix bugs 332200 and JETTY-1416
// The issue was caused by additional information added to the
// message after the class name when running in Apache Felix,
// as well as before the class name when running in JBoss.
if (e.getMessage().contains(mName))
LOG.ignore(e);
else
LOG.warn(e);
}
catch (Error e)
{
LOG.warn(e);
mbean = null;
}
catch (Exception e)
{
LOG.warn(e);
mbean = null;
}
oClass = oClass.getSuperclass();
}
}
catch (Exception e)
{
LOG.ignore(e);
}
return null;
}
public ObjectMBean(Object managedObject)
{
_managed = managedObject;
_loader = Thread.currentThread().getContextClassLoader();
}
public Object getManagedObject()
{
return _managed;
}
public ObjectName getObjectName()
{
return null;
}
public String getObjectContextBasis()
{
return null;
}
public String getObjectNameBasis()
{
return null;
}
protected void setMBeanContainer(MBeanContainer container)
{
this._mbeanContainer = container;
}
public MBeanContainer getMBeanContainer ()
{
return this._mbeanContainer;
}
public MBeanInfo getMBeanInfo()
{
try
{
if (_info==null)
{
// Start with blank lazy lists attributes etc.
String desc=null;
List attributes = new ArrayList();
List constructors = new ArrayList();
List operations = new ArrayList();
List notifications = new ArrayList();
// Find list of classes that can influence the mbean
Class> o_class=_managed.getClass();
List> influences = findInfluences(new ArrayList>(), _managed.getClass());
LOG.debug("Influence Count: {}", influences.size() );
// Process Type Annotations
ManagedObject primary = o_class.getAnnotation( ManagedObject.class);
if ( primary != null )
{
desc = primary.value();
}
else
{
LOG.debug("No @ManagedObject declared on {}", _managed.getClass());
}
// For each influence
for (int i=0;i oClass = influences.get(i);
ManagedObject typeAnnotation = oClass.getAnnotation( ManagedObject.class );
LOG.debug("Influenced by: " + oClass.getCanonicalName() );
if ( typeAnnotation == null )
{
LOG.debug("Annotations not found for: {}", oClass.getCanonicalName() );
continue;
}
// Process Method Annotations
for (Method method : oClass.getDeclaredMethods())
{
ManagedAttribute methodAttributeAnnotation = method.getAnnotation(ManagedAttribute.class);
if (methodAttributeAnnotation != null)
{
// TODO sort out how a proper name could get here, its a method name as an attribute at this point.
LOG.debug("Attribute Annotation found for: {}", method.getName());
MBeanAttributeInfo mai = defineAttribute(method,methodAttributeAnnotation);
if ( mai != null )
{
attributes.add(mai);
}
}
ManagedOperation methodOperationAnnotation = method.getAnnotation(ManagedOperation.class);
if (methodOperationAnnotation != null)
{
LOG.debug("Method Annotation found for: {}", method.getName());
MBeanOperationInfo oi = defineOperation(method,methodOperationAnnotation);
if (oi != null)
{
operations.add(oi);
}
}
}
}
_info = new MBeanInfo(o_class.getName(),
desc,
(MBeanAttributeInfo[])attributes.toArray(new MBeanAttributeInfo[attributes.size()]),
(MBeanConstructorInfo[])constructors.toArray(new MBeanConstructorInfo[constructors.size()]),
(MBeanOperationInfo[])operations.toArray(new MBeanOperationInfo[operations.size()]),
(MBeanNotificationInfo[])notifications.toArray(new MBeanNotificationInfo[notifications.size()]));
}
}
catch(RuntimeException e)
{
LOG.warn(e);
throw e;
}
return _info;
}
/* ------------------------------------------------------------ */
public Object getAttribute(String name) throws AttributeNotFoundException, MBeanException, ReflectionException
{
Method getter = (Method) _getters.get(name);
if (getter == null)
{
throw new AttributeNotFoundException(name);
}
try
{
Object o = _managed;
if (getter.getDeclaringClass().isInstance(this))
o = this; // mbean method
// get the attribute
Object r=getter.invoke(o, (java.lang.Object[]) null);
// convert to ObjectName if the type has the @ManagedObject annotation
if (r!=null )
{
if (r.getClass().isArray())
{
if (r.getClass().getComponentType().isAnnotationPresent(ManagedObject.class))
{
ObjectName[] on = new ObjectName[Array.getLength(r)];
for (int i = 0; i < on.length; i++)
{
on[i] = _mbeanContainer.findMBean(Array.get(r,i));
}
r = on;
}
}
else if (r instanceof Collection>)
{
@SuppressWarnings("unchecked")
Collection