分享

关于JMX的示例

 昵称20874412 2015-02-11
当我们需要编写一个Java程序支持JMX时,可以参考该文章:

1. 整体的框架:
添加JMX的代码的整体框架比较简单

        // 创建一个JMX的Bean Server实例
       //  create a JMX MBean Server server instance
        MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();

如果需要管理的对象叫多,建议可以先创建一个基础的MBean封装类,例如Mina框架中的org.apache.mina.integration.jmx.ObjectMBean类。对不同类型的管理类型继承ObjectMBean类,针对需要管理的对象创建一个合适的封装MBean,例如IoServiceMBean或者IoFilterMBean类。用这样的处理方式的好处是很明显的,就是方便分开管理。

将需要管理的实例取一个ObjectName,这个Name将成为该MBean实例的名字,这里需要遵循MBean的命名规范:
ObjectName acceptorName = new ObjectName( acceptor.getClass().getPackage().getName() +
            ":type=acceptor,name=" + acceptor.getClass().getSimpleName());

然后将该MBean注册到MBeanServer上
mBeanServer.registerMBean( acceptorMBean, acceptorName );

这样,首先就完成了MBean的注册了。

2. 如何在MBean中添加属性、操作和通知
我们知道MBean中的对象可以分为属性、操作和通知三类,所有的信息都是通过MBeanInfo来加入MBean的。下面我们就来看看三种类型的对象是如果添加入MBean中的。

属性:
在MBeanInfo中添加属性信息时代码如下:
List<ModelMBeanAttributeInfo> attributes = new ArrayList<ModelMBeanAttributeInfo>();
通过在List中添加ModelMBeanAttributeInfo即可。那么问题来了,如何能够快速添加呢?

一般来说,我们在添加属性时,将属性分为两类,一类是该类MBean中默认添加的,一类是该类中个别的MBean实例需要特殊添加的。分别用方法:
addAttributes(attributes, source);
addExtraAttributes(attributes);(这个方法您看着写,反正搞一个ModelMBeanAttributeInfo扔进attributes就可以)
这里addAttributes有两个传入值,一个是attributes,也就是我们之前定义的属性List,另一个source是一个泛型,也就是传入的MBean。
对于传入的MBean,我们用
PropertyDescriptor[] pdescs = Introspector.getBeanInfo(type).getPropertyDescriptors();来获得该Bean中的属性描述。拿到Bean中的属性,那么下面干什么就由我们了....
当然,以下的一些过滤方法还是值得效仿的:
            //对于只读属性就算了吧...
            if (pdesc.getReadMethod() == null) {
                continue;
            }
            // 对于管不了的就算了吧,比如这里是Class的属性
            String attrName = pdesc.getName();
            Class<?> attrType = pdesc.getPropertyType();
            if (attrName.equals("class")) {
                continue;
            }
           // 对于没有读功能的就算了吧,至于什么是有读功能?那还不是你说啥就是啥:)
           if (!isReadable(type, attrName)) {
                continue;
            }
            // 看看这个属性能不能继续往下拆,比如Session啦,能的话爸爸就放了,只抓最下面的,谁让最下面的有价值呢:).
            if (isExpandable(type, attrName)) {
                expandAttribute(attributes, object, prefix, pdesc);
                continue;
            }
搞了一堆,然后呢,当然是装进List了
            attributes.add(new ModelMBeanAttributeInfo(fqan, convertType(object.getClass(), attrName, attrType,
                    writable).getName(), pdesc.getShortDescription(), true, writable, false));
这里要特别说说converType方法,这个方法就是一个彻彻底底的类型大解密,怎么说呢,还是看代码吧。

 private Class<?> convertType(Class<?> type, String attrName, Class<?> attrType, boolean writable) {
        if ((attrName != null) && ((attrType == Long.class) || (attrType == long.class))) {
            if (attrName.endsWith("Time") && (attrName.indexOf("Total") < 0) && (attrName.indexOf("Min") < 0)
                    && (attrName.indexOf("Max") < 0) && (attrName.indexOf("Avg") < 0)
                    && (attrName.indexOf("Average") < 0) && !propertyDescriptors.containsKey(attrName + "InMillis")) {
                return Date.class;
            }
        }

        if (IoFilterChain.class.isAssignableFrom(attrType)) {
            return Map.class;
        }

        if (IoFilterChainBuilder.class.isAssignableFrom(attrType)) {
            return Map.class;
        }

        if (!writable) {
            if (Collection.class.isAssignableFrom(attrType) || Map.class.isAssignableFrom(attrType)) {
                if (List.class.isAssignableFrom(attrType)) {
                    return List.class;
                }
                if (Set.class.isAssignableFrom(attrType)) {
                    return Set.class;
                }
                if (Map.class.isAssignableFrom(attrType)) {
                    return Map.class;
                }
                return Collection.class;
            }

            if (attrType.isPrimitive() || Date.class.isAssignableFrom(attrType)
                    || Boolean.class.isAssignableFrom(attrType) || Character.class.isAssignableFrom(attrType)
                    || Number.class.isAssignableFrom(attrType)) {
                if ((attrName == null) || !attrName.endsWith("InMillis")
                        || !propertyDescriptors.containsKey(attrName.substring(0, attrName.length() - 8))) {
                    return attrType;
                }
            }
        }

        return String.class;
    }
窃以为上面这个方法还是很值得拿来直接用的。
到这里,属性搞定!

2. 操作
在MBeanInfo中添加操作信息如下:
List<ModelMBeanOperationInfo> operations = new ArrayList<ModelMBeanOperationInfo>();
和属性类似,也是分为两类一类是该类MBean中默认添加的,一类是该类中个别的MBean实例需要特殊添加的。分别用方法:
addOperations(operations, source);
addExtraOperations(operations);
说到方法就没有属性那么复杂了,应该类本身就有获得方法的函数object.getClass().getMethods()
当然了,这里也有一些函数可以让大家来参考的:
            // 忽略Get和Set方法.
            if (mname.startsWith("is") || mname.startsWith("get") || mname.startsWith("set")) {
                continue;
            }

            // 忽略从Object那里继承过来的方法.
            if (mname.matches("(wait|notify|notifyAll|toString|equals|compareTo|hashCode|clone)")) {
                continue;
            }

            // 忽略一些不想当作方法的函数
            if (!isOperation(mname, m.getParameterTypes())) {
                continue;
            }
和属性不太一样的地方,就是操作都有参数的,这点好理解吧
List<MBeanParameterInfo> signature = new ArrayList<MBeanParameterInfo>();
 int i = 1;
            for (Class<?> paramType : m.getParameterTypes()) {
                String paramName = "p" + (i++);
                if (getPropertyEditor(source.getClass(), paramName, paramType) == null) {
                    continue;
                }
                signature.add(new MBeanParameterInfo(paramName, convertType(null, null, paramType, true).getName(),
                        paramName));
            }
把操作的参数收集一下。
然后将操作注册到ModelMBeanOperationInfo中
            Class<?> returnType = convertType(null, null, m.getReturnType(), false);
            operations.add(new ModelMBeanOperationInfo(m.getName(), m.getName(), signature
                    .toArray(new MBeanParameterInfo[signature.size()]), returnType.getName(),
                    ModelMBeanOperationInfo.ACTION));
这里我们注意到了在impact这里用了 ModelMBeanOperationInfo.ACTION。那么如果是其它的impack怎么办?那么就只能请你重写了。。。

最后加上        operations.add(new ModelMBeanOperationInfo("unregisterMBean", "unregisterMBean", new MBeanParameterInfo[0],
                void.class.getName(), ModelMBeanOperationInfo.ACTION));
总要有取消注册的方法吧?不过我觉得没有必要。。。

最后封装一下,返回一个给new ModelMBeanInfoSupport(className, description,
                attributes.toArray(new ModelMBeanAttributeInfo[attributes.size()]), constructors,
                operations.toArray(new ModelMBeanOperationInfo[operations.size()]), notifications);

3. Notification
Mbean之间的通信是必不可少的,Notification就起到了在Mbean之间沟通桥梁的作用。JMX notification 由四部分组成:
   * Notification 这个相当于一个信息包,封装了需要传递的信息
   * Notification broadcaster 这相当于一个广播器,把消息广播出去
   * Notification listerner 这是一个监听器,用于监听广播出来的Notification消息
   * Notification filter 这是一个过滤器,过滤掉不需要的Notification消息
   Notification broadcaster不需要我们实现,JMX的内部已经有了。Notification filter一般也很少用。
常用接口
NotificationEmitter, 只要实现此接口,就可以发出Notification和订阅Notification. 类NotificationBroadcasterSupport则实现了NotificationEmitter.
NotificationListener, 实现此接口的可以订阅JMX的Notification。
Notification, 消息本身。

Notification采用的是观察者模式,个人认为这在正式的应用中采用的机会不多,因为完全没有这样的必要。但是这里还是需要介绍一下的。
首先MBean需要继承NotificationBroadcasterSupport,用来提供广播服务。
在需要通知的方法中(比如值的改变),new一个Notification,比如
AttributeChangeNotification,这个类是javax.management.Notification的子类,而javax.management.Notification这个类又是java.util.EventObject的子类,由此可以证实上边所说的,JMX通知机制使用了
观察者设计模式.
javax.management.Notification是一个JMX的通知核心类,将来需要扩展或者其他JMX自带
的消息,均集成自此类.
AttributeChangeNotification根据类名可知,是一个属性改变的通知,造方法参数如下:

Object source,                 // 事件源,一直传递到java.util.EventObject的source

long sequenceNumber,   // 通知序号,标识每次通知的计数器

long timeStamp,              // 通知发出的时间戳 

String msg,                     // 通知发送的message

String attributeName,     // 被修改属性名

String attributeType,      // 被修改属性类型

Object oldValue,             // 被修改属性修改以前的值

Object newValue            // 被修改属性修改以后的值

根据观察者模式,由事件与广播组成,所以这里继承了NotificationBroadcasterSupport,来提供广播机制,

调用NotificationBroadcasterSupportr的sendNotification(notification) 发送广播,

广播会根据注册的观察者来对观察者进行逐一通知.

有广播,那么肯定有接收者啦。接收者需要继承NotificationListener接口,然后处理handleNotification(Notification n, Object handback)就可以了。继承了NotificationBroadcasterSupport是可以注册Listener的,用addNotificationListener(new HelloListener(), null, null); 就可以了

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多