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
018 package org.apache.commons.modeler.modules;
019
020 import org.apache.commons.logging.Log;
021 import org.apache.commons.logging.LogFactory;
022 import org.apache.commons.modeler.AttributeInfo;
023 import org.apache.commons.modeler.ManagedBean;
024 import org.apache.commons.modeler.OperationInfo;
025 import org.apache.commons.modeler.ParameterInfo;
026 import org.apache.commons.modeler.Registry;
027 import org.apache.commons.modeler.ConstructorInfo;
028
029 import javax.management.ObjectName;
030
031 import java.lang.reflect.Method;
032 import java.lang.reflect.Modifier;
033 import java.lang.reflect.Constructor;
034 import java.math.BigDecimal;
035 import java.math.BigInteger;
036 import java.util.ArrayList;
037 import java.util.Enumeration;
038 import java.util.Hashtable;
039 import java.util.List;
040
041 public class MbeansDescriptorsIntrospectionSource extends ModelerSource
042 {
043 private static Log log = LogFactory.getLog(MbeansDescriptorsIntrospectionSource.class);
044
045 Registry registry;
046 String location;
047 String type;
048 Object source;
049 List mbeans=new ArrayList();
050
051 public void setRegistry(Registry reg) {
052 this.registry=reg;
053 }
054
055 public void setLocation( String loc ) {
056 this.location=loc;
057 }
058
059 /** Used if a single component is loaded
060 *
061 * @param type
062 */
063 public void setType( String type ) {
064 this.type=type;
065 }
066
067 public void setSource( Object source ) {
068 this.source=source;
069 }
070
071 public List loadDescriptors( Registry registry, String location,
072 String type, Object source)
073 throws Exception
074 {
075 setRegistry(registry);
076 setLocation(location);
077 setType(type);
078 setSource(source);
079 execute();
080 return mbeans;
081 }
082
083 public void execute() throws Exception {
084 if( registry==null ) registry=Registry.getRegistry();
085 try {
086 ManagedBean managed=createManagedBean(registry, null, (Class)source, type);
087 if( managed==null ) return;
088 managed.setName( type );
089
090 mbeans.add(managed);
091
092 } catch( Exception ex ) {
093 log.error( "Error reading descriptors ", ex);
094 }
095 }
096
097
098
099 // ------------ 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