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