You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@avalon.apache.org by le...@apache.org on 2001/04/16 16:08:58 UTC

cvs commit: jakarta-avalon-phoenix/proposal/4.0/src/java/org/apache/jmx/introspector DynamicMBeanFactory.java DefaultDynamicNotificationMBean.java DefaultDynamicMBean.java

leosimons    01/04/16 07:08:58

  Added:       proposal/4.0/src/java/org/apache/jmx/introspector
                        DynamicMBeanFactory.java
                        DefaultDynamicNotificationMBean.java
                        DefaultDynamicMBean.java
  Log:
  adding introspector for automated creation of Dynamic MBeans
  
  Revision  Changes    Path
  1.1                  jakarta-avalon-phoenix/proposal/4.0/src/java/org/apache/jmx/introspector/DynamicMBeanFactory.java
  
  Index: DynamicMBeanFactory.java
  ===================================================================
  /*
   * Copyright (C) The Apache Software Foundation. All rights reserved.
   *
   * This software is published under the terms of the Apache Software License
   * version 1.1, a copy of which has been included with this distribution in
   * the LICENSE file.
   */
  package org.apache.jmx.introspector;
  
  import javax.management.DynamicMBean;
  import javax.management.NotCompliantMBeanException;
  import javax.management.MBeanInfo;
  import javax.management.NotificationBroadcaster;
  
  /**
   * This class uses introspection to create DynamicMBeans for
   * any java object. It exposes all public methods. If you wish
   * to provide human-readable information about the exposed
   * properties and methods, you can provide your own MBeanInfo
   * object.
   *
   * @author <a href="mailto:mail@leosimons.com">Leo Simons</a>
   */
  
  public class DynamicMBeanFactory
  {
      /**
       * All DynamicMBeanFactory methods are static.
       * It is unneccessary to create instances.
       */
      private DynamicMBeanFactory()
      {
      }
      /**
       * Get a <code>DynamicMBean</code> that represents the
       * supplied object.
       */
      public static DynamicMBean create( Object obj ) throws NotCompliantMBeanException
      {
          if( obj instanceof NotificationBroadcaster )
          {
              return new DefaultDynamicNotificationMBean( (NotificationBroadcaster)obj );
          }
          else
          {
              return new DefaultDynamicMBean( obj );
          }
      }
      /**
       * Get a <code>DynamicMBean</code> that represents the
       * supplied object, using the supplied <code>MBeanInfo</code>
       * object.
       *
       * @exception IllegalArgumentException if the supplied object
       * does not implement the class specified by the supplied
       * MBeanInfo's getClassName() method.
       */
      public static DynamicMBean create( Object obj, MBeanInfo mBeanInfo )
          throws IllegalArgumentException, NotCompliantMBeanException
      {
          if( obj instanceof NotificationBroadcaster )
          {
              return new DefaultDynamicNotificationMBean( (NotificationBroadcaster)obj, mBeanInfo );
          }
          else
          {
              return new DefaultDynamicMBean( obj, mBeanInfo );
          }
      }
  }
  
  
  1.1                  jakarta-avalon-phoenix/proposal/4.0/src/java/org/apache/jmx/introspector/DefaultDynamicNotificationMBean.java
  
  Index: DefaultDynamicNotificationMBean.java
  ===================================================================
  /*
   * Copyright (C) The Apache Software Foundation. All rights reserved.
   *
   * This software is published under the terms of the Apache Software License
   * version 1.1, a copy of which has been included with this distribution in
   * the LICENSE file.
   */
  package org.apache.jmx.introspector;
  
  import javax.management.MBeanInfo;
  import javax.management.NotCompliantMBeanException;
  import javax.management.NotificationBroadcaster;
  import javax.management.NotificationListener;
  import javax.management.NotificationFilter;
  import javax.management.ListenerNotFoundException;
  import javax.management.NotificationBroadcasterSupport;
  import javax.management.MBeanNotificationInfo;
  
  
  /**
   * This class is used by DynamicMBeanFactory to create DynamicMBeans.
   * It extends DefaultDynamicMBean, adding support for notifications.
   *
   * @author <a href="mailto:mail@leosimons.com">Leo Simons</a>
   */
  public class DefaultDynamicNotificationMBean
      extends DefaultDynamicMBean implements NotificationBroadcaster
  {
      public DefaultDynamicNotificationMBean( NotificationBroadcaster obj )
          throws NotCompliantMBeanException
      {
          super( obj );
  
          this.notifications = obj.getNotificationInfo();
          this.createMBeanInfo();
      }
      public DefaultDynamicNotificationMBean( NotificationBroadcaster obj, MBeanInfo mBeanInfo )
          throws NotCompliantMBeanException
      {
          super( obj, mBeanInfo );
          this.notifications = obj.getNotificationInfo();
          this.createMBeanInfo();
      }
  
      /////////////////////////////////////////
      /// NOTIFICATIONBROADCASTER INTERFACE ///
      /////////////////////////////////////////
      public void addNotificationListener( NotificationListener listener, NotificationFilter filter, Object handback )
          throws java.lang.IllegalArgumentException
      {
          ((NotificationBroadcaster)this.obj).addNotificationListener( listener, filter, handback );
      }
      public void removeNotificationListener( NotificationListener listener )
          throws ListenerNotFoundException
      {
          ((NotificationBroadcaster)this.obj).removeNotificationListener( listener );
      }
      public MBeanNotificationInfo[] getNotificationInfo()
      {
          return this.notifications;
      }
  
  }
  
  
  1.1                  jakarta-avalon-phoenix/proposal/4.0/src/java/org/apache/jmx/introspector/DefaultDynamicMBean.java
  
  Index: DefaultDynamicMBean.java
  ===================================================================
  /*
   * Copyright (C) The Apache Software Foundation. All rights reserved.
   *
   * This software is published under the terms of the Apache Software License
   * version 1.1, a copy of which has been included with this distribution in
   * the LICENSE file.
   */
  package org.apache.jmx.introspector;
  
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.List;
  import java.lang.reflect.Constructor;
  import java.lang.reflect.Method;
  
  import javax.management.DynamicMBean;
  import javax.management.Attribute;
  import javax.management.AttributeList;
  import javax.management.AttributeNotFoundException;
  import javax.management.MBeanException;
  import javax.management.NotCompliantMBeanException;
  import javax.management.ReflectionException;
  import javax.management.InvalidAttributeValueException;
  import javax.management.IntrospectionException;
  import javax.management.AttributeList;
  import javax.management.MBeanInfo;
  import javax.management.MBeanAttributeInfo;
  import javax.management.MBeanConstructorInfo;
  import javax.management.MBeanOperationInfo;
  import javax.management.MBeanNotificationInfo;
  import javax.management.NotificationBroadcaster;
  
  /**
   * This class is used by DynamicMBeanFactory to create DynamicMBeans.
   * It can represent any object. Notifications are not supported.
   *
   * You can easily create custom DynamicMBeans by extending this class
   * and replacing the createXXX() methods. TODO: Or, you can replace the
   * getDescriptionFor() methods to add sensible documentation to your
   * DynamicMBean, making it easier to create Open MBeans if you wish
   * to do so.
   *
   * @author <a href="mailto:mail@leosimons.com">Leo Simons</a>
   */
  class DefaultDynamicMBean implements DynamicMBean {
      private static final String DEFAULT_DESCRIPTION = "A manageable object.";
  
      /** The object represented by this MBean */
      protected final Object obj;
      /** The MBeanInfo object. */
      protected MBeanInfo mBeanInfo = null;
  
      /** The human readable description of the class. */
      protected String description = null;
      /** Tells if this is an open MBean. */
      protected boolean isOpen = false;
      /** The MBean attribute descriptors. */
      protected MBeanAttributeInfo[] attributes = null;
      /** The MBean operation descriptors. */
      protected MBeanOperationInfo[] operations = null;
      /** The MBean constructor descriptors. */
      protected MBeanConstructorInfo[] constructors = null;
      /** The MBean notification descriptors. */
      protected MBeanNotificationInfo[] notifications = null;
  
      DefaultDynamicMBean( Object obj ) throws NotCompliantMBeanException
      {
          this.obj = obj;
          final Class clazz = obj.getClass();
  
          final Constructor[] constructors = clazz.getConstructors();
          final Method[] methods = clazz.getMethods();
  
          this.constructors = createConstructorInfo( constructors );
          this.operations = createOperationInfo( methods );
          this.attributes = createAttributeInfo( methods );
          this.notifications = new MBeanNotificationInfo[0];
          this.description = this.DEFAULT_DESCRIPTION;
  
          createMBeanInfo();
      }
      DefaultDynamicMBean( Object obj, MBeanInfo mBeanInfo )
          throws IllegalArgumentException, NotCompliantMBeanException
      {
          this(obj);
  
          this.mBeanInfo = mBeanInfo;
  
          // make sure the MBeanInfo is correct
          try {
              if(!( Class.forName( mBeanInfo.getClassName() ) == obj.getClass() ) )
              {
                  throw new IllegalArgumentException( "The supplied mBeanInfo does not describe the object!" );
              }
              if( mBeanInfo.getNotifications() != this.notifications )
              {
                  throw new IllegalArgumentException( "The DefaultDynamicMBean does not support notifications!" );
              }
          }
          catch( ClassNotFoundException cnfe )
          {
              throw new IllegalArgumentException( "Unable to find the class specified by the MBeanInfo!" );
          }
          this.description = mBeanInfo.getDescription();
      }
  
      ///////////////////////////////
      /// DYNAMIC MBEAN INTERFACE ///
      ///////////////////////////////
      /**
       * Obtains the value of a specific attribute of the Dynamic MBean.
       */
      public Object getAttribute( String attribute )
          throws AttributeNotFoundException, MBeanException, ReflectionException
      {
          final String methodName = "get" + attribute.substring(0,1).toUpperCase() + attribute.substring(1);
          final String methodName2 = "is" + attribute.substring(0,1).toUpperCase() + attribute.substring(1);
          Method m;
          try
          {
              m = this.obj.getClass().getMethod(
                  methodName,
                  new Class[0] );
          } catch( Exception e )
          {
              try
              {
                  m = this.obj.getClass().getMethod(
                      methodName2,
                      new Class[0] );
              }
              catch( Exception ee )
              {
                  throw new AttributeNotFoundException();
              }
          }
          try
          {
              return m.invoke( this.obj, new Object[0] );
          }
          catch( Exception e )
          {
              throw new MBeanException( e );
          }
      }
  
      /**
       * Sets the value of a specific attribute of the Dynamic MBean
       */
      public void setAttribute( Attribute attribute )
          throws AttributeNotFoundException, InvalidAttributeValueException,
          MBeanException, ReflectionException
      {
          final String methodName = "set" + attribute.getName().substring(0,1).toUpperCase() + attribute.getName().substring(1);
          Method m;
          try
          {
              m = this.obj.getClass().getMethod(
                  methodName,
                  new Class[] { attribute.getValue().getClass() } );
          } catch( Exception e )
          {
              throw new AttributeNotFoundException();
          }
          try
          {
              m.invoke( this.obj, new Object[] { attribute.getValue() } );
          }
          catch( Exception e )
          {
              throw new MBeanException( e );
          }
      }
  
      /**
       * Enables the values of several attributes of the Dynamic MBean.
       */
      public AttributeList getAttributes( String[] atts )
      {
          AttributeList attList = new AttributeList();
          for( int i = 0; i < atts.length; i++ )
          {
              final String name = atts[i];
              final String methodName = "get" + atts[i].substring(0,1).toUpperCase() + atts[i].substring(1);
              final String methodName2 = "is" + atts[i].substring(0,1).toUpperCase() + atts[i].substring(1);
              Object value = null;
              Method m;
              try { m = this.obj.getClass().getMethod( methodName, new Class[0] ); }
              catch( Exception e )
              {
                  try { m = this.obj.getClass().getMethod( methodName2, new Class[0] ); }
                  catch( Exception ee ) { continue; }
              }
              try { value = m.invoke(this.obj, new Object[0] ); }
              catch( Exception e ) {}
              if( value != null )
                  attList.add( new Attribute( name, value ) );
          }
          return attList;
      }
  
      /**
       * Sets the values of several attributes of the Dynamic MBean.
       */
      public AttributeList setAttributes( AttributeList atts )
      {
          AttributeList attList = new AttributeList();
          for( int i = 0; i < atts.size(); i++ )
          {
              final Attribute att = (Attribute)atts.get(i);
              final String name = att.getName();
              final String methodName = "set" + name.substring(0,1).toUpperCase() + name.substring(1);
              Object value = null;
              Method m;
              try { m = this.obj.getClass().getMethod( methodName, new Class[] { att.getValue().getClass() } ); }
              catch( Exception e ) { continue; }
              try { value = m.invoke(this.obj, new Object[] { att.getValue() } ); }
              catch( Exception e ) {}
              if( value != null )
                  attList.add( new Attribute( name, value ) );
          }
          return attList;
      }
  
      /**
       * Allows an action to be invoked on the Dynamic MBean.
       *
       * @param actionName The name of the action to be invoked.
       * @param params An array containing the parameters to be set when the action is
       * invoked.
       * @param signature An array containing the signature of the action. The class objects will
       * be loaded through the same class loader as the one used for loading the
       * MBean on which the action is invoked.
       *
       * @return  The object returned by the action, which represents the result of
       * invoking the action on the MBean specified.
       * @exception MBeanException  Wraps a <CODE>java.lang.Exception</CODE> thrown by the MBean's invoked method.
       * @exception ReflectionException  Wraps a <CODEjava.lang.Exception</CODE thrown while trying to invoke the method
       */
      public Object invoke( String actionName, Object[] params, String[] signature )
          throws MBeanException, ReflectionException
      {
          Method m;
          try
          {
              Class[] classes = new Class[signature.length];
              for( int i = 0; i < classes.length; i++ )
              {
                  classes[i] = Class.forName( signature[i] );
              }
              m = this.obj.getClass().getMethod( actionName, classes );
          }
          catch( Exception e )
          {
              throw new ReflectionException( e );
          }
          try
          {
              return m.invoke( this.obj, params );
          }
          catch( Exception e )
          {
              throw new MBeanException( e );
          }
      }
  
      /**
       * Provides the exposed attributes and actions of the Dynamic MBean using an MBeanInfo object.
       *
       * @return  An instance of <CODE>MBeanInfo</CODE> allowing all attributes and actions
       * exposed by this Dynamic MBean to be retrieved.
       */
      public MBeanInfo getMBeanInfo()
      {
          return this.mBeanInfo;
      }
  
      //////////////////////
      /// HELPER METHODS ///
      //////////////////////
      /**
       * Creates an MBeanConstructorInfo array with an entry for each of the
       * supplied constructors.
       */
      protected MBeanConstructorInfo[] createConstructorInfo( Constructor[] constructors )
      {
          final MBeanConstructorInfo[] info = new MBeanConstructorInfo[constructors.length];
  
          for( int i = 0; i < constructors.length; i++ )
          {
              info[i] = new MBeanConstructorInfo(
                  "Creates a new instance of this MBean.",constructors[i] );
          }
          return info;
      }
      /**
       * Creates an MBeanOperationInfo array with an entry for each supplied
       * method that is an operation. A method is an operation when it is not
       * named getXxxx, setXxxx or isXxxx.
       */
      protected MBeanOperationInfo[] createOperationInfo( Method[] methods )
      {
          // getXXX, setXXX and isXXX are not operations, so remove those
          final Method[] ops = getOperationFor( methods );
  
          MBeanOperationInfo[] info = new MBeanOperationInfo[ops.length];
  
          for( int i = 0; i < ops.length; i++ )
          {
              info[i] = new MBeanOperationInfo(
                  "Call a method on this MBean", ops[i] );
          }
          return info;
      }
      /**
       * Extract all the Methods from the supplied array that are
       * operations.
       */
      protected Method[] getOperationFor( Method[] methods )
      {
          final ArrayList ops = new ArrayList();
          for( int i = 0; i < methods.length; i++ )
          {
              final String name = methods[i].getName();
              final String uppercaseChars = new String("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
              final boolean isGetter =
                  ( name.startsWith("get") &&
                    ( uppercaseChars.indexOf( name.substring(3,4) ) != -1 ) );
              final boolean isSetter =
                  ( name.startsWith("set") &&
                    ( uppercaseChars.indexOf( name.substring(3,4) ) != -1 ) );
              final boolean isIs =
                  ( name.startsWith("is") &&
                    ( uppercaseChars.indexOf( name.substring(2,3) ) != -1 ) );
              if( !(isGetter || isSetter || isIs) )
              {
                  ops.add(methods[i]);
              }
          }
          return (Method[])ops.toArray();
      }
      /**
       * Creates an MBeanAttributeInfo array with an entry for every attribute.
       * The exposed attributes are found by taking a look at all methods that
       * are named getXxx and isXxx. While this method finds the corresponding
       * setXxx method for these, it does not add an entry into the returned
       * array for write-only attributes.
       *
       * TODO:
       * @throws NotCompliantMBeanException when the getXxx, isXxx or setXxx don't have the right number of arguments (0, 0 and 1, respectively).
       */
      protected MBeanAttributeInfo[] createAttributeInfo( Method[] methods )
          throws NotCompliantMBeanException
      {
          // only getXXX, setXXX and isXXX expose attributes, so remove
          // all others
          final Method[][] m = getAttributeFor( methods );
  
          // all getter and isser methods mean an attribute
          final MBeanAttributeInfo[] info = new MBeanAttributeInfo[(m[0].length+m[2].length)];
          // fill info from this point
          int beginIndex = 0;
  
          // create MBeanAttributeInfo's for all getter methods
          for( int i = 0; i < m[0].length; i++ )
          {
              final Method getter = m[0][i];
              String attributeName = getter.getName().substring(3);
              Method setter = null;
              for( int j = 0; j < m[1].length; j++ )
              {
                  String setterName = m[1][j].getName();
                  if( setterName.equals( "set"+attributeName ) )
                  {
                      setter = m[0][j];
                      break;
                  }
              }
              attributeName = attributeName.substring(0,1).toLowerCase() + attributeName.substring(1);
              try {
                  info[beginIndex] = new MBeanAttributeInfo( attributeName, "", getter, setter );
              }
              catch( IntrospectionException ie )
              {
                  throw new NotCompliantMBeanException();
              }
              beginIndex++;
          }
          // create MBeanAttributeInfo's for all isser methods
          for( int i = 0; i < m[2].length; i++ )
          {
              final Method isser = m[2][i];
              final String attributeName = isser.getName().substring(2);
              Method setter = null;
              for( int j = 0; j < m[1].length; j++ )
              {
                  String setterName = m[1][j].getName();
                  if( setterName.equals( "set"+attributeName ) )
                  {
                      setter = m[0][j];
                      break;
                  }
              }
              try
              {
              info[beginIndex] = new MBeanAttributeInfo( attributeName, "", isser, setter );
              }
              catch( IntrospectionException ie )
              {
                  throw new NotCompliantMBeanException();
              }
              beginIndex++;
          }
          return info;
      }
      /**
       * Extract all Methods from the supplied array that are of
       * the form getXxx, setXxx or isXxx.
       *
       * @return A multi-dimensional array, with the first element being an array of getter methods, the second element being an array of setter methods, and the third element being an array of isser methods.
       */
      protected Method[][] getAttributeFor( Method[] methods )
      {
          final ArrayList getters = new ArrayList();
          final ArrayList setters = new ArrayList();
          final ArrayList issers = new ArrayList();
  
          Method[][] m = new Method[3][];
          for( int i = 0; i < methods.length; i++ )
          {
              final String name = methods[i].getName();
              final String uppercaseChars = new String("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
              final boolean isGetter =
                  ( name.startsWith("get") &&
                    ( uppercaseChars.indexOf( name.substring(3,4) ) != -1 ) );
              final boolean isSetter =
                  ( name.startsWith("set") &&
                    ( uppercaseChars.indexOf( name.substring(3,4) ) != -1 ) );
              final boolean isIs =
                  ( name.startsWith("is") &&
                    ( uppercaseChars.indexOf( name.substring(2,3) ) != -1 ) );
              if( isGetter )
                  getters.add(methods[i]);
              if( isSetter )
                  setters.add(methods[i]);
              if( isIs )
                  issers.add(methods[i]);
          }
          m[0] = (Method[])getters.toArray();
          m[1] = (Method[])setters.toArray();
          m[2] = (Method[])issers.toArray();
          return m;
      }
      protected void createMBeanInfo()
      {
          this.mBeanInfo = new MBeanInfo(
              this.obj.getClass().getName(),
              this.description,
              this.attributes,
              this.constructors,
              this.operations,
              this.notifications );
      }
   }
  
  
  

---------------------------------------------------------------------
To unsubscribe, e-mail: avalon-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: avalon-dev-help@jakarta.apache.org