View Javadoc

1   /*
2    * Copyright 2001-2004 The Apache Software Foundation.
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * 
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    * 
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.apache.commons.modeler.modules;
18  
19  import org.apache.commons.logging.Log;
20  import org.apache.commons.logging.LogFactory;
21  import org.apache.commons.modeler.AttributeInfo;
22  import org.apache.commons.modeler.ManagedBean;
23  import org.apache.commons.modeler.OperationInfo;
24  import org.apache.commons.modeler.ParameterInfo;
25  import org.apache.commons.modeler.Registry;
26  import org.apache.commons.modeler.ConstructorInfo;
27  
28  import javax.management.ObjectName;
29  
30  import java.lang.reflect.Method;
31  import java.lang.reflect.Modifier;
32  import java.lang.reflect.Constructor;
33  import java.math.BigDecimal;
34  import java.math.BigInteger;
35  import java.util.ArrayList;
36  import java.util.Enumeration;
37  import java.util.Hashtable;
38  import java.util.List;
39  
40  public class MbeansDescriptorsIntrospectionSource extends ModelerSource
41  {
42      private static Log log = LogFactory.getLog(MbeansDescriptorsIntrospectionSource.class);
43  
44      Registry registry;
45      String location;
46      String type;
47      Object source;
48      List mbeans=new ArrayList();
49  
50      public void setRegistry(Registry reg) {
51          this.registry=reg;
52      }
53  
54      public void setLocation( String loc ) {
55          this.location=loc;
56      }
57  
58      /*** Used if a single component is loaded
59       *
60       * @param type
61       */
62      public void setType( String type ) {
63         this.type=type;
64      }
65  
66      public void setSource( Object source ) {
67          this.source=source;
68      }
69  
70      public List loadDescriptors( Registry registry, String location,
71                                   String type, Object source)
72              throws Exception
73      {
74          setRegistry(registry);
75          setLocation(location);
76          setType(type);
77          setSource(source);
78          execute();
79          return mbeans;
80      }
81  
82      public void execute() throws Exception {
83          if( registry==null ) registry=Registry.getRegistry();
84          try {
85              ManagedBean managed=createManagedBean(registry, null, (Class)source, type);
86              if( managed==null ) return;
87              managed.setName( type );
88  
89              mbeans.add(managed);
90  
91          } catch( Exception ex ) {
92              log.error( "Error reading descriptors ", ex);
93          }
94      }
95  
96  
97  
98      // ------------ Implementation for non-declared introspection classes
99  
100     static Hashtable specialMethods=new Hashtable();
101     static {
102         specialMethods.put( "preDeregister", "");
103         specialMethods.put( "postDeregister", "");
104     }
105 
106     private static String strArray[]=new String[0];
107     private static ObjectName objNameArray[]=new ObjectName[0];
108     // createMBean == registerClass + registerMBean
109 
110     private static Class[] supportedTypes  = new Class[] {
111         Boolean.class,
112         Boolean.TYPE,
113         Byte.class,
114         Byte.TYPE,
115         Character.class,
116         Character.TYPE,
117         Short.class,
118         Short.TYPE,
119         Integer.class,
120         Integer.TYPE,
121         Long.class,
122         Long.TYPE,
123         Float.class, 
124         Float.TYPE,
125         Double.class,
126         Double.TYPE,
127         String.class,
128         strArray.getClass(),
129         BigDecimal.class,
130         BigInteger.class,
131         ObjectName.class,
132         objNameArray.getClass(),
133         java.io.File.class,
134     };
135     
136     /***
137      * Check if this class is one of the supported types.
138      * If the class is supported, returns true.  Otherwise,
139      * returns false.
140      * @param ret The class to check
141      * @return boolean True if class is supported
142      */ 
143     private boolean supportedType(Class ret) {
144         for (int i = 0; i < supportedTypes.length; i++) {
145             if (ret == supportedTypes[i]) {
146                 return true;
147             }
148         }
149         if (isBeanCompatible(ret)) {
150             return true;
151         }
152         return false;
153     }
154 
155     /***
156      * Check if this class conforms to JavaBeans specifications.
157      * If the class is conformant, returns true.
158      *
159      * @param javaType The class to check
160      * @return boolean True if the class is compatible.
161      */
162     protected boolean isBeanCompatible(Class javaType) {
163         // Must be a non-primitive and non array
164         if (javaType.isArray() || javaType.isPrimitive()) {
165             return false;
166         }
167 
168         // Anything in the java or javax package that
169         // does not have a defined mapping is excluded.
170         if (javaType.getName().startsWith("java.") || 
171             javaType.getName().startsWith("javax.")) {
172             return false;
173         }
174 
175         try {
176             javaType.getConstructor(new Class[]{});
177         } catch (java.lang.NoSuchMethodException e) {
178             return false;
179         }
180 
181         // Make sure superclass is compatible
182         Class superClass = javaType.getSuperclass();
183         if (superClass != null && 
184             superClass != java.lang.Object.class && 
185             superClass != java.lang.Exception.class && 
186             superClass != java.lang.Throwable.class) {
187             if (!isBeanCompatible(superClass)) {
188                 return false;
189             }
190         }
191         return true;
192     }
193     
194     /*** 
195      * Process the methods and extract 'attributes', methods, etc
196      *
197      * @param realClass The class to process
198      * @param methods The methods to process
199      * @param attMap The attribute map (complete)
200      * @param getAttMap The readable attributess map
201      * @param setAttMap The settable attributes map
202      * @param invokeAttMap The invokable attributes map
203      */
204     private void initMethods(Class realClass,
205                              Method methods[],
206                              Hashtable attMap, Hashtable getAttMap,
207                              Hashtable setAttMap, Hashtable invokeAttMap)
208     {
209         for (int j = 0; j < methods.length; ++j) {
210             String name=methods[j].getName();
211 
212             if( Modifier.isStatic(methods[j].getModifiers()))
213                 continue;
214             if( ! Modifier.isPublic( methods[j].getModifiers() ) ) {
215                 if( log.isDebugEnabled())
216                     log.debug("Not public " + methods[j] );
217                 continue;
218             }
219             if( methods[j].getDeclaringClass() == Object.class )
220                 continue;
221             Class params[]=methods[j].getParameterTypes();
222 
223             if( name.startsWith( "get" ) && params.length==0) {
224                 Class ret=methods[j].getReturnType();
225                 if( ! supportedType( ret ) ) {
226                     if( log.isDebugEnabled() )
227                         log.debug("Unsupported type " + methods[j]);
228                     continue;
229                 }
230                 name=unCapitalize( name.substring(3));
231 
232                 getAttMap.put( name, methods[j] );
233                 // just a marker, we don't use the value
234                 attMap.put( name, methods[j] );
235             } else if( name.startsWith( "is" ) && params.length==0) {
236                 Class ret=methods[j].getReturnType();
237                 if( Boolean.TYPE != ret  ) {
238                     if( log.isDebugEnabled() )
239                         log.debug("Unsupported type " + methods[j] + " " + ret );
240                     continue;
241                 }
242                 name=unCapitalize( name.substring(2));
243 
244                 getAttMap.put( name, methods[j] );
245                 // just a marker, we don't use the value
246                 attMap.put( name, methods[j] );
247 
248             } else if( name.startsWith( "set" ) && params.length==1) {
249                 if( ! supportedType( params[0] ) ) {
250                     if( log.isDebugEnabled() )
251                         log.debug("Unsupported type " + methods[j] + " " + params[0]);
252                     continue;
253                 }
254                 name=unCapitalize( name.substring(3));
255                 setAttMap.put( name, methods[j] );
256                 attMap.put( name, methods[j] );
257             } else {
258                 if( params.length == 0 ) {
259                     if( specialMethods.get( methods[j].getName() ) != null )
260                         continue;
261                     invokeAttMap.put( name, methods[j]);
262                 } else {
263                     boolean supported=true;
264                     for( int i=0; i<params.length; i++ ) {
265                         if( ! supportedType( params[i])) {
266                             supported=false;
267                             break;
268                         }
269                     }
270                     if( supported )
271                         invokeAttMap.put( name, methods[j]);
272                 }
273             }
274         }
275     }
276 
277     /***
278      * XXX Find if the 'className' is the name of the MBean or
279      *       the real class ( I suppose first )
280      * XXX Read (optional) descriptions from a .properties, generated
281      *       from source
282      * XXX Deal with constructors
283      *
284      * @param registry The Bean registry (not used)
285      * @param domain The bean domain (not used)
286      * @param realClass The class to analyze
287      * @param type The bean type
288      * @return ManagedBean The create MBean
289      */
290     public ManagedBean createManagedBean(Registry registry, String domain,
291                                          Class realClass, String type)
292     {
293         ManagedBean mbean= new ManagedBean();
294 
295         Method methods[]=null;
296 
297         Hashtable attMap=new Hashtable();
298         // key: attribute val: getter method
299         Hashtable getAttMap=new Hashtable();
300         // key: attribute val: setter method
301         Hashtable setAttMap=new Hashtable();
302         // key: operation val: invoke method
303         Hashtable invokeAttMap=new Hashtable();
304 
305         methods = realClass.getMethods();
306 
307         initMethods(realClass, methods, attMap, getAttMap, setAttMap, invokeAttMap );
308 
309         try {
310 
311             Enumeration en=attMap.keys();
312             while( en.hasMoreElements() ) {
313                 String name=(String)en.nextElement();
314                 AttributeInfo ai=new AttributeInfo();
315                 ai.setName( name );
316                 Method gm=(Method)getAttMap.get(name);
317                 if( gm!=null ) {
318                     //ai.setGetMethodObj( gm );
319                     ai.setGetMethod( gm.getName());
320                     Class t=gm.getReturnType();
321                     if( t!=null )
322                         ai.setType( t.getName() );
323                 }
324                 Method sm=(Method)setAttMap.get(name);
325                 if( sm!=null ) {
326                     //ai.setSetMethodObj(sm);
327                     Class t=sm.getParameterTypes()[0];
328                     if( t!=null )
329                         ai.setType( t.getName());
330                     ai.setSetMethod( sm.getName());
331                 }
332                 ai.setDescription("Introspected attribute " + name);
333                 if( log.isDebugEnabled()) log.debug("Introspected attribute " +
334                         name + " " + gm + " " + sm);
335                 if( gm==null )
336                     ai.setReadable(false);
337                 if( sm==null )
338                     ai.setWriteable(false);
339                 if( sm!=null || gm!=null )
340                     mbean.addAttribute(ai);
341             }
342 
343             en=invokeAttMap.keys();
344             while( en.hasMoreElements() ) {
345                 String name=(String)en.nextElement();
346                 Method m=(Method)invokeAttMap.get(name);
347                 if( m!=null && name != null ) {
348                     OperationInfo op=new OperationInfo();
349                     op.setName(name);
350                     op.setReturnType(m.getReturnType().getName());
351                     op.setDescription("Introspected operation " + name);
352                     Class parms[]=m.getParameterTypes();
353                     for(int i=0; i<parms.length; i++ ) {
354                         ParameterInfo pi=new ParameterInfo();
355                         pi.setType(parms[i].getName());
356                         pi.setName( "param" + i);
357                         pi.setDescription("Introspected parameter param" + i);
358                         op.addParameter(pi);
359                     }
360                     mbean.addOperation(op);
361                 } else {
362                     log.error("Null arg " + name + " " + m );
363                 }
364             }
365 
366             Constructor[] constructors = realClass.getConstructors();
367             for(int i=0;i<constructors.length;i++) {
368                 ConstructorInfo info = new ConstructorInfo();
369                 String className = realClass.getName();
370                 int nIndex = -1;
371                 if((nIndex = className.lastIndexOf('.'))!=-1) {
372                     className = className.substring(nIndex+1);
373                 }
374                 info.setName(className);
375                 info.setDescription(constructors[i].getName());
376                 Class classes[] = constructors[i].getParameterTypes();
377                 for(int j=0;j<classes.length;j++) {
378                     ParameterInfo pi = new ParameterInfo();
379                     pi.setType(classes[j].getName());
380                     pi.setName("param" + j);
381                     pi.setDescription("Introspected parameter param" + j);
382                     info.addParameter(pi);
383                 }
384                 mbean.addConstructor(info);
385             }
386             
387             if( log.isDebugEnabled())
388                 log.debug("Setting name: " + type );
389             mbean.setName( type );
390 
391             return mbean;
392         } catch( Exception ex ) {
393             ex.printStackTrace();
394             return null;
395         }
396     }
397 
398 
399     // -------------------- Utils --------------------
400     /***
401      * Converts the first character of the given
402      * String into lower-case.
403      *
404      * @param name The string to convert
405      * @return String
406      */
407     private static String unCapitalize(String name) {
408         if (name == null || name.length() == 0) {
409             return name;
410         }
411         char chars[] = name.toCharArray();
412         chars[0] = Character.toLowerCase(chars[0]);
413         return new String(chars);
414     }
415 
416 }
417 
418 // End of class: MbeanDescriptorsIntrospectionSource