按照"JMX in Action"这本书写了一个最简单的HelloWorld的例子,应该可以很好的诠释JMX的3层模型。(去年年末写的,整理时候很幸运发现了这个例子)
代码部分:
Instrumental Layer:
首先,我们定义一个MBean的接口:它包含了一些setter/getter和业务方法的声明:
- /*
- * This file is confidential by Charles.Wang
- * Copyright belongs to Charles.wang
- * You can make contact with Charles.Wang (charles_wang888@126.com)
- */
- package com.charles.jmx;
- /**
- * @author charles.wang (Senior Software Engineer)
- * mailto: charles_wang888@126.com
- * @created 2011-12-31 6:36:57
- *
- * 这是MBean的接口
- * 它定义了这个MBean的方法,其中1组getter/setter方法和一个业务方法(printGreeting)
- *
- */
- public interface HelloWorldMBean {
- public void setGreeting(String greeting);
- public String getGreeting();
- public void printGreeting();
- }
然后我们实现了这个定义MBean实现类,要特别注意的是,因为我们想演示MBean的通知能力,所以我们让其扩展自NotificationBroadcasterSupport类从而它可以发送通知并且可以注册别的MBean为自己广播通知的监听器:
- /*
- * This file is confidential by Charles.Wang
- * Copyright belongs to Charles.wang
- * You can make contact with Charles.Wang (charles_wang888@126.com)
- */
- package com.charles.jmx;
- import javax.management.Notification;
- import javax.management.NotificationBroadcasterSupport;
- /**
- * @author charles.wang (Senior Software Engineer)
- * mailto: charles_wang888@126.com
- * @created 2011-12-31 6:39:23
- *
- * Description: 这是MBean接口的实现类,它是一个Standard MBean,
- * 因为考虑到MBean要和其他Bean或者Adaptor进行基于消息的交互,所以必须让这个MBean能够发送通知
- * 所以这个MBean必须具备广播通知从而让接收者监听的功能,所以让其扩展自notificationbroadcasterSupport类
- */
- public class HelloWorld extends NotificationBroadcasterSupport implements
- HelloWorldMBean {
- private String greeting = "null";
- public HelloWorld() {
- this.greeting = "Hello World! I am a standard MBean";
- }
- public HelloWorld(String greeting) {
- this.greeting = greeting;
- }
- /**
- * 这个方法用于设定这个MBean的问候语,这其中代码就演示了如何让这个MBean发送通知
- *
- */
- public void setGreeting(String greeting) {
- // TODO Auto-generated method stub
- System.out.println("HelloWorldBean->setGreeting(String) is invoked...");
- this.greeting = greeting;
- //以下这段代码显示了MBean如何发送通知
- //首先,它创建了一个通知对象,其构造函数包括:
- // 第一个参数(type),用于唯一标示一个通知
- //第二个参数(source),表示由哪个MBean来发出这个通知(必须是MBean实例名或者是MBean实例的ObjectName)
- //第三个参数(sequence),表示这个通知的序列号
- //第四个参数(timestamp),表示这个通知发出的时间戳
- //第五个参数(message),表示这个通知包含的消息内容
- Notification notification = new Notification(
- "com.charles.helloWorld.test", this, -1,
- System.currentTimeMillis(), greeting);
- System.out.println("HelloWorldMBean->Before sending notification...");
- //等创建好通知对象之后,让这个MBean吧这个通知广播出去,这样所有该MBean的监听者就可以接受到这个通知
- //谁是这个MBean的监听器呢,要在对方的MBean里面用 这个mbean实例.addNotificationListener()来注册
- sendNotification(notification);
- System.out.println("HelloWorldMBean->After sending notification...");
- }
- /*
- * (non-Javadoc)
- *
- * @see com.charles.jmx.HelloWorldBean#getGreeting()
- */
- public String getGreeting() {
- // TODO Auto-generated method stub
- return greeting;
- }
- /*
- * (non-Javadoc)
- *
- * @see com.charles.jmx.HelloWorldBean#printGreeting()
- */
- @Override
- public void printGreeting() {
- // TODO Auto-generated method stub
- System.out.println(greeting);
- }
- }
下面我们开始构建 Agent Layer和Distributed Layer:
Agent Layer我们只要定义一个JMXAgent,然后让其创建一个MBeanServer就可以了,对于Distributed Layer,因为它是负责对外交互的,因为我们想要一个HTML客户端可以来访问MBean,所以我们定义了一个HTMLAdaptor,并且让其注册到MBeanServer。值得提出的是,为了让我们这个JMXAgent可以监听先前的创建的HelloWorld MBean发出来的通知,我们必须让其实现NotificationListener接口,并且实现handleNotification()方法:
- /*
- * This file is confidential by Charles.Wang
- * Copyright belongs to Charles.wang
- * You can make contact with Charles.Wang (charles_wang888@126.com)
- */
- package com.charles.jmx;
- import javax.management.MBeanServer;
- import javax.management.MBeanServerFactory;
- import javax.management.Notification;
- import javax.management.NotificationBroadcasterSupport;
- import javax.management.NotificationListener;
- import javax.management.ObjectName;
- import com.sun.jdmk.comm.HtmlAdaptorServer;
- /**
- * @author charles.wang (Senior Software Engineer)
- * mailto: charles_wang888@126.com
- * @created 2011-12-31 6:46:20
- *
- * Description: 这个JMXAgent会创建MBeanServer,来作为MBean的容器,
- * 并且它实现了NotificationListener接口从而可以监听到来自MBean的消息
- *
- */
- public class HelloAgent implements NotificationListener {
- //MBeanServer是HelloAgent创建并且属于HelloAgent的
- private MBeanServer mbs = null;
- /**
- * HelloAgent做了以下事情:
- * (1)创建MBeanServer作为MBean的容器
- * (2)它创建了一个HTMLAdaptor来处理HTML客户端的连接(说白了就是让我们的Web Browser能看这些Bean信息)
- * (回想我们有两种方式,一种是adaptor,一种是connector,这里是adaptor,所以只存在在 Agent中)
- * (3)因为我们有一个MBean叫HelloWorld,所以我们创建这个Mbean的实例,并且注册到MBeanServer中
- */
- public HelloAgent() {
- //用MbeanServerFactory来创建一个MBeanServer,这个参数是Agent的domain名称,
- //这个domain名称可以用来标识一组MBean,
- //创建完这个MBeanserver之后,它就可以用来注册,存储,查询和操作MBean
- mbs = MBeanServerFactory.createMBeanServer("HelloAgent");
- //现在我们有了一个Agent,那么如何访问它呢,我们需要一个管理应用程序(Management Application)
- //所以我们我们创建一个adaptor,从而可以通过html方式(也就是web浏览器)来访问Agent
- HtmlAdaptorServer adapter = new HtmlAdaptorServer();
- // 现在,我们到了instrumental layer,既然我们已经定义了MBean的接口和实现类,所以我们这里将MBean实例化
- HelloWorld hw = new HelloWorld();
- //光注册MBean还不够,我们必须还要区分多个MBean,这时候我们就需要ObjectName帮忙了
- //ObjectName提供了MBean的命名系统
- //每一个ObjectName有两部分组成,一个是domain name,一个是kev-value对
- //domainName不一定要和MBeanServer的domain name一致
- //key-value对则是用来唯一标识MBean以及提供MBean的信息
- ObjectName adapterName = null;
- ObjectName helloWorldName = null;
- // 所以,我们这里分别为adaptor和HelloWorld MBean注册ObjectName
- try {
- //我们为adapter来分配一个ObjectName
- //新建的adapterName有它的domain name
- //然后key-value对之间用逗号分开,这些属性不一定要是MBean的真实属性,但是必须是唯一,从而与同MBeanServer的其他MBean区分
- adapterName = new ObjectName(
- "HelloAgent:name=htmladapter,port=9092");
- //因为这是个adaptor,所以必须绑定端口从而对外提供服务(这里是HTML访问服务)
- adapter.setPort(9092);
- //最终吧这种adaptor类型的MBean实例关联到ObjectName,并且注册到MBeanServer上
- mbs.registerMBean(adapter, adapterName);
- //为了让管理应用程序通过指定的端口可以和这个adaptor交互,必须将这adpator启动起来
- //这样之后,这个adaptor就可以接受客户端的调用了
- adapter.start();
- //同样,创建helloWorldMbean的ObjectName,并且绑定到helloWorldMbean实例上并且注册到MBeanServer
- //MBeanServer中的所有MBean的ObjectName必须是唯一的
- helloWorldName = new ObjectName("HelloAgent:name=helloWorld1");
- mbs.registerMBean(hw, helloWorldName);
- //因为我们在先前HelloWorld MBean的类定义中已经让其实现了NotificationBroadcasterSupport 接口,也就是说那个MBean已经可以发送消息
- //所以,我们这里为了让这个HelloAgent能监听HelloWorld MBean的定义,就必须让HelloWorldMBean其添加自身为其监听器
- //注册HelloAgent成为helloWorldBean的监听器,从而它可以监听helloWorldBean的事件
- hw.addNotificationListener(this, null, null);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- /**
- * 定义了一个Main方法从而可以让这个Agent以standalone的形式启动
- */
- public static void main(String[] args) {
- System.out.println("HelloAgent is running ");
- HelloAgent agent = new HelloAgent();
- }
- /**
- * 因为我们这个HelloAgent已经实现了NotificationListener接口,所以必须实现接口中定义的方法 handleNotification
- * 这个方法定义了当通知到的这个监听器时候,所采取的动作
- */
- @Override
- public void handleNotification(Notification notification, Object handback) {
- // TODO Auto-generated method stub
- //我们这里动作逻辑很简单,仅仅打印出通知的类型和通知的消息
- System.out.println("Receiving Notification...");
- System.out.println("Notification Type: " + notification.getType());
- System.out.println("Notification Message: " + notification.getMessage());
- }
- }
最后,因为我们采用的是Sun的Reference Implementation,所以我们只要启动这个Agent,这样Agent就可以通过在里面的htmlAdaptor和浏览器进行交互了:
演示部分:
我们打开浏览器,并且输入之前绑定的端口,主机名因为是部署在本地,所以localhost:
这里我们可以看到有3种视图:分别是
Agent View-我们可以看到Agent的信息,包括它所包含的MBeanServer以及其中的MBean,它提供了Filter从而可以过滤要看到的MBean,支持? *等通配符
MBean View-这个页面包含了某个MBean的细节信息,点击任何一个MBean就进去了,其中MBeanServerDelegate是一个特殊的MBean,可以让我们看到Reference Implementation的细节(获取属性名信息时用到了反射机制),我们也可以尽情修改字段或者调用方法:
Admin View-这个页面可以让我们为某个Agent注册新的MBean
最后演示下MBean Notification的实验:
因为在MBean实现类中,我们看到是在设置greeting字段(setGreeting())时触发了通知,所以我们打开helloWorldMBean的MBean视图,把Greeting字段的内容改为“Charles changed the greeting on May 12,2012”,然后点击"Apply":
因为我们的HelloAgent已经注册为HelloWorldMBean的监听器,所以我们到HelloAgent的控制台中看结果,果然如预期的: