001 /** 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 package org.apache.camel.management; 018 019 import java.lang.reflect.Method; 020 import java.lang.reflect.Proxy; 021 import java.util.HashMap; 022 import java.util.LinkedHashSet; 023 import java.util.Map; 024 import java.util.Set; 025 import javax.management.Descriptor; 026 import javax.management.IntrospectionException; 027 import javax.management.JMException; 028 import javax.management.modelmbean.ModelMBeanAttributeInfo; 029 import javax.management.modelmbean.ModelMBeanInfo; 030 import javax.management.modelmbean.ModelMBeanInfoSupport; 031 import javax.management.modelmbean.ModelMBeanNotificationInfo; 032 import javax.management.modelmbean.ModelMBeanOperationInfo; 033 034 import org.apache.camel.api.management.ManagedAttribute; 035 import org.apache.camel.api.management.ManagedNotification; 036 import org.apache.camel.api.management.ManagedNotifications; 037 import org.apache.camel.api.management.ManagedOperation; 038 import org.apache.camel.api.management.ManagedResource; 039 import org.apache.camel.util.IntrospectionSupport; 040 import org.apache.camel.util.ObjectHelper; 041 import org.slf4j.Logger; 042 import org.slf4j.LoggerFactory; 043 044 /** 045 * A Camel specific {@link javax.management.MBeanInfo} assembler that reads the 046 * details from the {@link ManagedResource}, {@link ManagedAttribute}, {@link ManagedOperation}, 047 * {@link ManagedNotification}, and {@link ManagedNotifications} annotations. 048 */ 049 public class MBeanInfoAssembler { 050 051 private static final Logger LOG = LoggerFactory.getLogger(MBeanInfoAssembler.class); 052 053 /** 054 * Gets the {@link ModelMBeanInfo} for the given managed bean 055 * 056 * @param defaultManagedBean the default managed bean 057 * @param customManagedBean an optional custom managed bean 058 * @param objectName the object name 059 * @return the model info, or <tt>null</tt> if not possible to create, for example due the managed bean is a proxy class 060 * @throws JMException is thrown if error creating the model info 061 */ 062 public ModelMBeanInfo getMBeanInfo(Object defaultManagedBean, Object customManagedBean, String objectName) throws JMException { 063 // skip proxy classes 064 if (Proxy.isProxyClass(defaultManagedBean.getClass())) { 065 LOG.trace("Skip creating ModelMBeanInfo due proxy class {}", defaultManagedBean.getClass()); 066 return null; 067 } 068 069 // maps and lists to contain information about attributes and operations 070 Map<String, ManagedAttributeInfo> attributes = new HashMap<String, ManagedAttributeInfo>(); 071 Set<ManagedOperationInfo> operations = new LinkedHashSet<ManagedOperationInfo>(); 072 Set<ModelMBeanAttributeInfo> mBeanAttributes = new LinkedHashSet<ModelMBeanAttributeInfo>(); 073 Set<ModelMBeanOperationInfo> mBeanOperations = new LinkedHashSet<ModelMBeanOperationInfo>(); 074 Set<ModelMBeanNotificationInfo> mBeanNotifications = new LinkedHashSet<ModelMBeanNotificationInfo>(); 075 076 // extract details from default managed bean 077 extractAttributesAndOperations(defaultManagedBean.getClass(), attributes, operations); 078 extractMbeanAttributes(defaultManagedBean, attributes, mBeanAttributes, mBeanOperations); 079 extractMbeanOperations(defaultManagedBean, operations, mBeanOperations); 080 extractMbeanNotifications(defaultManagedBean, mBeanNotifications); 081 082 // extract details from custom managed bean 083 if (customManagedBean != null) { 084 extractAttributesAndOperations(customManagedBean.getClass(), attributes, operations); 085 extractMbeanAttributes(customManagedBean, attributes, mBeanAttributes, mBeanOperations); 086 extractMbeanOperations(customManagedBean, operations, mBeanOperations); 087 extractMbeanNotifications(customManagedBean, mBeanNotifications); 088 } 089 090 // create the ModelMBeanInfo 091 String name = getName(customManagedBean != null ? customManagedBean : defaultManagedBean, objectName); 092 String description = getDescription(customManagedBean != null ? customManagedBean : defaultManagedBean, objectName); 093 ModelMBeanAttributeInfo[] arrayAttributes = mBeanAttributes.toArray(new ModelMBeanAttributeInfo[mBeanAttributes.size()]); 094 ModelMBeanOperationInfo[] arrayOperations = mBeanOperations.toArray(new ModelMBeanOperationInfo[mBeanOperations.size()]); 095 ModelMBeanNotificationInfo[] arrayNotifications = mBeanNotifications.toArray(new ModelMBeanNotificationInfo[mBeanNotifications.size()]); 096 097 ModelMBeanInfo info = new ModelMBeanInfoSupport(name, description, arrayAttributes, null, arrayOperations, arrayNotifications); 098 LOG.trace("Created ModelMBeanInfo {}", info); 099 return info; 100 } 101 102 private void extractAttributesAndOperations(Class<?> managedClass, Map<String, ManagedAttributeInfo> attributes, Set<ManagedOperationInfo> operations) { 103 // extract the class 104 doExtractAttributesAndOperations(managedClass, attributes, operations); 105 106 // and then any sub classes 107 if (managedClass.getSuperclass() != null) { 108 Class<?> clazz = managedClass.getSuperclass(); 109 // skip any JDK classes 110 if (!clazz.getName().startsWith("java")) { 111 LOG.trace("Extracting attributes and operations from sub class: {}", clazz); 112 extractAttributesAndOperations(clazz, attributes, operations); 113 } 114 } 115 116 // and then any additional interfaces (as interfaces can be annotated as well) 117 if (managedClass.getInterfaces() != null) { 118 for (Class<?> clazz : managedClass.getInterfaces()) { 119 // recursive as there may be multiple interfaces 120 if (clazz.getName().startsWith("java")) { 121 // skip any JDK classes 122 continue; 123 } 124 LOG.trace("Extracting attributes and operations from implemented interface: {}", clazz); 125 extractAttributesAndOperations(clazz, attributes, operations); 126 } 127 } 128 } 129 130 private void doExtractAttributesAndOperations(Class<?> managedClass, Map<String, ManagedAttributeInfo> attributes, Set<ManagedOperationInfo> operations) { 131 LOG.trace("Extracting attributes and operations from class: {}", managedClass); 132 for (Method method : managedClass.getDeclaredMethods()) { 133 LOG.trace("Extracting attributes and operations from method: {}", method); 134 135 ManagedAttribute ma = method.getAnnotation(ManagedAttribute.class); 136 if (ma != null) { 137 String key; 138 String desc = ma.description(); 139 Method getter = null; 140 Method setter = null; 141 142 if (IntrospectionSupport.isGetter(method)) { 143 key = IntrospectionSupport.getGetterShorthandName(method); 144 getter = method; 145 } else if (IntrospectionSupport.isSetter(method)) { 146 key = IntrospectionSupport.getSetterShorthandName(method); 147 setter = method; 148 } else { 149 throw new IllegalArgumentException("@ManagedAttribute can only be used on Java bean methods, was: " + method + " on bean: " + managedClass); 150 } 151 152 // they key must be capitalized 153 key = ObjectHelper.capitalize(key); 154 155 // lookup first 156 ManagedAttributeInfo info = attributes.get(key); 157 if (info == null) { 158 info = new ManagedAttributeInfo(key, desc); 159 } 160 if (getter != null) { 161 info.setGetter(getter); 162 } 163 if (setter != null) { 164 info.setSetter(setter); 165 } 166 167 attributes.put(key, info); 168 } 169 170 // operations 171 ManagedOperation mo = method.getAnnotation(ManagedOperation.class); 172 if (mo != null) { 173 String desc = mo.description(); 174 Method operation = method; 175 operations.add(new ManagedOperationInfo(desc, operation)); 176 } 177 } 178 } 179 180 private void extractMbeanAttributes(Object managedBean, Map<String, ManagedAttributeInfo> attributes, 181 Set<ModelMBeanAttributeInfo> mBeanAttributes, Set<ModelMBeanOperationInfo> mBeanOperations) throws IntrospectionException { 182 183 for (ManagedAttributeInfo info : attributes.values()) { 184 ModelMBeanAttributeInfo mbeanAttribute = new ModelMBeanAttributeInfo(info.getKey(), info.getDescription(), info.getGetter(), info.getSetter()); 185 186 // add missing attribute descriptors, this is needed to have attributes accessible 187 Descriptor desc = mbeanAttribute.getDescriptor(); 188 if (info.getGetter() != null) { 189 desc.setField("getMethod", info.getGetter().getName()); 190 // attribute must also be added as mbean operation 191 ModelMBeanOperationInfo mbeanOperation = new ModelMBeanOperationInfo(info.getKey(), info.getGetter()); 192 mBeanOperations.add(mbeanOperation); 193 } 194 if (info.getSetter() != null) { 195 desc.setField("setMethod", info.getSetter().getName()); 196 // attribute must also be added as mbean operation 197 ModelMBeanOperationInfo mbeanOperation = new ModelMBeanOperationInfo(info.getKey(), info.getSetter()); 198 mBeanOperations.add(mbeanOperation); 199 } 200 mbeanAttribute.setDescriptor(desc); 201 202 mBeanAttributes.add(mbeanAttribute); 203 LOG.trace("Assembled attribute: {}", mbeanAttribute); 204 } 205 } 206 207 private void extractMbeanOperations(Object managedBean, Set<ManagedOperationInfo> operations, Set<ModelMBeanOperationInfo> mBeanOperations) { 208 for (ManagedOperationInfo info : operations) { 209 ModelMBeanOperationInfo mbean = new ModelMBeanOperationInfo(info.getDescription(), info.getOperation()); 210 mBeanOperations.add(mbean); 211 LOG.trace("Assembled operation: {}", mbean); 212 } 213 } 214 215 private void extractMbeanNotifications(Object managedBean, Set<ModelMBeanNotificationInfo> mBeanNotifications) { 216 ManagedNotifications notifications = managedBean.getClass().getAnnotation(ManagedNotifications.class); 217 if (notifications != null) { 218 for (ManagedNotification notification : notifications.value()) { 219 ModelMBeanNotificationInfo info = new ModelMBeanNotificationInfo(notification.notificationTypes(), notification.name(), notification.description()); 220 mBeanNotifications.add(info); 221 LOG.trace("Assembled notification: {}", info); 222 } 223 } 224 } 225 226 private String getDescription(Object managedBean, String objectName) { 227 ManagedResource mr = ObjectHelper.getAnnotation(managedBean, ManagedResource.class); 228 return mr != null ? mr.description() : ""; 229 } 230 231 private String getName(Object managedBean, String objectName) { 232 return managedBean.getClass().getName(); 233 } 234 235 private static final class ManagedAttributeInfo { 236 private String key; 237 private String description; 238 private Method getter; 239 private Method setter; 240 241 private ManagedAttributeInfo(String key, String description) { 242 this.key = key; 243 this.description = description; 244 } 245 246 public String getKey() { 247 return key; 248 } 249 250 public String getDescription() { 251 return description; 252 } 253 254 public Method getGetter() { 255 return getter; 256 } 257 258 public void setGetter(Method getter) { 259 this.getter = getter; 260 } 261 262 public Method getSetter() { 263 return setter; 264 } 265 266 public void setSetter(Method setter) { 267 this.setter = setter; 268 } 269 270 @Override 271 public String toString() { 272 return "ManagedAttributeInfo: [" + key + " + getter: " + getter + ", setter: " + setter + "]"; 273 } 274 } 275 276 private static final class ManagedOperationInfo { 277 private final String description; 278 private final Method operation; 279 280 private ManagedOperationInfo(String description, Method operation) { 281 this.description = description; 282 this.operation = operation; 283 } 284 285 public String getDescription() { 286 return description; 287 } 288 289 public Method getOperation() { 290 return operation; 291 } 292 293 @Override 294 public String toString() { 295 return "ManagedOperationInfo: [" + operation + "]"; 296 } 297 } 298 299 }