JMX in action第二篇

2019-07-01 15:12:26 浏览数 (2)

一,DynamicMBean

其实一看到Dynamic这个词就基本上确定了,就是反射那一套,不外乎属性获取,设定,方法调用等等,但是这个在使用中是至关重要的,因为现有系统如果都想把接口改造成符合Standard MBean方式的,不太现实,而利用DynamicMBean,通过反射将需要管理的Bean动态生成,对现有系统减少侵入性,也减少了问题产生的可能。来看看DynamicMBean接口

代码语言:javascript复制
public interface DynamicMBean {


    /**
     * Obtain the value of a specific attribute of the Dynamic MBean.
     *
     * @param attribute The name of the attribute to be retrieved
     *
     * @return  The value of the attribute retrieved.
     *
     * @exception AttributeNotFoundException
     * @exception MBeanException  Wraps a <CODE>java.lang.Exception</CODE> thrown by the MBean's getter.
     * @exception ReflectionException  Wraps a <CODE>java.lang.Exception</CODE> thrown while trying to invoke the getter.
     *
     * @see #setAttribute
     */
    public Object getAttribute(String attribute) throws AttributeNotFoundException,
        MBeanException, ReflectionException;

    /**
     * Set the value of a specific attribute of the Dynamic MBean.
     *
     * @param attribute The identification of the attribute to
     * be set and  the value it is to be set to.
     *
     * @exception AttributeNotFoundException
     * @exception InvalidAttributeValueException
     * @exception MBeanException Wraps a <CODE>java.lang.Exception</CODE> thrown by the MBean's setter.
     * @exception ReflectionException Wraps a <CODE>java.lang.Exception</CODE> thrown while trying to invoke the MBean's setter.
     *
     * @see #getAttribute
     */
    public void setAttribute(Attribute attribute) throws AttributeNotFoundException,
        InvalidAttributeValueException, MBeanException, ReflectionException ;

    /**
     * Get the values of several attributes of the Dynamic MBean.
     *
     * @param attributes A list of the attributes to be retrieved.
     *
     * @return  The list of attributes retrieved.
     *
     * @see #setAttributes
     */
    public AttributeList getAttributes(String[] attributes);

    /**
     * Sets the values of several attributes of the Dynamic MBean.
     *
     * @param attributes A list of attributes: The identification of the
     * attributes to be set and  the values they are to be set to.
     *
     * @return  The list of attributes that were set, with their new values.
     *
     * @see #getAttributes
     */
    public AttributeList setAttributes(AttributeList attributes);

    /**
     * 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 <CODE>java.lang.Exception</CODE> thrown while trying to invoke the method
     */
    public Object invoke(String actionName, Object params[], String signature[])
        throws MBeanException, ReflectionException ;

    /**
     * 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();

 }

JMX定义几个类是我们需要看的,其一是Attribute,是jmx封装的name-value的类,其二就是MBeanInfo这个了,这个是比较重要的一个类,无论是DynamicMBean还是StandardMBean,在注册到MBean Server中去后,都会统一的处理成MBeanInfo的形式,是的Server可以不用去区分我们的实现而用统一的方式去管理MBean

来看看MBeanInfo拥有的属性:

代码语言:javascript复制
    private transient Descriptor descriptor;

    /**
     * @serial The human readable description of the class.
     */
    private final String description;

    /**
     * @serial The MBean qualified name.
     */
    private final String className;

    /**
     * @serial The MBean attribute descriptors.
     */
    private final MBeanAttributeInfo[] attributes;

    /**
     * @serial The MBean operation descriptors.
     */
    private final MBeanOperationInfo[] operations;

     /**
     * @serial The MBean constructor descriptors.
     */
    private final MBeanConstructorInfo[] constructors;

    /**
     * @serial The MBean notification descriptors.
     */
    private final MBeanNotificationInfo[] notifications;

可以看到,MBeanInfo其实就是对一个类的描述,包含了类名className,属性的集合attributes,操作的集合operations,构造器的集合constructors,还一个是通知集合notifications,这个是jmx中事件相关的属性

DynamicMBean并没有什么神秘的,关键还是使用这个接口的方式,如果对于每一个类都实现这个接口,然后构造一大堆类的元数据,也是挺痛苦的一件事,JIA提供了一个DynamicMBeanSupport类通过反射对类的属性方法获取简化了一下,

代码语言:javascript复制
public class DynamicMBeanSupport implements DynamicMBean {

    List<MBeanAttributeInfo> attributeList = new ArrayList<MBeanAttributeInfo>();
    List<MBeanOperationInfo> operationInfos = new ArrayList<MBeanOperationInfo>();
    List<MBeanConstructorInfo> constructorInfos = new ArrayList<MBeanConstructorInfo>();
//    List<MBeanNotificationInfo> notificationInfos = new ArrayList<MBeanNotificationInfo>();
    MBeanInfo mBeanInfo = null;
    String description = null;
    volatile boolean changedEver = false;

    public DynamicMBeanSupport(String description){
        this.description = description;
    }
    /**
     * Obtain the value of a specific attribute of the Dynamic MBean.
     *
     * @param attribute The name of the attribute to be retrieved
     * @return The value of the attribute retrieved.
     * @throws AttributeNotFoundException
     * @throws MBeanException             Wraps a <CODE>java.lang.Exception</CODE> thrown by the MBean's getter.
     * @throws ReflectionException        Wraps a <CODE>java.lang.Exception</CODE> thrown while trying to invoke the getter.
     * @see #setAttribute
     */
    @Override
    public Object getAttribute(String attribute) throws AttributeNotFoundException, MBeanException, ReflectionException {
        //有两种办法,一种获取Field然后反射获得,一种反射getter方法
        Class cls=this.getClass();
        String getterName = "get" attribute;//jmx中属性头字母是大写的
        try {
            Method method = cls.getMethod(getterName,null);
            return method.invoke(this,null);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

        return null;
    }

    /**
     * Set the value of a specific attribute of the Dynamic MBean.
     *
     * @param attribute The identification of the attribute to
     *                  be set and  the value it is to be set to.
     * @throws AttributeNotFoundException
     * @throws InvalidAttributeValueException
     * @throws MBeanException                 Wraps a <CODE>java.lang.Exception</CODE> thrown by the MBean's setter.
     * @throws ReflectionException            Wraps a <CODE>java.lang.Exception</CODE> thrown while trying to invoke the MBean's setter.
     * @see #getAttribute
     */
    @Override
    public void setAttribute(Attribute attribute) throws AttributeNotFoundException, InvalidAttributeValueException, MBeanException, ReflectionException {
        //有两种办法,一种获取Field然后反射获得,一种反射setter方法
        Class cls=this.getClass();
        String attributeName = attribute.getName();
        Object attributeValue = attribute.getValue();

        attributeName = attributeName.substring(0,1).toLowerCase() attributeName.substring(1);

        try {
            Field field = cls.getDeclaredField(attributeName);
            field.setAccessible(true);
            field.set(this,attributeValue);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    /**
     * Get the values of several attributes of the Dynamic MBean.
     *
     * @param attributes A list of the attributes to be retrieved.
     * @return The list of attributes retrieved.
     * @see #setAttributes
     */
    @Override
    public AttributeList getAttributes(String[] attributes) {
        AttributeList attributeList = new AttributeList();
        if(attributes == null || attributes.length == 0){
            return null;
        }
        for(String name : attributes){
            try {
                attributeList.add(new Attribute(name,this.getAttribute(name)));
            } catch (AttributeNotFoundException e) {
                e.printStackTrace();
            } catch (MBeanException e) {
                e.printStackTrace();
            } catch (ReflectionException e) {
                e.printStackTrace();
            }
        }
        return attributeList;
    }

    /**
     * Sets the values of several attributes of the Dynamic MBean.
     *
     * @param attributes A list of attributes: The identification of the
     *                   attributes to be set and  the values they are to be set to.
     * @return The list of attributes that were set, with their new values.
     * @see #getAttributes
     */
    @Override
    public AttributeList setAttributes(AttributeList attributes) {
        if(attributes == null || attributes.isEmpty()){
            return null;
        }
        for (Attribute attribute : attributes.asList()){
            try {
                this.setAttribute(attribute);
            } catch (AttributeNotFoundException e) {
                e.printStackTrace();
            } catch (InvalidAttributeValueException e) {
                e.printStackTrace();
            } catch (MBeanException e) {
                e.printStackTrace();
            } catch (ReflectionException e) {
                e.printStackTrace();
            }
        }
        return attributes;
    }

    /**
     * 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.
     * @throws MBeanException      Wraps a <CODE>java.lang.Exception</CODE> thrown by the MBean's invoked method.
     * @throws ReflectionException Wraps a <CODE>java.lang.Exception</CODE> thrown while trying to invoke the method
     */
    @Override
    public Object invoke(String actionName, Object[] params, String[] signature) throws MBeanException, ReflectionException {
        Class cls = this.getClass();

        //signature is the params classes full name,
        //I wonder if the params class can also infer the signature
        //like blow
//        Class[] paramCls = params ==null ? null : new Class[params.length];
//        if(paramCls != null){
//            for (int i = 0 ; i < params.length ; i   ){
//                paramCls[i] = params[i].getClass();
//            }
//        }

        //just in case , you don't know the inner implementation after all
        Class[] paramCls = params ==null ? null : new Class[signature.length];
        if(paramCls != null){
            for (int i = 0 ; i < signature.length ; i   ) {
                try {
                    paramCls[i] = Class.forName(signature[i]);
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
            }
        }

        try {
            Method method = cls.getDeclaredMethod(actionName,paramCls);
            return method.invoke(this,params);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 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.
     */
    @Override
    public MBeanInfo getMBeanInfo() {
        synchronized (mBeanInfo){
            if(mBeanInfo!=null && !changedEver){
                return mBeanInfo;
            }
        }
        buildMBeanInfo(this.description);
        return mBeanInfo;
    }

    /**
     * methods for subclass to add attribute
     * @param name
     * @param description
     * @param getter
     * @param setter
     * @throws IntrospectionException
     */
    protected void addAttributeInfo(String name,String description,Method getter,Method setter) throws IntrospectionException {
        MBeanAttributeInfo attributeInfo = new MBeanAttributeInfo(name,description,getter,setter);
        synchronized (attributeList){
            changedEver = true;
            attributeList.add(attributeInfo);
        }

    }
    protected void addOperationInfo(String description,Method method){
        MBeanOperationInfo operationInfo = new MBeanOperationInfo(description,method);
        synchronized (operationInfos){
            changedEver = true;
            operationInfos.add(operationInfo);
        }
    }
    protected void addConstructorInfo(String description, Constructor constructor){
        MBeanConstructorInfo constructorInfo = new MBeanConstructorInfo(description,constructor);
        synchronized (constructorInfos){
            changedEver = true;
            constructorInfos.add(constructorInfo);
        }
    }

    /**
     * construct mbeaninfo 
     * @param description
     */
    private void buildMBeanInfo(String description){
        synchronized (mBeanInfo){
            changedEver = false;
            MBeanAttributeInfo [] attributeInfosArray = null;
            MBeanOperationInfo [] operationInfosArray = null;
            MBeanConstructorInfo[] constructorInfosArray = null;
            synchronized (attributeList){
                attributeInfosArray =  new MBeanAttributeInfo[attributeList.size()];
                attributeInfosArray = attributeList.toArray(attributeInfosArray);
            }
            synchronized (operationInfos){
                operationInfosArray = new MBeanOperationInfo[operationInfos.size()];
                operationInfosArray = operationInfos.toArray(operationInfosArray);
            }
            synchronized (constructorInfos) {
                constructorInfosArray = new MBeanConstructorInfo[constructorInfos.size()];
                constructorInfosArray = constructorInfos.toArray(constructorInfosArray);
            }
            this.mBeanInfo = new MBeanInfo(this.getClass().getName(),description,attributeInfosArray,
                    constructorInfosArray,operationInfosArray,null);
        }

    }
}

其实这种暴露属性方法的方式还是比较粗糙的,因为暴露的方法其实还是硬编码在了子类中

tomcat提供了一种配置方式来暴露,使用一个mb ean-descriptor,将需要暴露的方法属性写到里面,用xml digest解析出来,比较直观,在我看来是一种更好的办法。

0 人点赞