MBeanIntrospector.java
001 /*
002  *
003  * Licensed to the Apache Software Foundation (ASF) under one
004  * or more contributor license agreements.  See the NOTICE file
005  * distributed with this work for additional information
006  * regarding copyright ownership.  The ASF licenses this file
007  * to you under the Apache License, Version 2.0 (the
008  * "License"); you may not use this file except in compliance
009  * with the License.  You may obtain a copy of the License at
010  
011  *   http://www.apache.org/licenses/LICENSE-2.0
012  
013  * Unless required by applicable law or agreed to in writing,
014  * software distributed under the License is distributed on an
015  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
016  * KIND, either express or implied.  See the License for the
017  * specific language governing permissions and limitations
018  * under the License.
019  *
020  */
021 package org.apache.qpid.server.management;
022 
023 import java.lang.annotation.Annotation;
024 import java.lang.reflect.Constructor;
025 import java.lang.reflect.Method;
026 import java.util.ArrayList;
027 import java.util.List;
028 
029 import javax.management.MBeanAttributeInfo;
030 import javax.management.MBeanConstructorInfo;
031 import javax.management.MBeanOperationInfo;
032 import javax.management.MBeanParameterInfo;
033 import javax.management.NotCompliantMBeanException;
034 
035 /**
036  * This class is a utility class to introspect the MBean class and the management
037  * interface class for various purposes.
038  @author  Bhupendra Bhardwaj
039  @version 0.1
040  */
041 class MBeanIntrospector {
042 
043     private static final String _defaultAttributeDescription = "Management attribute";
044     private static final String _defaultOerationDescription = "Management operation";
045     private static final String _defaultConstructorDescription = "MBean constructor";
046     private static final String _defaultMbeanDescription = "Management interface of the MBean";
047 
048     /**
049      * Introspects the management interface class for MBean attributes.
050      @param interfaceClass
051      @return MBeanAttributeInfo[]
052      @throws NotCompliantMBeanException
053      */
054     static MBeanAttributeInfo[] getMBeanAttributesInfo(Class interfaceClass)
055         throws NotCompliantMBeanException
056     {
057         List<MBeanAttributeInfo> attributesList = new ArrayList<MBeanAttributeInfo>();
058 
059         /**
060          * Using reflection, all methods of the managemetn interface will be analysed,
061          * and MBeanInfo will be created.
062          */
063         for (Method method : interfaceClass.getMethods())
064         {
065             String name = method.getName();
066             Class<?>  resultType = method.getReturnType();
067             MBeanAttributeInfo attributeInfo = null;
068 
069             if (isAttributeGetterMethod(method))
070             {
071                 String desc = getAttributeDescription(method);
072                 attributeInfo = new MBeanAttributeInfo(name.substring(3),
073                                                 resultType.getName(),
074                                                 desc,
075                                                 true,
076                                                 false,
077                                                 false);
078                 int index = getIndexIfAlreadyExists(attributeInfo, attributesList);
079                 if (index == -1)
080                 {
081                     attributesList.add(attributeInfo);
082                 }
083                 else
084                 {
085                     attributeInfo = new MBeanAttributeInfo(name.substring(3),
086                                                 resultType.getName(),
087                                                 desc,
088                                                 true,
089                                                 true,
090                                                 false);
091                     attributesList.set(index, attributeInfo);
092                 }
093             }
094             else if (isAttributeSetterMethod(method))
095             {
096                 String desc = getAttributeDescription(method);
097                 attributeInfo = new MBeanAttributeInfo(name.substring(3),
098                                                 method.getParameterTypes()[0].getName(),
099                                                 desc,
100                                                 false,
101                                                 true,
102                                                 false);
103                 int index = getIndexIfAlreadyExists(attributeInfo, attributesList);
104                 if (index == -1)
105                 {
106                     attributesList.add(attributeInfo);
107                 }
108                 else
109                 {
110                     attributeInfo = new MBeanAttributeInfo(name.substring(3),
111                                                 method.getParameterTypes()[0].getName(),
112                                                 desc,
113                                                 true,
114                                                 true,
115                                                 false);
116                     attributesList.set(index, attributeInfo);
117                 }
118             }
119             else if (isAttributeBoolean(method))
120             {
121                 attributeInfo = new MBeanAttributeInfo(name.substring(2),
122                                                 resultType.getName(),
123                                                 getAttributeDescription(method),
124                                                 true,
125                                                 false,
126                                                 true);
127                 attributesList.add(attributeInfo);
128             }
129         }
130 
131         return attributesList.toArray(new MBeanAttributeInfo[0]);
132     }
133 
134     /**
135      * Introspects the management interface class for management operations.
136      @param interfaceClass
137      @return MBeanOperationInfo[]
138      */
139     static MBeanOperationInfo[] getMBeanOperationsInfo(Class interfaceClass)
140     {
141         List<MBeanOperationInfo> operationsList = new ArrayList<MBeanOperationInfo>();
142 
143         for (Method method : interfaceClass.getMethods())
144         {
145             if (!isAttributeGetterMethod(method&&
146                 !isAttributeSetterMethod(method&&
147                 !isAttributeBoolean(method))
148             {
149                 operationsList.add(getOperationInfo(method));
150             }
151         }
152 
153         return operationsList.toArray(new MBeanOperationInfo[0]);
154     }
155 
156     /**
157      * Checks if the method is an attribute getter method.
158      @param method
159      @return true if the method is an attribute getter method.
160      */
161     private static boolean isAttributeGetterMethod(Method method)
162     {
163         if (!(method.getName().equals("get")) &&
164             method.getName().startsWith("get"&&
165             method.getParameterTypes().length == &&
166             !method.getReturnType().equals(void.class))
167         {
168             return true;
169         }
170 
171         return false;
172     }
173 
174     /**
175      * Checks if the method is an attribute setter method.
176      @param method
177      @return true if the method is an attribute setter method.
178      */
179     private static boolean isAttributeSetterMethod(Method method)
180     {
181         if (!(method.getName().equals("set")) &&
182             method.getName().startsWith("set"&&
183             method.getParameterTypes().length == &&
184             method.getReturnType().equals(void.class))
185         {
186             return true;
187         }
188 
189         return false;
190     }
191 
192     /**
193      * Checks if the attribute is a boolean and the method is a isX kind og method.
194      @param method
195      @return true if the method is an attribute isX type of method
196      */
197     private static boolean isAttributeBoolean(Method method)
198     {
199         if (!(method.getName().equals("is")) &&
200             method.getName().startsWith("is"&&
201             method.getParameterTypes().length == &&
202             method.getReturnType().equals(boolean.class))
203         {
204             return true;
205         }
206 
207         return false;
208     }
209 
210     /**
211      * Helper method to retrieve the attribute index from the list of attributes.
212      @param attribute
213      @param list
214      @return attribute index no. -1 if attribtue doesn't exist
215      @throws NotCompliantMBeanException
216      */
217     private static int getIndexIfAlreadyExists(MBeanAttributeInfo attribute,
218                                                     List<MBeanAttributeInfo> list)
219         throws NotCompliantMBeanException
220     {
221         String exceptionMsg = "Conflicting attribute methods for attribute " + attribute.getName();
222 
223         for (MBeanAttributeInfo memberAttribute : list)
224         {
225             if (attribute.getName().equals(memberAttribute.getName()))
226             {
227                 if (!attribute.getType().equals(memberAttribute.getType()))
228                 {
229                     throw new NotCompliantMBeanException(exceptionMsg);
230                 }
231                 if (attribute.isReadable() && memberAttribute.isReadable())
232                 {
233                     if (attribute.isIs() != memberAttribute.isIs())
234                     {
235                         throw new NotCompliantMBeanException(exceptionMsg);
236                     }
237                 }
238 
239                 return list.indexOf(memberAttribute);
240             }
241         }
242 
243         return -1;
244     }
245 
246     /**
247      * Retrieves the attribute description from annotation
248      @param attributeMethod
249      @return attribute description
250      */
251     private static String getAttributeDescription(Method attributeMethod)
252     {
253         MBeanAttribute anno = attributeMethod.getAnnotation(MBeanAttribute.class);
254         if (anno != null)
255         {
256             return anno.description();
257         }
258         return _defaultAttributeDescription;
259     }
260 
261     /**
262      * Introspects the method to retrieve the operation information.
263      @param operation
264      @return MBeanOperationInfo
265      */
266     private static MBeanOperationInfo getOperationInfo(Method operation)
267     {
268         MBeanOperationInfo operationInfo = null;
269         Class<?> returnType = operation.getReturnType();
270 
271         MBeanParameterInfo[] paramsInfo = getParametersInfo(operation.getParameterAnnotations(),
272                                                             operation.getParameterTypes());
273 
274         String operationDesc = _defaultOerationDescription;
275         int impact = MBeanOperationInfo.UNKNOWN;
276 
277         if (operation.getAnnotation(MBeanOperation.class!= null)
278         {
279             operationDesc = operation.getAnnotation(MBeanOperation.class).description();
280             impact = operation.getAnnotation(MBeanOperation.class).impact();
281         }
282         operationInfo = new MBeanOperationInfo(operation.getName(),
283                                                operationDesc,
284                                                paramsInfo,
285                                                returnType.getName(),
286                                                impact);
287 
288         return operationInfo;
289     }
290 
291     /**
292      * Constructs the parameter info.
293      @param paramsAnno
294      @param paramTypes
295      @return MBeanParameterInfo[]
296      */
297     private static MBeanParameterInfo[] getParametersInfo(Annotation[][] paramsAnno,
298                                                           Class<?>[] paramTypes)
299     {
300         int noOfParams = paramsAnno.length;
301 
302         MBeanParameterInfo[] paramsInfo = new MBeanParameterInfo[noOfParams];
303 
304         for (int i = 0; i < noOfParams; i++)
305         {
306             MBeanParameterInfo paramInfo = null;
307             String type = paramTypes[i].getName();
308             for (Annotation anno : paramsAnno[i])
309             {
310                 String name,desc;
311                 if (MBeanOperationParameter.class.isInstance(anno))
312                 {
313                     name = MBeanOperationParameter.class.cast(anno).name();
314                     desc = MBeanOperationParameter.class.cast(anno).description();
315                     paramInfo = new MBeanParameterInfo(name, type, desc);
316                 }
317             }
318 
319 
320             if (paramInfo == null)
321             {
322                 paramInfo = new MBeanParameterInfo("p " (i + 1), type, "parameter " (i + 1));
323             }
324             if (paramInfo != null)
325                 paramsInfo[i= paramInfo;
326         }
327 
328         return paramsInfo;
329     }
330 
331     /**
332      * Introspects the MBean class for constructors
333      @param implClass
334      @return MBeanConstructorInfo[]
335      */
336     static MBeanConstructorInfo[] getMBeanConstructorsInfo(Class implClass)
337     {
338         List<MBeanConstructorInfo> constructors = new ArrayList<MBeanConstructorInfo>();
339 
340         for (Constructor cons : implClass.getConstructors())
341         {
342             MBeanConstructorInfo constructorInfo = getMBeanConstructorInfo(cons);
343             //MBeanConstructorInfo constructorInfo = new MBeanConstructorInfo("desc", cons);
344             if (constructorInfo != null)
345                 constructors.add(constructorInfo);
346         }
347 
348         return constructors.toArray(new MBeanConstructorInfo[0]);
349     }
350 
351     /**
352      * Retrieves the constructor info from given constructor.
353      @param cons
354      @return MBeanConstructorInfo
355      */
356     private static MBeanConstructorInfo getMBeanConstructorInfo(Constructor cons)
357     {
358         String desc = null;
359         Annotation anno = cons.getAnnotation(MBeanConstructor.class);
360         if (anno != null && MBeanConstructor.class.isInstance(anno))
361         {
362             desc = MBeanConstructor.class.cast(anno).value();
363         }
364 
365         //MBeanParameterInfo[] paramsInfo = getParametersInfo(cons.getParameterAnnotations(),
366         //                                                    cons.getParameterTypes());
367 
368         return new MBeanConstructorInfo(cons.getName(),
369                                         desc != null ? _defaultConstructorDescription : desc ,
370                                         null);
371     }
372 
373     /**
374      * Retrieves the description from the annotations of given class
375      @param annotatedClass
376      @return class description
377      */
378     static String getMBeanDescription(Class annotatedClass)
379     {
380         Annotation anno = annotatedClass.getAnnotation(MBeanDescription.class);
381         if (anno != null && MBeanDescription.class.isInstance(anno))
382         {
383             return MBeanDescription.class.cast(anno).value();
384         }
385         return _defaultMbeanDescription;
386     }
387 
388 }