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