Java tutorial
/** * Copyright 2011 Pedro Ribeiro * * 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. */ package org.jalphanode.jmx; import java.beans.BeanInfo; import java.beans.Introspector; import java.beans.MethodDescriptor; import java.beans.PropertyDescriptor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.text.MessageFormat; import java.util.List; import java.util.Map; import java.util.Set; import org.jalphanode.jmx.annotation.MBean; import org.jalphanode.jmx.annotation.ManagedAttribute; import org.jalphanode.jmx.annotation.ManagedOperation; import org.jalphanode.jmx.annotation.ManagedParameter; import org.jalphanode.util.ReflectionUtils; import com.google.common.base.Preconditions; import com.google.common.collect.Maps; import com.google.common.collect.Sets; /** * Extracts all MBean metadata from a class. * * @author pribeiro */ public class MBeanAnnotationScanner { public MBeanMetadata scan(final Class<?> klass) { Preconditions.checkNotNull(klass, "klass"); MBeanMetadata metadata = null; final MBean mBean = ReflectionUtils.getAnnotation(klass, MBean.class); if (mBean != null) { if (!Modifier.isPublic(klass.getModifiers())) { throw new MalformedMBeanException("MBean modifiers should be public!"); } final MBeanMetadata.Builder builder = new MBeanMetadata.Builder(klass); if (!mBean.objectName().isEmpty()) { builder.withObjectName(mBean.objectName()); } if (!mBean.description().isEmpty()) { builder.withDescription(mBean.description()); } try { final BeanInfo beanInfo = Introspector.getBeanInfo(klass, Object.class); buildAttributeMetadata(klass, beanInfo, builder); buildOperationMetadata(beanInfo, builder); metadata = builder.build(); } catch (java.beans.IntrospectionException e) { throw new MalformedMBeanException(e); } } return metadata; } protected void buildAttributeMetadata(final Class<?> klass, final BeanInfo beanInfo, final MBeanMetadata.Builder builder) { final List<Field> fields = ReflectionUtils.getAnnotatedFields(klass, ManagedAttribute.class); final Map<String, Field> indexFields = Maps.newHashMapWithExpectedSize(fields.size()); for (Field field : fields) { indexFields.put(field.getName(), field); } PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors(); final Set<String> attributes = Sets.newHashSetWithExpectedSize(propertyDescriptors.length); for (PropertyDescriptor property : propertyDescriptors) { final Field annotatedField = indexFields.remove(property.getName()); final Method readMethod = property.getReadMethod(); final Method writeMethod = property.getWriteMethod(); ManagedAttribute annotation = getManagedAttribute(property.getName(), annotatedField, readMethod, writeMethod); if (annotation != null) { final String name = annotation.name().isEmpty() ? property.getName() : annotation.name(); if (!attributes.add(name)) { throw new MalformedMBeanException( MessageFormat.format("Attribute with name {0} already registered", name)); } ManagedAttributeMetadata.Builder attributeBuilder = new ManagedAttributeMetadata.Builder(name); if (!annotation.description().isEmpty()) { attributeBuilder.withDescription(annotation.description()); } if (readMethod != null) { attributeBuilder.withReadMethod(readMethod); } if (writeMethod != null) { attributeBuilder.withWriteMethod(writeMethod); } builder.putAttribute(attributeBuilder.build()); } } if (!indexFields.isEmpty()) { throw new MalformedMBeanException( MessageFormat.format("Found attribute(s) without getters/setters: {0}", indexFields.keySet())); } } private ManagedAttribute getManagedAttribute(final String name, final Field annotatedField, final Method readMethod, final Method writeMethod) { int annotationCounter = 0; ManagedAttribute annotation = null; if (annotatedField != null) { annotation = annotatedField.getAnnotation(ManagedAttribute.class); annotationCounter++; } if (readMethod != null) { final ManagedAttribute getterAttribute = readMethod.getAnnotation(ManagedAttribute.class); if (getterAttribute != null) { annotation = getterAttribute; annotationCounter++; } } if (writeMethod != null) { final ManagedAttribute setterAttribute = writeMethod.getAnnotation(ManagedAttribute.class); if (setterAttribute != null) { annotation = setterAttribute; annotationCounter++; } } if (annotationCounter > 1) { throw new MalformedMBeanException( MessageFormat.format("Attribute {0} contains multiple annotations", name)); } return annotation; } protected void buildOperationMetadata(final BeanInfo beanInfo, final MBeanMetadata.Builder builder) { final Set<Method> allAccessors = allAccessors(beanInfo); final MethodDescriptor[] methodDescriptors = beanInfo.getMethodDescriptors(); final Set<String> operationMethods = Sets.newHashSetWithExpectedSize(methodDescriptors.length); for (MethodDescriptor descriptor : methodDescriptors) { Method method = descriptor.getMethod(); ManagedOperation operationAnnotation = method.getAnnotation(ManagedOperation.class); if (operationAnnotation != null) { if (allAccessors.contains(method)) { throw new MalformedMBeanException(MessageFormat.format("Accessor method {0} annotated as {1}", method.getName(), ManagedOperation.class.getName())); } int mod = method.getModifiers(); if (!Modifier.isPublic(mod) || Modifier.isStatic(mod)) { throw new MalformedMBeanException(MessageFormat .format("MBean operation {0} should be public and non static!", method.getName())); } final String name = operationAnnotation.name().isEmpty() ? method.getName() : operationAnnotation.name(); if (!operationMethods.add(name)) { throw new MalformedMBeanException( MessageFormat.format("Operation with name {0} already registered", name)); } ManagedOperationMetadata.Builder operationBuilder = new ManagedOperationMetadata.Builder(method); if (!operationAnnotation.name().isEmpty()) { operationBuilder.withName(operationAnnotation.name()); } if (!operationAnnotation.description().isEmpty()) { operationBuilder.withDescription(operationAnnotation.description()); } operationBuilder.withImpact(operationAnnotation.impact()); buildParameterMetadata(method, operationBuilder); builder.putOperation(operationBuilder.build()); } } } private Set<Method> allAccessors(final BeanInfo beanInfo) { final PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors(); final Set<Method> allAccessors = Sets.newHashSetWithExpectedSize(propertyDescriptors.length); for (PropertyDescriptor propertyDescriptor : propertyDescriptors) { Method getter = propertyDescriptor.getReadMethod(); if (getter != null) { allAccessors.add(getter); } Method setter = propertyDescriptor.getWriteMethod(); if (setter != null) { allAccessors.add(setter); } } return allAccessors; } protected void buildParameterMetadata(final Method method, final ManagedOperationMetadata.Builder builder) { final Class<?>[] parameters = method.getParameterTypes(); for (int parameterIndex = 0; parameterIndex < parameters.length; parameterIndex++) { final String pType = method.getParameterTypes()[parameterIndex].getName(); // locate parameter annotation ManagedParameter parameter = ReflectionUtils.getParameterAnnotation(method, parameterIndex, ManagedParameter.class); if (parameter != null) { ManagedParameterMetadata.Builder parameterBuilder = new ManagedParameterMetadata.Builder(pType); if (!parameter.name().isEmpty()) { parameterBuilder.withName(parameter.name()); } if (!parameter.description().isEmpty()) { parameterBuilder.wirhDescription(parameter.description()); } builder.addParameterMetadata(parameterBuilder.build()); } } } }