1. /*
  2. * ====================================================================
  3. *
  4. * The Apache Software License, Version 1.1
  5. *
  6. * Copyright (c) 2001-2003 The Apache Software Foundation. All rights
  7. * reserved.
  8. *
  9. * Redistribution and use in source and binary forms, with or without
  10. * modification, are permitted provided that the following conditions
  11. * are met:
  12. *
  13. * 1. Redistributions of source code must retain the above copyright
  14. * notice, this list of conditions and the following disclaimer.
  15. *
  16. * 2. Redistributions in binary form must reproduce the above copyright
  17. * notice, this list of conditions and the following disclaimer in
  18. * the documentation and/or other materials provided with the
  19. * distribution.
  20. *
  21. * 3. The end-user documentation included with the redistribution, if
  22. * any, must include the following acknowlegement:
  23. * "This product includes software developed by the
  24. * Apache Software Foundation (http://www.apache.org/)."
  25. * Alternately, this acknowlegement may appear in the software itself,
  26. * if and wherever such third-party acknowlegements normally appear.
  27. *
  28. * 4. The names "The Jakarta Project", "Commons", and "Apache Software
  29. * Foundation" must not be used to endorse or promote products derived
  30. * from this software without prior written permission. For written
  31. * permission, please contact apache@apache.org.
  32. *
  33. * 5. Products derived from this software may not be called "Apache"
  34. * nor may "Apache" appear in their names without prior written
  35. * permission of the Apache Group.
  36. *
  37. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  38. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  39. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  40. * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
  41. * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  42. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  43. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  44. * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  45. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  46. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  47. * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  48. * SUCH DAMAGE.
  49. * ====================================================================
  50. *
  51. * This software consists of voluntary contributions made by many
  52. * individuals on behalf of the Apache Software Foundation. For more
  53. * information on the Apache Software Foundation, please see
  54. * <http://www.apache.org/>.
  55. *
  56. * [Additional notices, if required by prior licensing conditions]
  57. *
  58. */
  59. package org.apache.commons.modeler.modules;
  60. import org.apache.commons.logging.Log;
  61. import org.apache.commons.logging.LogFactory;
  62. import org.apache.commons.modeler.AttributeInfo;
  63. import org.apache.commons.modeler.ManagedBean;
  64. import org.apache.commons.modeler.OperationInfo;
  65. import org.apache.commons.modeler.ParameterInfo;
  66. import org.apache.commons.modeler.Registry;
  67. import org.apache.commons.modeler.ConstructorInfo;
  68. import javax.management.ObjectName;
  69. import java.lang.reflect.Method;
  70. import java.lang.reflect.Modifier;
  71. import java.lang.reflect.Constructor;
  72. import java.math.BigDecimal;
  73. import java.math.BigInteger;
  74. import java.util.ArrayList;
  75. import java.util.Enumeration;
  76. import java.util.Hashtable;
  77. import java.util.List;
  78. public class MbeansDescriptorsIntrospectionSource extends ModelerSource
  79. {
  80. private static Log log = LogFactory.getLog(MbeansDescriptorsIntrospectionSource.class);
  81. Registry registry;
  82. String location;
  83. String type;
  84. Object source;
  85. List mbeans=new ArrayList();
  86. public void setRegistry(Registry reg) {
  87. this.registry=reg;
  88. }
  89. public void setLocation( String loc ) {
  90. this.location=loc;
  91. }
  92. /** Used if a single component is loaded
  93. *
  94. * @param type
  95. */
  96. public void setType( String type ) {
  97. this.type=type;
  98. }
  99. public void setSource( Object source ) {
  100. this.source=source;
  101. }
  102. public List loadDescriptors( Registry registry, String location,
  103. String type, Object source)
  104. throws Exception
  105. {
  106. setRegistry(registry);
  107. setLocation(location);
  108. setType(type);
  109. setSource(source);
  110. execute();
  111. return mbeans;
  112. }
  113. public void execute() throws Exception {
  114. if( registry==null ) registry=Registry.getRegistry();
  115. try {
  116. ManagedBean managed=createManagedBean(registry, null, (Class)source, type);
  117. if( managed==null ) return;
  118. managed.setName( type );
  119. mbeans.add(managed);
  120. } catch( Exception ex ) {
  121. log.error( "Error reading descriptors ", ex);
  122. }
  123. }
  124. // ------------ Implementation for non-declared introspection classes
  125. static Hashtable specialMethods=new Hashtable();
  126. static {
  127. specialMethods.put( "preDeregister", "");
  128. specialMethods.put( "postDeregister", "");
  129. }
  130. private static String strArray[]=new String[0];
  131. private static ObjectName objNameArray[]=new ObjectName[0];
  132. // createMBean == registerClass + registerMBean
  133. private static Class[] supportedTypes = new Class[] {
  134. Boolean.class,
  135. Boolean.TYPE,
  136. Byte.class,
  137. Byte.TYPE,
  138. Character.class,
  139. Character.TYPE,
  140. Short.class,
  141. Short.TYPE,
  142. Integer.class,
  143. Integer.TYPE,
  144. Long.class,
  145. Long.TYPE,
  146. Float.class,
  147. Float.TYPE,
  148. Double.class,
  149. Double.TYPE,
  150. String.class,
  151. strArray.getClass(),
  152. BigDecimal.class,
  153. BigInteger.class,
  154. ObjectName.class,
  155. objNameArray.getClass(),
  156. java.io.File.class,
  157. };
  158. /**
  159. * Check if this class is one of the supported types.
  160. * If the class is supported, returns true. Otherwise,
  161. * returns false.
  162. * @param ret The class to check
  163. * @return boolean True if class is supported
  164. */
  165. private boolean supportedType(Class ret) {
  166. for (int i = 0; i < supportedTypes.length; i++) {
  167. if (ret == supportedTypes[i]) {
  168. return true;
  169. }
  170. }
  171. if (isBeanCompatible(ret)) {
  172. return true;
  173. }
  174. return false;
  175. }
  176. /**
  177. * Check if this class conforms to JavaBeans specifications.
  178. * If the class is conformant, returns true.
  179. *
  180. * @param javaType The class to check
  181. * @return boolean True if the class is compatible.
  182. */
  183. protected boolean isBeanCompatible(Class javaType) {
  184. // Must be a non-primitive and non array
  185. if (javaType.isArray() || javaType.isPrimitive()) {
  186. return false;
  187. }
  188. // Anything in the java or javax package that
  189. // does not have a defined mapping is excluded.
  190. if (javaType.getName().startsWith("java.") ||
  191. javaType.getName().startsWith("javax.")) {
  192. return false;
  193. }
  194. try {
  195. javaType.getConstructor(new Class[]{});
  196. } catch (java.lang.NoSuchMethodException e) {
  197. return false;
  198. }
  199. // Make sure superclass is compatible
  200. Class superClass = javaType.getSuperclass();
  201. if (superClass != null &&
  202. superClass != java.lang.Object.class &&
  203. superClass != java.lang.Exception.class &&
  204. superClass != java.lang.Throwable.class) {
  205. if (!isBeanCompatible(superClass)) {
  206. return false;
  207. }
  208. }
  209. return true;
  210. }
  211. /**
  212. * Process the methods and extract 'attributes', methods, etc
  213. *
  214. * @param realClass The class to process
  215. * @param methods The methods to process
  216. * @param attMap The attribute map (complete)
  217. * @param getAttMap The readable attributess map
  218. * @param setAttMap The settable attributes map
  219. * @param invokeAttMap The invokable attributes map
  220. */
  221. private void initMethods(Class realClass,
  222. Method methods[],
  223. Hashtable attMap, Hashtable getAttMap,
  224. Hashtable setAttMap, Hashtable invokeAttMap)
  225. {
  226. for (int j = 0; j < methods.length; ++j) {
  227. String name=methods[j].getName();
  228. if( Modifier.isStatic(methods[j].getModifiers()))
  229. continue;
  230. if( ! Modifier.isPublic( methods[j].getModifiers() ) ) {
  231. if( log.isDebugEnabled())
  232. log.debug("Not public " + methods[j] );
  233. continue;
  234. }
  235. if( methods[j].getDeclaringClass() == Object.class )
  236. continue;
  237. Class params[]=methods[j].getParameterTypes();
  238. if( name.startsWith( "get" ) && params.length==0) {
  239. Class ret=methods[j].getReturnType();
  240. if( ! supportedType( ret ) ) {
  241. if( log.isDebugEnabled() )
  242. log.debug("Unsupported type " + methods[j]);
  243. continue;
  244. }
  245. name=unCapitalize( name.substring(3));
  246. getAttMap.put( name, methods[j] );
  247. // just a marker, we don't use the value
  248. attMap.put( name, methods[j] );
  249. } else if( name.startsWith( "is" ) && params.length==0) {
  250. Class ret=methods[j].getReturnType();
  251. if( Boolean.TYPE != ret ) {
  252. if( log.isDebugEnabled() )
  253. log.debug("Unsupported type " + methods[j] + " " + ret );
  254. continue;
  255. }
  256. name=unCapitalize( name.substring(2));
  257. getAttMap.put( name, methods[j] );
  258. // just a marker, we don't use the value
  259. attMap.put( name, methods[j] );
  260. } else if( name.startsWith( "set" ) && params.length==1) {
  261. if( ! supportedType( params[0] ) ) {
  262. if( log.isDebugEnabled() )
  263. log.debug("Unsupported type " + methods[j] + " " + params[0]);
  264. continue;
  265. }
  266. name=unCapitalize( name.substring(3));
  267. setAttMap.put( name, methods[j] );
  268. attMap.put( name, methods[j] );
  269. } else {
  270. if( params.length == 0 ) {
  271. if( specialMethods.get( methods[j].getName() ) != null )
  272. continue;
  273. invokeAttMap.put( name, methods[j]);
  274. } else {
  275. boolean supported=true;
  276. for( int i=0; i<params.length; i++ ) {
  277. if( ! supportedType( params[i])) {
  278. supported=false;
  279. break;
  280. }
  281. }
  282. if( supported )
  283. invokeAttMap.put( name, methods[j]);
  284. }
  285. }
  286. }
  287. }
  288. /**
  289. * XXX Find if the 'className' is the name of the MBean or
  290. * the real class ( I suppose first )
  291. * XXX Read (optional) descriptions from a .properties, generated
  292. * from source
  293. * XXX Deal with constructors
  294. *
  295. * @param registry The Bean registry (not used)
  296. * @param domain The bean domain (not used)
  297. * @param realClass The class to analyze
  298. * @param type The bean type
  299. * @return ManagedBean The create MBean
  300. */
  301. public ManagedBean createManagedBean(Registry registry, String domain,
  302. Class realClass, String type)
  303. {
  304. ManagedBean mbean= new ManagedBean();
  305. Method methods[]=null;
  306. Hashtable attMap=new Hashtable();
  307. // key: attribute val: getter method
  308. Hashtable getAttMap=new Hashtable();
  309. // key: attribute val: setter method
  310. Hashtable setAttMap=new Hashtable();
  311. // key: operation val: invoke method
  312. Hashtable invokeAttMap=new Hashtable();
  313. methods = realClass.getMethods();
  314. initMethods(realClass, methods, attMap, getAttMap, setAttMap, invokeAttMap );
  315. try {
  316. Enumeration en=attMap.keys();
  317. while( en.hasMoreElements() ) {
  318. String name=(String)en.nextElement();
  319. AttributeInfo ai=new AttributeInfo();
  320. ai.setName( name );
  321. Method gm=(Method)getAttMap.get(name);
  322. if( gm!=null ) {
  323. //ai.setGetMethodObj( gm );
  324. ai.setGetMethod( gm.getName());
  325. Class t=gm.getReturnType();
  326. if( t!=null )
  327. ai.setType( t.getName() );
  328. }
  329. Method sm=(Method)setAttMap.get(name);
  330. if( sm!=null ) {
  331. //ai.setSetMethodObj(sm);
  332. Class t=sm.getParameterTypes()[0];
  333. if( t!=null )
  334. ai.setType( t.getName());
  335. ai.setSetMethod( sm.getName());
  336. }
  337. ai.setDescription("Introspected attribute " + name);
  338. if( log.isDebugEnabled()) log.debug("Introspected attribute " +
  339. name + " " + gm + " " + sm);
  340. if( gm==null )
  341. ai.setReadable(false);
  342. if( sm==null )
  343. ai.setWriteable(false);
  344. if( sm!=null || gm!=null )
  345. mbean.addAttribute(ai);
  346. }
  347. en=invokeAttMap.keys();
  348. while( en.hasMoreElements() ) {
  349. String name=(String)en.nextElement();
  350. Method m=(Method)invokeAttMap.get(name);
  351. if( m!=null && name != null ) {
  352. OperationInfo op=new OperationInfo();
  353. op.setName(name);
  354. op.setReturnType(m.getReturnType().getName());
  355. Class parms[]=m.getParameterTypes();
  356. for(int i=0; i<parms.length; i++ ) {
  357. ParameterInfo pi=new ParameterInfo();
  358. pi.setType(parms[i].getName());
  359. pi.setName( "param" + i);
  360. op.addParameter(pi);
  361. }
  362. mbean.addOperation(op);
  363. } else {
  364. log.error("Null arg " + name + " " + m );
  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. info.addParameter(pi);
  383. }
  384. mbean.addConstructor(info);
  385. }
  386. if( log.isDebugEnabled())
  387. log.debug("Setting name: " + type );
  388. mbean.setName( type );
  389. return mbean;
  390. } catch( Exception ex ) {
  391. ex.printStackTrace();
  392. return null;
  393. }
  394. }
  395. // -------------------- Utils --------------------
  396. /**
  397. * Converts the first character of the given
  398. * String into lower-case.
  399. *
  400. * @param name The string to convert
  401. * @return String
  402. */
  403. private static String unCapitalize(String name) {
  404. if (name == null || name.length() == 0) {
  405. return name;
  406. }
  407. char chars[] = name.toCharArray();
  408. chars[0] = Character.toLowerCase(chars[0]);
  409. return new String(chars);
  410. }
  411. }
  412. // End of class: MbeanDescriptorsIntrospectionSource