org.jboss.mx.metadata.StandardMetaData Maven / Gradle / Ivy
/*
* JBoss, Home of Professional Open Source.
* Copyright 2008, Red Hat Middleware LLC, and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.mx.metadata;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import javax.management.IntrospectionException;
import javax.management.MBeanAttributeInfo;
import javax.management.MBeanConstructorInfo;
import javax.management.MBeanInfo;
import javax.management.MBeanNotificationInfo;
import javax.management.MBeanOperationInfo;
import javax.management.NotCompliantMBeanException;
import javax.management.NotificationBroadcaster;
/**
* This metadata builder implementation builds a MBean info based on the
* naming rules of the Standard MBeans. The MBean server uses this builder
* to generate the metadata for Standard MBeans.
*
* In cooperation with the
* {@link MBeanInfoConversion#toModelMBeanInfo MBeanInfoConversion} class you
* can use this builder as a migration tool from Standard to Model MBeans, or
* for cases where you want the management interface be based on a compile-time
* type safe interface. It is also possible to subclass this builder
* implementation to extend it to support more sophisticated introspection rules
* such as adding descriptors to management interface elements.
*
* @author Juha Lindfors.
* @author Trevor Squires.
* @author Thomas Diesler.
* @author Dimitris Andreadis.
*/
public class StandardMetaData extends AbstractBuilder
{
// Attributes ----------------------------------------------------
/**
* The MBean object instance.
* Can be null.
*/
private Object mbeanInstance = null;
/**
* The class of the MBean instance.
*/
private Class mbeanClass = null;
/**
* The interface used as a basis for constructing the MBean metadata.
*/
private Class mbeanInterface = null;
// Static --------------------------------------------------------
/**
* Locates an interface for a class that matches the Standard MBean naming
* convention.
*
* @param mbeanClass the class to investigate
*
* @return the Standard MBean interface class or null if not found
*/
public static Class findStandardInterface(Class mbeanClass)
{
Class concrete = mbeanClass;
Class stdInterface = null;
while (null != concrete)
{
stdInterface = findStandardInterface(concrete, concrete.getInterfaces());
if (null != stdInterface)
{
return stdInterface;
}
concrete = concrete.getSuperclass();
}
return null;
}
private static Class findStandardInterface(Class concrete, Class[] interfaces)
{
String stdName = concrete.getName() + "MBean";
Class retval = null;
// look to see if this class implements MBean std interface
for (int i = 0; i < interfaces.length; ++i)
{
if (interfaces[i].getName().equals(stdName))
{
retval = interfaces[i];
break;
}
}
return retval;
}
// Constructors --------------------------------------------------
/**
* Initializes the Standard metadata builder. The JMX metadata is based
* on the class of the given resource instance.
*
* @param mbeanInstance MBean instance
*/
public StandardMetaData(Object mbeanInstance) throws NotCompliantMBeanException
{
this(mbeanInstance.getClass());
this.mbeanInstance = mbeanInstance;
}
/**
* Initializes the Standard metadata builder. The JMX metadata is based
* on the given class.
*
* @param mbeanClass resource class that implements an interface
* adhering to the Standard MBean naming conventions
*/
public StandardMetaData(Class mbeanClass) throws NotCompliantMBeanException
{
this.mbeanClass = mbeanClass;
this.mbeanInterface = StandardMetaData.findStandardInterface(mbeanClass);
if (this.mbeanInterface == null)
throw new NotCompliantMBeanException("Cannot obtain management interface for: " + mbeanClass);
}
/**
* Initializes the Standard metadata builder. The JMX metadata is based
* on the passed mbean interface.
*
* @param mbInstance MBean instance
* @param mbInterface the management interface
*/
public StandardMetaData(Object mbInstance, Class mbInterface) throws NotCompliantMBeanException
{
this.mbeanInstance = mbInstance;
this.mbeanClass = mbInstance.getClass();
this.mbeanInterface = mbInterface;
// search for it
if (this.mbeanInterface == null)
this.mbeanInterface = StandardMetaData.findStandardInterface(mbeanClass);
if (this.mbeanInterface == null)
throw new NotCompliantMBeanException("Cannot obtain management interface for: " + mbeanClass);
if (this.mbeanInterface.isInterface() == false)
throw new NotCompliantMBeanException("Management interface is not an interface: " + mbeanInterface);
}
/**
* Retrieve the management interface
*/
public Class getMBeanInterface()
{
return mbeanInterface;
}
// MetaDataBuilder implementation --------------------------------
public MBeanInfo build() throws NotCompliantMBeanException
{
try
{
// First check the mbean instance implements the interface
if (mbeanInterface == null)
throw new NotCompliantMBeanException("The mbean does not implement a management interface");
if (mbeanInstance != null && mbeanInterface.isInstance(mbeanInstance) == false)
throw new NotCompliantMBeanException("The mbean does not implement its management interface " +
mbeanInterface.getName());
// First build the constructors
Constructor[] constructors = mbeanClass.getConstructors();
MBeanConstructorInfo[] constructorInfo = new MBeanConstructorInfo[constructors.length];
for (int i = 0; i < constructors.length; ++i)
{
constructorInfo[i] = new MBeanConstructorInfo("MBean Constructor.", constructors[i]);
}
// Next we have to figure out how the methods in the mbean class map
// to attributes and operations
Method[] methods = mbeanInterface.getMethods();
HashMap getters = new HashMap();
HashMap setters = new HashMap();
HashMap operInfo = new HashMap();
List attrInfo = new ArrayList();
for (int i = 0; i < methods.length; ++i)
{
String methodName = methods[i].getName();
Class[] signature = methods[i].getParameterTypes();
Class returnType = methods[i].getReturnType();
if (methodName.startsWith("set") && methodName.length() > 3
&& signature.length == 1 && returnType == Void.TYPE)
{
String key = methodName.substring(3, methodName.length());
Method setter = (Method) setters.get(key);
if (setter != null && setter.getParameterTypes()[0].equals(signature[0]) == false)
{
throw new IntrospectionException("overloaded type for attribute set: " + key);
}
setters.put(key, methods[i]);
}
else if (methodName.startsWith("get") && methodName.length() > 3
&& signature.length == 0 && returnType != Void.TYPE)
{
String key = methodName.substring(3, methodName.length());
Method getter = (Method) getters.get(key);
if (getter != null && getter.getName().startsWith("is"))
{
throw new IntrospectionException("mixed use of get/is for attribute " + key);
}
getters.put(key, methods[i]);
}
else if (methodName.startsWith("is") && methodName.length() > 2
&& signature.length == 0 && isBooleanReturn(returnType))
{
String key = methodName.substring(2, methodName.length());
Method getter = (Method) getters.get(key);
if (getter != null && getter.getName().startsWith("get"))
{
throw new IntrospectionException("mixed use of get/is for attribute " + key);
}
getters.put(key, methods[i]);
}
else
{
MBeanOperationInfo info = new MBeanOperationInfo("MBean Operation.", methods[i]);
operInfo.put(getSignatureString(methods[i]), info);
}
}
Object[] keys = getters.keySet().toArray();
for (int i = 0; i < keys.length; ++i)
{
String attrName = (String) keys[i];
Method getter = (Method) getters.remove(attrName);
Method setter = (Method) setters.remove(attrName);
MBeanAttributeInfo info = new MBeanAttributeInfo(attrName, "MBean Attribute.", getter, setter);
attrInfo.add(info);
}
Iterator it = setters.keySet().iterator();
while (it.hasNext())
{
String attrName = (String) it.next();
Method setter = (Method) setters.get(attrName);
MBeanAttributeInfo info = new MBeanAttributeInfo(attrName, "MBean Attribute.", null, setter);
attrInfo.add(info);
}
// save away the attribute and operation info objects
MBeanAttributeInfo[] attributeInfo = (MBeanAttributeInfo[]) attrInfo.toArray(new MBeanAttributeInfo[0]);
MBeanOperationInfo[] operationInfo = (MBeanOperationInfo[]) operInfo.values().toArray(new MBeanOperationInfo[0]);
// if the builder was initialized with the resource instance, check if
// it is a notification broadcaster, and add the appropriate notifications
// to the interface.
MBeanNotificationInfo[] notifications = null;
if (mbeanInstance instanceof NotificationBroadcaster)
{
notifications = ((NotificationBroadcaster) mbeanInstance).getNotificationInfo();
}
else
{
notifications = new MBeanNotificationInfo[0];
}
return new MBeanInfo(mbeanClass.getName(), "Management Bean.",
attributeInfo, constructorInfo, operationInfo, notifications);
}
catch (IntrospectionException e)
{
throw new NotCompliantMBeanException(e.getMessage());
}
}
/**
* JMX standard specifies that only "boolean isX()" style methods
* represent attributes. "Boolean isX()" methods map to operations.
*/
private boolean isBooleanReturn(Class returnType)
{
return returnType == Boolean.TYPE;
}
protected String getSignatureString(Method method)
{
String name = method.getName();
Class[] signature = method.getParameterTypes();
StringBuffer buffer = new StringBuffer(512);
buffer.append(name);
buffer.append("(");
if (signature != null)
{
for (int i = 0; i < signature.length; i++)
{
buffer.append(signature[i].getName());
if (i < signature.length-1)
buffer.append(",");
}
}
buffer.append(")");
return buffer.toString();
}
}