/* * (C) Copyright 2006-2008 Nuxeo SA (http://nuxeo.com/) and others. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * Contributors: * matic */ package org.nuxeo.runtime.management.inspector; import java.beans.BeanInfo; import java.beans.FeatureDescriptor; import java.beans.Introspector; import java.beans.MethodDescriptor; import java.beans.ParameterDescriptor; import java.beans.PropertyDescriptor; import java.lang.reflect.Method; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.management.Descriptor; import javax.management.IntrospectionException; import javax.management.modelmbean.DescriptorSupport; import javax.management.modelmbean.ModelMBeanAttributeInfo; import javax.management.modelmbean.ModelMBeanConstructorInfo; import javax.management.modelmbean.ModelMBeanInfo; import javax.management.modelmbean.ModelMBeanInfoSupport; import javax.management.modelmbean.ModelMBeanNotificationInfo; import javax.management.modelmbean.ModelMBeanOperationInfo; import org.nuxeo.runtime.management.ManagementRuntimeException; /** * @author matic */ public class ModelMBeanIntrospector { protected final Class<?> clazz; protected ModelMBeanInfo managementInfo; protected final Map<String, ModelMBeanAttributeInfo> attributesInfo = new HashMap<String, ModelMBeanAttributeInfo>(); protected final Map<String, ModelMBeanConstructorInfo> constructorsInfo = new HashMap<String, ModelMBeanConstructorInfo>(); protected final Map<String, ModelMBeanOperationInfo> operationsInfo = new HashMap<String, ModelMBeanOperationInfo>(); protected final Map<String, ModelMBeanNotificationInfo> notificationsInfo = new HashMap<String, ModelMBeanNotificationInfo>(); public ModelMBeanIntrospector(Class<?> clazz) { this.clazz = clazz; } ModelMBeanInfo introspect() { if (managementInfo != null) { return managementInfo; } // Collect ifaces Set<Class<?>> ifaces = new HashSet<Class<?>>(1); if (clazz.isInterface()) { ifaces.add(clazz); } else { doCollectMgmtIfaces(ifaces, clazz); if (ifaces.isEmpty()) { doCollectIfaces(ifaces, clazz); } } // Introspect for (Class<?> iface : ifaces) { BeanInfo beanInfo; try { beanInfo = Introspector.getBeanInfo(iface); } catch (java.beans.IntrospectionException e) { throw ManagementRuntimeException.wrap("Cannot introspect " + iface, e); } doCollectAttributes(iface, beanInfo); doCollectConstructors(iface, beanInfo); doCollectOperations(iface, beanInfo); doCollectNotifications(iface, beanInfo); } // Assemble model mbean infos managementInfo = new ModelMBeanInfoSupport(clazz.getCanonicalName(), "", attributesInfo.values().toArray( new ModelMBeanAttributeInfo[attributesInfo.size()]), constructorsInfo.values().toArray( new ModelMBeanConstructorInfo[constructorsInfo.size()]), operationsInfo.values().toArray( new ModelMBeanOperationInfo[operationsInfo.size()]), notificationsInfo.values().toArray( new ModelMBeanNotificationInfo[notificationsInfo.size()])); return managementInfo; } protected void doCollectMgmtIfaces(Set<Class<?>> ifaces, Class<?> clazz) { if (clazz == null) { return; } if (Object.class.equals(clazz)) { return; } for (Class<?> iface : clazz.getInterfaces()) { if (iface.getName().endsWith("MBean") || iface.getName().endsWith("MXBean")) { ifaces.add(iface); doCollectMgmtIfaces(ifaces, iface); } } doCollectMgmtIfaces(ifaces, clazz.getSuperclass()); } protected void doCollectIfaces(Set<Class<?>> ifaces, Class<?> clazz) { if (clazz == null) { return; } if (Object.class.equals(clazz)) { return; } for (Class<?> iface : clazz.getInterfaces()) { if (iface.getName().endsWith("MBean") || iface.getName().endsWith("MXBean")) { ifaces.clear(); ifaces.add(iface); return; } ifaces.add(iface); } doCollectIfaces(ifaces, clazz.getSuperclass()); } protected void doCollectNotifications(Class<?> clazz, BeanInfo info) { } protected void doCollectAttributes(Class<?> clazz, BeanInfo beanInfo) { for (PropertyDescriptor propertyInfo : beanInfo.getPropertyDescriptors()) { if (propertyInfo.isHidden()) { continue; } ModelMBeanAttributeInfo attributeInfo = null; try { Descriptor descriptor = doGetDescriptor(propertyInfo, "attribute"); Method readMethod = propertyInfo.getReadMethod(); Method writeMethod = propertyInfo.getWriteMethod(); if (readMethod != null) { descriptor.setField("getMethod", readMethod.getName()); } if (writeMethod != null) { descriptor.setField("setMethod", writeMethod.getName()); } attributeInfo = new ModelMBeanAttributeInfo(propertyInfo.getName(), propertyInfo.getShortDescription(), propertyInfo.getReadMethod(), propertyInfo.getWriteMethod(), descriptor); } catch (IntrospectionException e) { continue; } attributesInfo.put(attributeInfo.getName(), attributeInfo); } } protected void doCollectConstructors(Class<?> clazz, BeanInfo info) { } protected void doCollectOperations(Class<?> clazz, BeanInfo beanInfo) { for (MethodDescriptor methodInfo : beanInfo.getMethodDescriptors()) { if (methodInfo.isHidden()) { continue; } Descriptor descriptor = doGetDescriptor(methodInfo, "operation"); String name = methodInfo.getName(); Method method = methodInfo.getMethod(); ParameterDescriptor[] parameters = methodInfo.getParameterDescriptors(); boolean hasParameters = parameters != null && parameters.length > 0; Class<?> returnType = method.getReturnType(); boolean returnValue = returnType != null && !void.class.equals(returnType); if ((name.startsWith("get") && hasParameters && returnValue) || (name.startsWith("is") && !hasParameters && boolean.class.equals(returnType))) { descriptor.setField("role", "getter"); } else if (methodInfo.getName().startsWith("set") && void.class.equals(returnType) && hasParameters && parameters.length == 1) { // doFixAttribute(clazz, methodInfo.getName()); descriptor.setField("role", "setter"); } else { descriptor.setField("role", "operation"); } ModelMBeanOperationInfo operationInfo = new ModelMBeanOperationInfo(methodInfo.getShortDescription(), methodInfo.getMethod(), descriptor); operationsInfo.put(operationInfo.getName(), operationInfo); } } protected Descriptor doGetDescriptor(FeatureDescriptor info, String descriptorType) { Descriptor descriptor = new DescriptorSupport(); descriptor.setField("name", info.getName()); descriptor.setField("displayName", info.getDisplayName()); descriptor.setField("description", info.getShortDescription()); descriptor.setField("descriptorType", descriptorType); return descriptor; } private final Pattern attributePattern = Pattern.compile("(get|set|is)(.*)"); protected String doExtractMethodSuffix(String operationName) { Matcher matcher = attributePattern.matcher(operationName); if (!matcher.matches()) { throw new IllegalArgumentException(operationName + " does not match"); } return matcher.group(2); } }