View Javadoc

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