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