001/* 002 * (C) Copyright 2006-2008 Nuxeo SAS (http://nuxeo.com/) and contributors. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the GNU Lesser General Public License 006 * (LGPL) version 2.1 which accompanies this distribution, and is available at 007 * http://www.gnu.org/licenses/lgpl.html 008 * 009 * This library is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 012 * Lesser General Public License for more details. 013 * 014 * Contributors: 015 * matic 016 */ 017package org.nuxeo.runtime.management.inspector; 018 019import java.beans.BeanInfo; 020import java.beans.FeatureDescriptor; 021import java.beans.Introspector; 022import java.beans.MethodDescriptor; 023import java.beans.ParameterDescriptor; 024import java.beans.PropertyDescriptor; 025import java.lang.reflect.Method; 026import java.util.HashMap; 027import java.util.HashSet; 028import java.util.Map; 029import java.util.Set; 030import java.util.regex.Matcher; 031import java.util.regex.Pattern; 032 033import javax.management.Descriptor; 034import javax.management.IntrospectionException; 035import javax.management.modelmbean.DescriptorSupport; 036import javax.management.modelmbean.ModelMBeanAttributeInfo; 037import javax.management.modelmbean.ModelMBeanConstructorInfo; 038import javax.management.modelmbean.ModelMBeanInfo; 039import javax.management.modelmbean.ModelMBeanInfoSupport; 040import javax.management.modelmbean.ModelMBeanNotificationInfo; 041import javax.management.modelmbean.ModelMBeanOperationInfo; 042 043import org.nuxeo.runtime.management.ManagementRuntimeException; 044 045/** 046 * @author matic 047 */ 048public class ModelMBeanIntrospector { 049 050 protected final Class<?> clazz; 051 052 protected ModelMBeanInfo managementInfo; 053 054 protected final Map<String, ModelMBeanAttributeInfo> attributesInfo = new HashMap<String, ModelMBeanAttributeInfo>(); 055 056 protected final Map<String, ModelMBeanConstructorInfo> constructorsInfo = new HashMap<String, ModelMBeanConstructorInfo>(); 057 058 protected final Map<String, ModelMBeanOperationInfo> operationsInfo = new HashMap<String, ModelMBeanOperationInfo>(); 059 060 protected final Map<String, ModelMBeanNotificationInfo> notificationsInfo = new HashMap<String, ModelMBeanNotificationInfo>(); 061 062 public ModelMBeanIntrospector(Class<?> clazz) { 063 this.clazz = clazz; 064 } 065 066 ModelMBeanInfo introspect() { 067 if (managementInfo != null) { 068 return managementInfo; 069 } 070 071 // Collect ifaces 072 Set<Class<?>> ifaces = new HashSet<Class<?>>(1); 073 if (clazz.isInterface()) { 074 ifaces.add(clazz); 075 } else { 076 doCollectMgmtIfaces(ifaces, clazz); 077 if (ifaces.isEmpty()) { 078 doCollectIfaces(ifaces, clazz); 079 } 080 } 081 082 // Introspect 083 for (Class<?> iface : ifaces) { 084 BeanInfo beanInfo; 085 try { 086 beanInfo = Introspector.getBeanInfo(iface); 087 } catch (java.beans.IntrospectionException e) { 088 throw ManagementRuntimeException.wrap("Cannot introspect " + iface, e); 089 } 090 doCollectAttributes(iface, beanInfo); 091 doCollectConstructors(iface, beanInfo); 092 doCollectOperations(iface, beanInfo); 093 doCollectNotifications(iface, beanInfo); 094 } 095 096 // Assemble model mbean infos 097 managementInfo = new ModelMBeanInfoSupport(clazz.getCanonicalName(), "", attributesInfo.values().toArray( 098 new ModelMBeanAttributeInfo[attributesInfo.size()]), constructorsInfo.values().toArray( 099 new ModelMBeanConstructorInfo[constructorsInfo.size()]), operationsInfo.values().toArray( 100 new ModelMBeanOperationInfo[operationsInfo.size()]), notificationsInfo.values().toArray( 101 new ModelMBeanNotificationInfo[notificationsInfo.size()])); 102 103 return managementInfo; 104 } 105 106 protected void doCollectMgmtIfaces(Set<Class<?>> ifaces, Class<?> clazz) { 107 if (clazz == null) { 108 return; 109 } 110 if (Object.class.equals(clazz)) { 111 return; 112 } 113 for (Class<?> iface : clazz.getInterfaces()) { 114 if (iface.getName().endsWith("MBean") || iface.getName().endsWith("MXBean")) { 115 ifaces.add(iface); 116 doCollectMgmtIfaces(ifaces, iface); 117 } 118 } 119 doCollectMgmtIfaces(ifaces, clazz.getSuperclass()); 120 } 121 122 protected void doCollectIfaces(Set<Class<?>> ifaces, Class<?> clazz) { 123 if (clazz == null) { 124 return; 125 } 126 if (Object.class.equals(clazz)) { 127 return; 128 } 129 for (Class<?> iface : clazz.getInterfaces()) { 130 if (iface.getName().endsWith("MBean") || iface.getName().endsWith("MXBean")) { 131 ifaces.clear(); 132 ifaces.add(iface); 133 return; 134 } 135 ifaces.add(iface); 136 } 137 doCollectIfaces(ifaces, clazz.getSuperclass()); 138 } 139 140 protected void doCollectNotifications(Class<?> clazz, BeanInfo info) { 141 } 142 143 protected void doCollectAttributes(Class<?> clazz, BeanInfo beanInfo) { 144 for (PropertyDescriptor propertyInfo : beanInfo.getPropertyDescriptors()) { 145 if (propertyInfo.isHidden()) { 146 continue; 147 } 148 ModelMBeanAttributeInfo attributeInfo = null; 149 try { 150 Descriptor descriptor = doGetDescriptor(propertyInfo, "attribute"); 151 Method readMethod = propertyInfo.getReadMethod(); 152 Method writeMethod = propertyInfo.getWriteMethod(); 153 if (readMethod != null) { 154 descriptor.setField("getMethod", readMethod.getName()); 155 } 156 if (writeMethod != null) { 157 descriptor.setField("setMethod", writeMethod.getName()); 158 } 159 attributeInfo = new ModelMBeanAttributeInfo(propertyInfo.getName(), propertyInfo.getShortDescription(), 160 propertyInfo.getReadMethod(), propertyInfo.getWriteMethod(), descriptor); 161 162 } catch (IntrospectionException e) { 163 continue; 164 } 165 attributesInfo.put(attributeInfo.getName(), attributeInfo); 166 } 167 } 168 169 protected void doCollectConstructors(Class<?> clazz, BeanInfo info) { 170 } 171 172 protected void doCollectOperations(Class<?> clazz, BeanInfo beanInfo) { 173 for (MethodDescriptor methodInfo : beanInfo.getMethodDescriptors()) { 174 if (methodInfo.isHidden()) { 175 continue; 176 } 177 Descriptor descriptor = doGetDescriptor(methodInfo, "operation"); 178 String name = methodInfo.getName(); 179 Method method = methodInfo.getMethod(); 180 ParameterDescriptor[] parameters = methodInfo.getParameterDescriptors(); 181 boolean hasParameters = parameters != null && parameters.length > 0; 182 Class<?> returnType = method.getReturnType(); 183 boolean returnValue = returnType != null && !void.class.equals(returnType); 184 if ((name.startsWith("get") && hasParameters && returnValue) 185 || (name.startsWith("is") && !hasParameters && boolean.class.equals(returnType))) { 186 descriptor.setField("role", "getter"); 187 } else if (methodInfo.getName().startsWith("set") && void.class.equals(returnType) && hasParameters 188 && parameters.length == 1) { 189 // doFixAttribute(clazz, methodInfo.getName()); 190 descriptor.setField("role", "setter"); 191 } else { 192 descriptor.setField("role", "operation"); 193 } 194 ModelMBeanOperationInfo operationInfo = new ModelMBeanOperationInfo(methodInfo.getShortDescription(), 195 methodInfo.getMethod(), descriptor); 196 operationsInfo.put(operationInfo.getName(), operationInfo); 197 } 198 } 199 200 protected Descriptor doGetDescriptor(FeatureDescriptor info, String descriptorType) { 201 Descriptor descriptor = new DescriptorSupport(); 202 descriptor.setField("name", info.getName()); 203 descriptor.setField("displayName", info.getDisplayName()); 204 descriptor.setField("description", info.getShortDescription()); 205 descriptor.setField("descriptorType", descriptorType); 206 return descriptor; 207 } 208 209 private final Pattern attributePattern = Pattern.compile("(get|set|is)(.*)"); 210 211 protected String doExtractMethodSuffix(String operationName) { 212 Matcher matcher = attributePattern.matcher(operationName); 213 if (!matcher.matches()) { 214 throw new IllegalArgumentException(operationName + " does not match"); 215 } 216 return matcher.group(2); 217 } 218 219}