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.activemq.broker.jmx;
018    
019    import java.lang.annotation.Annotation;
020    import java.lang.reflect.Method;
021    import java.util.HashMap;
022    import java.util.Map;
023    
024    import javax.management.InstanceAlreadyExistsException;
025    import javax.management.MBeanAttributeInfo;
026    import javax.management.MBeanOperationInfo;
027    import javax.management.MBeanParameterInfo;
028    import javax.management.MBeanRegistrationException;
029    import javax.management.MBeanServer;
030    import javax.management.NotCompliantMBeanException;
031    import javax.management.ObjectName;
032    import javax.management.StandardMBean;
033    
034    /**
035     * MBean that looks for method/parameter descriptions in the Info annotation.
036     */
037    public class AnnotatedMBean extends StandardMBean {
038    
039      private static final Map<String, Class<?>> primitives = new HashMap<String, Class<?>>();
040      static {
041        Class<?>[] p = { byte.class, short.class, int.class, long.class, float.class, double.class, char.class, boolean.class, };
042        for (Class<?> c : p)
043          primitives.put(c.getName(), c);
044      }
045      
046      @SuppressWarnings("unchecked")
047      public static void registerMBean(ManagementContext context, Object object, ObjectName objectName) 
048        throws Exception {
049    
050        String mbeanName = object.getClass().getName() + "MBean";
051        
052        for (Class c : object.getClass().getInterfaces()) {
053          if (mbeanName.equals(c.getName())) {
054            context.registerMBean(new AnnotatedMBean(object, c), objectName);
055            return;
056          }
057        }
058    
059        context.registerMBean(object, objectName);
060      }
061      
062      /** Instance where the MBean interface is implemented by another object. */
063      public <T> AnnotatedMBean(T impl, Class<T> mbeanInterface) throws NotCompliantMBeanException {
064        super(impl, mbeanInterface);
065      }
066    
067      /** Instance where the MBean interface is implemented by this object. */
068      protected AnnotatedMBean(Class<?> mbeanInterface) throws NotCompliantMBeanException {
069        super(mbeanInterface);
070      }
071    
072      /** {@inheritDoc} */
073      @Override
074      protected String getDescription(MBeanAttributeInfo info) {
075    
076        String descr = info.getDescription();
077        Method m = getMethod(getMBeanInterface(), "get"+info.getName().substring(0, 1).toUpperCase()+info.getName().substring(1));
078        if (m == null)
079          m = getMethod(getMBeanInterface(), "is"+info.getName().substring(0, 1).toUpperCase()+info.getName().substring(1));
080        if (m == null)
081          m = getMethod(getMBeanInterface(), "does"+info.getName().substring(0, 1).toUpperCase()+info.getName().substring(1));
082          
083        if (m != null) {
084          MBeanInfo d = m.getAnnotation(MBeanInfo.class);
085          if (d != null)
086            descr = d.value();
087        }
088        return descr;
089      }
090      
091      /** {@inheritDoc} */
092      @Override
093      protected String getDescription(MBeanOperationInfo op) {
094    
095        String descr = op.getDescription();
096        Method m = getMethod(op);
097        if (m != null) {
098          MBeanInfo d = m.getAnnotation(MBeanInfo.class);
099          if (d != null)
100            descr = d.value();
101        }
102        return descr;
103      }
104    
105      /** {@inheritDoc} */
106      @Override
107      protected String getParameterName(MBeanOperationInfo op, MBeanParameterInfo param, int paramNo) {
108        String name = param.getName();
109        Method m = getMethod(op);
110        if (m != null) {
111          for (Annotation a : m.getParameterAnnotations()[paramNo]) {
112            if (MBeanInfo.class.isInstance(a))
113              name = MBeanInfo.class.cast(a).value();
114          }
115        }
116        return name;
117      }
118    
119      /**
120       * Extracts the Method from the MBeanOperationInfo
121       * @param op
122       * @return
123       */
124      private Method getMethod(MBeanOperationInfo op) {
125        final MBeanParameterInfo[] params = op.getSignature();
126        final String[] paramTypes = new String[params.length];
127        for (int i = 0; i < params.length; i++)
128          paramTypes[i] = params[i].getType();
129    
130        return getMethod(getMBeanInterface(), op.getName(), paramTypes);
131      }
132    
133      /**
134       * Returns the Method with the specified name and parameter types for the given class,
135       * null if it doesn't exist.
136       * @param mbean
137       * @param method
138       * @param params
139       * @return
140       */
141      private static Method getMethod(Class<?> mbean, String method, String... params) {
142        try {
143          final ClassLoader loader = mbean.getClassLoader();
144          final Class<?>[] paramClasses = new Class<?>[params.length];
145          for (int i = 0; i < params.length; i++) {
146            paramClasses[i] = primitives.get(params[i]);
147            if (paramClasses[i] == null)
148              paramClasses[i] = Class.forName(params[i], false, loader);
149          }
150          return mbean.getMethod(method, paramClasses);
151        } catch (RuntimeException e) {
152          throw e;
153        } catch (Exception e) {
154          return null;
155        }
156      }
157    }