按照"JMX in Action"这本书写了一个最简单的HelloWorld的例子,应该可以很好的诠释JMX的3层模型。(去年年末写的,整理时候很幸运发现了这个例子)

 

代码部分:

Instrumental Layer:

首先,我们定义一个MBean的接口:它包含了一些setter/getter和业务方法的声明:

   
   
   
   
  1. /* 
  2.  *  This file is confidential by Charles.Wang 
  3.  *  Copyright belongs to Charles.wang 
  4.  *  You can make contact with Charles.Wang ([email protected]) 
  5.  */ 
  6. package com.charles.jmx; 
  7.  
  8. /** 
  9.  * @author charles.wang (Senior Software Engineer)  
  10.  * mailto: [email protected] 
  11.  * @created 2011-12-31 6:36:57 
  12.  *  
  13.  *  这是MBean的接口 
  14.  *  它定义了这个MBean的方法,其中1组getter/setter方法和一个业务方法(printGreeting) 
  15.  *  
  16.  */ 
  17. public interface HelloWorldMBean { 
  18.  
  19.     public void setGreeting(String greeting); 
  20.  
  21.     public String getGreeting(); 
  22.  
  23.     public void printGreeting(); 
  24.  

 

然后我们实现了这个定义MBean实现类,要特别注意的是,因为我们想演示MBean的通知能力,所以我们让其扩展自NotificationBroadcasterSupport类从而它可以发送通知并且可以注册别的MBean为自己广播通知的监听器:

   
   
   
   
  1. /* 
  2.  *  This file is confidential by Charles.Wang 
  3.  *  Copyright belongs to Charles.wang 
  4.  *  You can make contact with Charles.Wang ([email protected]) 
  5.  */ 
  6. package com.charles.jmx; 
  7.  
  8. import javax.management.Notification; 
  9. import javax.management.NotificationBroadcasterSupport; 
  10.  
  11. /** 
  12.  * @author charles.wang (Senior Software Engineer)  
  13.  *  mailto: [email protected] 
  14.  * @created 2011-12-31  6:39:23 
  15.  *  
  16.  *          Description: 这是MBean接口的实现类,它是一个Standard MBean, 
  17.  *          因为考虑到MBean要和其他Bean或者Adaptor进行基于消息的交互,所以必须让这个MBean能够发送通知 
  18.  *          所以这个MBean必须具备广播通知从而让接收者监听的功能,所以让其扩展自notificationbroadcasterSupport类 
  19.  */ 
  20. public class HelloWorld extends NotificationBroadcasterSupport implements 
  21.         HelloWorldMBean { 
  22.  
  23.     private String greeting = "null"
  24.  
  25.     public HelloWorld() { 
  26.         this.greeting = "Hello World! I am a standard MBean"
  27.     } 
  28.  
  29.     public HelloWorld(String greeting) { 
  30.         this.greeting = greeting; 
  31.     } 
  32.  
  33.     /** 
  34.      *  这个方法用于设定这个MBean的问候语,这其中代码就演示了如何让这个MBean发送通知 
  35.      * 
  36.      */ 
  37.          
  38.     public void setGreeting(String greeting) { 
  39.         // TODO Auto-generated method stub 
  40.  
  41.          
  42.         System.out.println("HelloWorldBean->setGreeting(String) is invoked..."); 
  43.         this.greeting = greeting; 
  44.  
  45.         //以下这段代码显示了MBean如何发送通知 
  46.         //首先,它创建了一个通知对象,其构造函数包括: 
  47.         // 第一个参数(type),用于唯一标示一个通知 
  48.         //第二个参数(source),表示由哪个MBean来发出这个通知(必须是MBean实例名或者是MBean实例的ObjectName) 
  49.         //第三个参数(sequence),表示这个通知的序列号 
  50.         //第四个参数(timestamp),表示这个通知发出的时间戳 
  51.         //第五个参数(message),表示这个通知包含的消息内容 
  52.         Notification notification = new Notification( 
  53.                 "com.charles.helloWorld.test"this, -1
  54.                 System.currentTimeMillis(), greeting); 
  55.          
  56.         System.out.println("HelloWorldMBean->Before sending notification..."); 
  57.          
  58.         //等创建好通知对象之后,让这个MBean吧这个通知广播出去,这样所有该MBean的监听者就可以接受到这个通知 
  59.         //谁是这个MBean的监听器呢,要在对方的MBean里面用 这个mbean实例.addNotificationListener()来注册 
  60.         sendNotification(notification); 
  61.          
  62.         System.out.println("HelloWorldMBean->After sending notification..."); 
  63.     } 
  64.  
  65.     /* 
  66.      * (non-Javadoc) 
  67.      *  
  68.      * @see com.charles.jmx.HelloWorldBean#getGreeting() 
  69.      */ 
  70.      
  71.     public String getGreeting() { 
  72.         // TODO Auto-generated method stub 
  73.         return greeting; 
  74.     } 
  75.  
  76.     /* 
  77.      * (non-Javadoc) 
  78.      *  
  79.      * @see com.charles.jmx.HelloWorldBean#printGreeting() 
  80.      */ 
  81.     @Override 
  82.     public void printGreeting() { 
  83.         // TODO Auto-generated method stub 
  84.         System.out.println(greeting); 
  85.          
  86.     } 
  87.      
  88.      
  89.  

 

下面我们开始构建 Agent LayerDistributed Layer:

Agent Layer我们只要定义一个JMXAgent,然后让其创建一个MBeanServer就可以了,对于Distributed Layer,因为它是负责对外交互的,因为我们想要一个HTML客户端可以来访问MBean,所以我们定义了一个HTMLAdaptor,并且让其注册到MBeanServer。值得提出的是,为了让我们这个JMXAgent可以监听先前的创建的HelloWorld MBean发出来的通知,我们必须让其实现NotificationListener接口,并且实现handleNotification()方法:

 

   
   
   
   
  1. /* 
  2.  *  This file is confidential by Charles.Wang 
  3.  *  Copyright belongs to Charles.wang 
  4.  *  You can make contact with Charles.Wang ([email protected]) 
  5.  */ 
  6. package com.charles.jmx; 
  7.  
  8. import javax.management.MBeanServer; 
  9. import javax.management.MBeanServerFactory; 
  10. import javax.management.Notification; 
  11. import javax.management.NotificationBroadcasterSupport; 
  12. import javax.management.NotificationListener; 
  13. import javax.management.ObjectName; 
  14.  
  15. import com.sun.jdmk.comm.HtmlAdaptorServer; 
  16.  
  17. /** 
  18.  * @author charles.wang (Senior Software Engineer)  
  19.  * mailto:  [email protected] 
  20.  * @created 2011-12-31 6:46:20 
  21.  *  
  22.  *          Description: 这个JMXAgent会创建MBeanServer,来作为MBean的容器, 
  23.  *          并且它实现了NotificationListener接口从而可以监听到来自MBean的消息 
  24.  *           
  25.  */ 
  26. public class HelloAgent implements NotificationListener { 
  27.  
  28.  
  29.  
  30.     //MBeanServer是HelloAgent创建并且属于HelloAgent的 
  31.     private MBeanServer mbs = null
  32.  
  33.     /** 
  34.      *  HelloAgent做了以下事情: 
  35.      *  (1)创建MBeanServer作为MBean的容器 
  36.      *  (2)它创建了一个HTMLAdaptor来处理HTML客户端的连接(说白了就是让我们的Web Browser能看这些Bean信息) 
  37.      *  (回想我们有两种方式,一种是adaptor,一种是connector,这里是adaptor,所以只存在在 Agent中) 
  38.      *  (3)因为我们有一个MBean叫HelloWorld,所以我们创建这个Mbean的实例,并且注册到MBeanServer中 
  39.      */ 
  40.     public HelloAgent() { 
  41.  
  42.         //用MbeanServerFactory来创建一个MBeanServer,这个参数是Agent的domain名称, 
  43.         //这个domain名称可以用来标识一组MBean, 
  44.         //创建完这个MBeanserver之后,它就可以用来注册,存储,查询和操作MBean 
  45.         mbs = MBeanServerFactory.createMBeanServer("HelloAgent"); 
  46.  
  47.         //现在我们有了一个Agent,那么如何访问它呢,我们需要一个管理应用程序(Management Application) 
  48.         //所以我们我们创建一个adaptor,从而可以通过html方式(也就是web浏览器)来访问Agent 
  49.         HtmlAdaptorServer adapter = new HtmlAdaptorServer(); 
  50.  
  51.         // 现在,我们到了instrumental layer,既然我们已经定义了MBean的接口和实现类,所以我们这里将MBean实例化 
  52.         HelloWorld hw = new HelloWorld(); 
  53.  
  54.      
  55.         //光注册MBean还不够,我们必须还要区分多个MBean,这时候我们就需要ObjectName帮忙了 
  56.         //ObjectName提供了MBean的命名系统 
  57.         //每一个ObjectName有两部分组成,一个是domain name,一个是kev-value对 
  58.         //domainName不一定要和MBeanServer的domain name一致 
  59.         //key-value对则是用来唯一标识MBean以及提供MBean的信息 
  60.         ObjectName adapterName = null
  61.         ObjectName helloWorldName = null
  62.  
  63.         // 所以,我们这里分别为adaptor和HelloWorld MBean注册ObjectName 
  64.         try { 
  65.  
  66.              
  67.             //我们为adapter来分配一个ObjectName 
  68.             //新建的adapterName有它的domain name 
  69.             //然后key-value对之间用逗号分开,这些属性不一定要是MBean的真实属性,但是必须是唯一,从而与同MBeanServer的其他MBean区分 
  70.             adapterName = new ObjectName( 
  71.                     "HelloAgent:name=htmladapter,port=9092"); 
  72.             //因为这是个adaptor,所以必须绑定端口从而对外提供服务(这里是HTML访问服务) 
  73.             adapter.setPort(9092); 
  74.             //最终吧这种adaptor类型的MBean实例关联到ObjectName,并且注册到MBeanServer上 
  75.             mbs.registerMBean(adapter, adapterName); 
  76.              
  77.             //为了让管理应用程序通过指定的端口可以和这个adaptor交互,必须将这adpator启动起来 
  78.             //这样之后,这个adaptor就可以接受客户端的调用了 
  79.             adapter.start(); 
  80.  
  81.             //同样,创建helloWorldMbean的ObjectName,并且绑定到helloWorldMbean实例上并且注册到MBeanServer 
  82.             //MBeanServer中的所有MBean的ObjectName必须是唯一的 
  83.             helloWorldName = new ObjectName("HelloAgent:name=helloWorld1"); 
  84.             mbs.registerMBean(hw, helloWorldName); 
  85.  
  86.             //因为我们在先前HelloWorld MBean的类定义中已经让其实现了NotificationBroadcasterSupport 接口,也就是说那个MBean已经可以发送消息 
  87.             //所以,我们这里为了让这个HelloAgent能监听HelloWorld MBean的定义,就必须让HelloWorldMBean其添加自身为其监听器 
  88.             //注册HelloAgent成为helloWorldBean的监听器,从而它可以监听helloWorldBean的事件 
  89.             hw.addNotificationListener(thisnullnull); 
  90.  
  91.         } catch (Exception e) { 
  92.             e.printStackTrace(); 
  93.         } 
  94.     } 
  95.  
  96.     /** 
  97.      * 定义了一个Main方法从而可以让这个Agent以standalone的形式启动 
  98.      */ 
  99.     public static void main(String[] args) { 
  100.         System.out.println("HelloAgent is running "); 
  101.         HelloAgent agent = new HelloAgent(); 
  102.     } 
  103.  
  104.     /** 
  105.      *  因为我们这个HelloAgent已经实现了NotificationListener接口,所以必须实现接口中定义的方法 handleNotification 
  106.      *  这个方法定义了当通知到的这个监听器时候,所采取的动作 
  107.      */ 
  108.     @Override 
  109.     public void handleNotification(Notification notification, Object handback) { 
  110.         // TODO Auto-generated method stub 
  111.  
  112.         //我们这里动作逻辑很简单,仅仅打印出通知的类型和通知的消息 
  113.         System.out.println("Receiving Notification..."); 
  114.         System.out.println("Notification Type: " + notification.getType()); 
  115.         System.out.println("Notification Message: " + notification.getMessage()); 
  116.  
  117.     } 
  118.  
  119.      
  120.      
  121.      

 

最后,因为我们采用的是Sun的Reference Implementation,所以我们只要启动这个Agent,这样Agent就可以通过在里面的htmlAdaptor和浏览器进行交互了:

 

演示部分:

我们打开浏览器,并且输入之前绑定的端口,主机名因为是部署在本地,所以localhost:

(JMX 读书笔记)最袖珍的HelloWorld JMX例子_第1张图片

这里我们可以看到有3种视图:分别是

Agent View-我们可以看到Agent的信息,包括它所包含的MBeanServer以及其中的MBean,它提供了Filter从而可以过滤要看到的MBean,支持? *等通配符

MBean View-这个页面包含了某个MBean的细节信息,点击任何一个MBean就进去了,其中MBeanServerDelegate是一个特殊的MBean,可以让我们看到Reference Implementation的细节(获取属性名信息时用到了反射机制),我们也可以尽情修改字段或者调用方法:

(JMX 读书笔记)最袖珍的HelloWorld JMX例子_第2张图片

Admin View-这个页面可以让我们为某个Agent注册新的MBean

 

最后演示下MBean Notification的实验:

因为在MBean实现类中,我们看到是在设置greeting字段(setGreeting())时触发了通知,所以我们打开helloWorldMBean的MBean视图,把Greeting字段的内容改为“Charles changed the greeting on May 12,2012”,然后点击"Apply":

(JMX 读书笔记)最袖珍的HelloWorld JMX例子_第3张图片

因为我们的HelloAgent已经注册为HelloWorldMBean的监听器,所以我们到HelloAgent的控制台中看结果,果然如预期的:

(JMX 读书笔记)最袖珍的HelloWorld JMX例子_第4张图片