JMX IN ACTION 第二章

第二章 JMX 版本的 “Hello World”

(想象你是个音乐发烧友)你决定买一套音响设备,于是先到音响店,挑了一个自己喜欢的型号带回家。接下来,你是哪种类型的人呢?第一种:仔细的打开所有的包装、检查零件清单,然后顺着组装手册一步一步的组装;第二种:打开所有包装、然后自己开始琢磨着所有的连接线开始组装。本章是为第二种类型的人写的,如果你是第一种类型的人,请确信你是否读了第一章,它描述了JMX 框架和基本架构(本章会重点描述)。

本章的目的是为了让你熟悉由Sun 提高的JMX 参考实现( Reference Implementation :RI )。学习完本章,你将会学会管理你的第一个资源,创建一个JMX 代理,并通过浏览器和代理通信。换句话说,你将新建一个MBean,使用MBean Server ,通过Sun 提供的参考实现里的HTML 适配器来管理你的MBean 。

本书的剩余部分假定你已经有JDK1.3 (最低版本)安装在你的机器上了,并且你正确的设置了环境变量。如果需要,你可以从 http://www.javasoft.com 上下载一个JDK。

2.1 开始吧

在开始继续之前,让我们简单的回顾一下JMX 的架构,然后建立一个开发环境。

2.1.1 JMX 架构回顾

    第一章详细的描述了JMX 的架构,并且讨论了它是怎样提供一个管理解决方案的。然而为了确保你掌握了第一章介绍的知识,我们先来回顾一下吧。JMX 架构设计为一个包含三部分或三层的Java 框架,它们一块工作从而提供一个java 的管理解决方案。表2.1 列出了JMX 架构的三层。

表 2.1 JMX 架构的三层组件

描述

分布层( Distributed layer )

包含使管理系统和 JMX 代理通信的组件

代理层( Agent layer )

包括代理和 MBean 服务器

指令层( Instrumentation layer )

包括代表可管理资源的 MBean

 

  图 2.1 说明三层怎样协同工作

 

 

图 2.1 三层协同工作

 

    本章你将会同三层中的各各层的组件打交道。在指令层,将会使用一个MBean ,MBean 是一个Java 对象,它封装了一个资源并将它暴露给管理程序。在代理层,将会使用一个JMX 代理,实际上,你会写一个你自己的代理,它包含你的MBean 。最后,在分布层,将会使用HTML 适配器,它是一个java 对象,它允许管理应用将HTML 作为一个通信协议来和你的代理通信。管理应用是任何对侵入、配置、操纵可管理资源感兴趣的应用。

2.1.2 设置开发环境

    如果你没有Sun 的JMX RI ,可以从http://www.javasoft.com 上面下载,你可以在Products and APIs 的下载区域找到它。下载了zip 文件后,解压到磁盘,会产生一个JMX 的父目录,其中包含以下目录:

§ contrib— — 包括Sun 不声明支持的分发。比如,Sun 在其中提供了一个RMI 连接器,或者说适配器,允许管理应用通过RMI 和JMX 代理通信。

§ jmx— 包括JMX RI/ 例子和文档

    在本书的剩下部分,你需要将你的java 源文件放在JMXBook 目录下,然后可以使用一个批处理来设置path 和classpath ,以便于从JMXBook 下编译和运行例子。批处理文件包含以下内容:

 

set CLASSPATH=c:/JMXBook;

C:/jmx-1_0_1-ribin/jmx/lib/jmxri.jar;

C:/jmx-1_0_1-ribin/jmx/lib/jmxtools.jar;

C:/jmx-1_0_1-ri-bin/contrib/remoting/jar/jmx_remoting.jar;

set PATH=c:/jdk1.3/bin;

 

    批处理文件用于在Windows 环境下设置JMX 环境来编译和运行例子,如果你使用Unix ,你需要相应的修改脚本。如你在脚本所见,我们使用JDK1.3 ,但是每一个例子都可以运行在任何J2SE 版本。当工作在命令行时,作进一步工作前,你需要先运行这个脚本。运行设置脚本后,可以输入java javax.management.ObjectName 来测试CLASSPATH 设置是否正确,如果你得到一个错误,告诉你此类中没有main 方法,那么恭喜你,你设对了。

使用ant: 对于熟悉ant 的读者,在附录B 中有一个ant 构建的设置,在哪里还有ant xml 构建文档以及相关的环境设置信息。

译者注:个人理解JMX RI 现在应该由JMDK 替代了,所以请下载JMDK 就可以使用本书的例子了。JMDK 下载:java.sun.com/products/jdmk/index.jsp 。下载opendmk-1.0-b02-bin-dual-01-Oct-2007_19-17-46.jar ,然后加压,按照README 的操作,在命令行下运行:java -jar opendmk-1.0-b02-bin-dual-01-Oct-2007_19-17-46.jar ,就可以得到jdmkrt.jar、jdmktk.jar 、jmxremote_optional.jar 三个jar ,将他们放在你classpth 中。

2.2 管理你的第一个资源

    到现在为止,我们已经简单的回顾了JMX 的主要组件,并且你已经有一个开发环境了。接下来准备构建你的第一个MBean 吧。本章只是对MBena 的一个简介,更复杂的例子将会在以后的章节中描述。

第一个例子,你将会构建一个简单的HelloWorldMBean 。HelloWorldMBean 暴露一个简单的Java String 对象作为被管理的资源,它仅是是一个成员变量。我们使用这个例子作为一个工具,向你介绍使用JMX 的方方面面,包括MBean 、MBeanServer 、HtmlAdapter 。需要强调的是,可管理资源是任何通过Mbean 可侵入和可管理的资源。(本章中,我们不需要担心Mbean 的编码标准。还记得第一章曾经介绍过吗?本书会介绍三种类型的Mbean :标准Mbean 、动态MBean 和模型MBean 。但是本章只会创建一个标准MBean ,MBean 的具体开发规则会在以后的章节中心详细描述)图2.2 显示了HelloWorldMBean 的UML 图。

 

 

  图 2.2   HelloWorld  

 

下一节开始讨论图中描述的接口和实现。

2.2.1 写 HelloWorld   MBean

开发HelloWorldMBean 第一步是写以哦Java 接口,包含三个方法:getter 、setter 和一个打印HelloWorldMBean 的greeting 的方法。通常,你可能不需要为如此简单的HelloWorld 例子定义一个接口,但是你将会在第4章和第5 章看到,JMX 使用接口来描述Mbean 暴露的属性和操作。

回忆一下,getter 方法是类中以getMember() 命名的方法,setter 是以setMember() 命名的方法。将标准Mbean接口中的方法想象成具体类实现的一个描述,你只需要简单的通过名字理解它的意图就可以了。Getter 和setter定义了使用Mbean 的对象的成员变量的访问方式。通过成员变量的getter 方法,可以允许读操作,setter 方法云讯写操作。如同你看到的下面的接口,这个MBean 十分简单。

package jmxbook.ch2;
 
public interface HelloWorldMBean{

  public void setGreeting(String greeting);
  
  public String getGreeting();

  public void printGreeting ();
  
}

注意package 语句,本章的所有例子都在包jmxbook.ch2 下,其他章节的例子也都在对于包下(例如第三章在mxbook.ch3 下)

HelloWorldMBean 接口定义了一个getter 方法 (getGreeting()) 和一个setter 方法(setGreeting()) 以及printGreeting () 方法。随后你可以使用printGreeting() 方法打印Mbean 的greeting 的值。2.1 展示了接口

//Listing 2.1 HelloWorld
package jmxbook.ch2;

//#1
public class HelloWorld implements HelloWorldMBean{
 
    private String greeting = null;

    //#2
    public HelloWorld (){
      this.greeting = "Hello World! I am a Standard MBean"; 
    }                                                       
    //#2
    public HelloWorld (String greeting){
      this.greeting = greeting;
    }
    
    public void setGreeting(String greeting){
      this.greeting = greeting;
    }
    
    public String getGreeting(){
      return greeting;  
     }
    
    public void printGreeting (){
      System.out.println(greeting);  
    } 
}


( 注解)<#1 实现HelloWorldMBean 接口 >

( 注解)<#2 定义两个公共构造函数>

如此就新建了你的第一个MBean ,为了测试它,需要建立一个JMX 代理来容纳这个MBean ,下一节讨论HelloAgent类的创建。创建了代理后就可以使用Mbean 了。

2.3 Creating a JMX agent

迄今为止Mbean 已经有了,我们需要把它注册在一个代理中使它可用。所以我们新建一个HelloAgent 类,它是一个简单的JMX 代理。

正如第一章说的那样,JMX 代理是JMX 代理层的一个组件,它是Mbean 容器。本书第三部分会详细描述JMX 代理。

HelloAgent 会做三件事:

§ 1. 创建一个Mbean 实例来放MBean

§ 2. 创建一个 HtmlAdapter 来处理从HTML 客户端来的连接  

§ 3. 注册一个HelloWorldMBean 的实例

如你将要看到的,HelloAgent 可能是你写的最简单的代理,但是它很有用。这样反应了使用JMX 的一个最主要的点:它非常有用但是却很简单。刚刚已经开发了HelloWorldMBean ,那么现在可以写一个简单的代理来管理这个MBean ,代理使用Sun 提供的HtmlAdapter 。

使用任一个浏览器,适配器使得你可以和代理交互,以查看注册在它里面的MBean 和它们的属性。图2.3 显示了这个交互。

 

Figure 2.3 Using a Web browser to contact the HTML adapter   present in the MBean   server

 

适配器可以让你:

§ 1. 查看可读的MBean 属性

§ 2. 更新可写的MBean 属性

§ 3. 调用一些方法

不仅仅如此,适配器同时向你提供了动态创建和注册额外的Mbean 的一个简便方法。最基本的,适配器提供了一个简单的管理工具来操纵MBean 。HTML 适配器返回一个协议(HTML ),使得你的浏览器成为一个有用的管理应用。但是我们还是别太超前了,首先来创建JMX 代理吧。

2.3.1 创建 HelloAgent

Listing 2.2 是HelloAgent 类,先别担心看不懂这些代码,在本书的第三部会详细的介绍JMX 代理。现在,你只需要简单的理解就可以了,简而言之,listing 2.2 做了如下工作:

1 1.       创建了一个MBeanServer 和一个HtmlAdapter

2 2.       注册了MBean (因此你才管理MBean )

3 3.       给了Mbean 一个唯一标识符

4.       注册HTML adapter 然后运行它

//Listing 2.2 HelloAgent .java
package jmxbook.ch2;
 
import javax.management.*;
import com.sun.jdmk.comm.*;

public class HelloAgent {
  private MBeanServer server = null;

  public HelloAgent() {
    server = MBeanServerFactory.createMBeanServer("HelloAgent");//#1
    HtmlAdaptorServer adapter = new HtmlAdaptorServer();//#2
    HelloWorld hw = new HelloWorld();//#3
    ObjectName adapterName = null;
    ObjectName helloWorldName = null;
    try {
      //#4
      helloWorldName = new ObjectName("HelloAgent:name=helloWorld1");
      server.registerMBean(hw, helloWorldName);

      //#5
      adapterName = new ObjectName("HelloAgent:name=htmladapter,port=9092");
      adapter.setPort(9092);
      server.registerMBean(adapter, adapterName);
      adapter.start();
     } catch (Exception e) {
        e.printStackTrace();
      }
  }

  public static void main(String args[]) {
    System.out.println("HelloAgent is running");
    HelloAgent agent = new HelloAgent();
  }
}

( 注解 )<#1,#2 创建MBeanServer 和 HtmlAdapter >

( 注解 )<#3 创建 HelloWorldMBean   实例 >

( 注解 )<#4 创建 ObjectName   实例 ; 注册 HelloWorldMBean   >

( 注解 )<#5 注册并运行 HtmlAdapterMBean   >

注意import语句JMX框架的所有类都在javax.management 包下,包com.sun.jdmk.comm 是Sun提供的JMX RI,这个包作为不声明支持包,即对于JMX开发虽然它很有用,但是它不是JMX规范的一部分。用来实例化HTML adapter 的类就在这个包中。

 

创建 MBean Server 和 HTML 适配器

#1 HelloAgent 类包含一个main方法,它可以使HelloAgent 作为一个独立的进程运行。Main方法只是简单调用HelloAgent 的构造方法,因此让我们从这儿开始分析代码吧。HelloAgent构造方法的第一步是新建了一个MBean Server。

还记得第一章说过的吗?MBeanServer是一个Java对象,它用来容纳和操纵JMX MBean。MBean Server是一个标准的JMX类,它是JMX代理的核心。代理使用javax.management.MBeanServerFactory 得到一个MBeanServer 实例,MBeanServerFactory是一个实现为工厂模式的JMX类,它提供MBeanServer的具体实例。当你需要一个MBeanServer实例时,就可以使用工厂对象来新建或是请求一个新实例。工厂可以管理很多MBeanServer实例,调用它就会返回一个已存在的或是新建的实例(如HelloAgent中那样 )。

注意createMBeanServer()的参数“HelloAgent ”,它标识了这个代理的域名。域名是一个唯一的标识符,用于标识一组MBean,它唯一的标识了这个MBeanServer 以区别域其他的MBeanServer 。每一个MBeanServer都有一个域名,使得你可以一种有特定意义的方式来分组MBean。如果你再次使用相同的域名参数调用createMBeanServer()方法,那么它将会只 简 单的返回刚才创建的那个MBeanServer实例。(关于MBeanServer的,在第8章将会学到更多。现在你只需要知道MBeanServer扮演了注册表的角色,通过它可以存储、查找、操纵MBean)

#2 下一步是为管理应用创建一种方式来和HelloAgent交互。回想一下,代理通过构造协议适配器和连接器来向管理应用暴露自己。适配器和连接器是JMX为啥如此有用的一个主要原因。适配器和连接器是Java对象,它使得应用可以使用特定的协议和JMX代理交互(在本书的后面,你将会学习到更多关于他们的信息,并创建更复杂的例子)。本章只会用到HTML适配器,只需要调用它的默认构造方法就可以新建一个适配器,如你在HelloAgent类中看到的那样。

注册和管理 MBean

#4 一旦适配器创建了,你需要在MBeanServer里注册它。这说明适配器(连接器)也是MBean,因此组成适配器和连接器的java类被写成符合JMX规范的某一种类型的MBean。因为它们也是Mbean,所以在运行期像其他的Mbean一样可以被管理。但是在注册一个Mbean之前,你需要确保你可以标识它和找到它,这就是javax.management.ObjectName的功能了。

唯一标识 MBeans

现在为止,代理已经创建了MBeanServer并注册了HTML适配器。接下来需要知道MBeanServer是怎样对注册在它里面的对象保持追踪了。回看一下在MBeanServer上注册HelloWorldMBean的代码(#4)。当注册Mbean时,需要使它和那些已经注册的MBean区别开来。

要做到这点,必须新建一个javax.management.ObjectName 实例。ObjectName是一个JMX类用来为MBean提供命名服务,用于唯一的标识每一个注册在它里面的MBean。每个ObjectName包括两部分:

§         一 个域名—域名一般是MBean想注册的那个MBeanServer 的域名。如果不是这样,就意味着想把这个MBean和其他的MBean隔离开来。

§         一  名值对属性列表—名值对属性用于唯一标识MBean,以及提供MBean的信息。对象名也许是用户首先看到的关于MBean的信息。可以是名值对属性提供诸如名字、端口、位置此类的信息。

例子中 HelloWorldMBean 的ObjectName 如下所示

"HelloAgent :name=helloWorld1"

这样你就有一个ObjectName   实例,一旦注册就可以标识和查找到这个MBean了。

注册和启动 HTML adapter

#5 如前所述, 代理为适配器新建了一个   ObjectName   并且把它注册在MBeanServer   里。因为它们是MBean,所以每个适配器都可以选择向管理应用暴露一些可以配置的属性。

尽管适配器已近创建和注册了,管理应用仍然不可以和代理交互。要使客户端可以使用适配器,必须调用它的start()方法。Start()方法告诉适配器MBean开始在默认端口9092监听HTTP连接。现在HelloAgent   已经准备奥接受客户段的请求了。

2.3.2 更多关于 object name

在前一节中我们简要的描述了ObjectName 。为了有一个全面的了解,让我们回到ObjectName 列上。就如你注意的那样,在创建ObjectName时必须有一个特定的结构。

图 2.4显示了 ObjectName 的结构:

图 2.4 ObjectName   的结构

 

域名

域名提供了代理和其他代理联系的上下文。例如,一个代理可能创建用来在一个特定的机器上容纳MBean管理资源,这种情况下,域名可能是机器的hostname。一个域名不一定非得如hostname那样有明确含义的,但一般来说,你应该尽量提供一些有意的名字。如此,你就可以更容易的了解ObjectName 和MBean更多的信息。

图2.4中,域名甚至可以不指定,如果左边是空的,MBean Server将会提供一个默认的域名。对MBeanServerFactory类也是同样的,如果使用不带域名参数的createMBeanServer(),工厂类将返回一个使用默认域名的MBeanServer 

差不多现在你已经注意到MbeanServer对象和Mbean是关联在一个域中的(通过ObjectName)。但是实际上,某个域的MBean可以在另一个有不同域名的MBeanServer上注册。这种情况也是可接受的,因为域名并没有对注册在一个MBeanServer上的Mbean附加任何规则和约束。

名值对属性列表

对象名的名值对属性列表是一系列以逗号分隔的属性值,它们提供了唯一标识MBeanServer中MBean的机制。属性不要求是MBean的精确属性,唯一要求是当它和ObjectName实例比较时必须是唯一的。对每一个ObjectName,你必须指定最少一个属性值以使它和MbeanServer中的其他MBean可唯一区分。

ObjectName提供三个使用String参数的构造方法。在HelloAgent如下新建ObjectName :  

helloWorldName = new ObjectName ( "HelloAgent :name=helloWorld1" );

这个 ObjectName使用name=helloWorld1 名值对来唯一标识HelloWorldMBean。如果你注册其他的MBean,你不可以再使用这个名值对,你需要其他的属性。  

注册 object name 冲突

你可以认为注册表像一个复杂Hashtable ,你将对象放在表中,使用一个key 关联它。 为了注册MBean,HelloAgent 类调用MBeanServer.registerMBean() 方法。如果ObjectName不是唯一的,MBeanServer将会抛出一个javax.management.InstanceAlreadyExistsException异常,来说明已经有一个相同ObjectName的MBean被注册了。MBeanServer不会比较ObjectName关联的对象时候相等,它只比较关联它们的ObjectName。

2.4 运行代理

我们检查一下到现在为止你做了那些事。首先你创建MBean,包含在HelloWorld类中,它向外暴露一个属性:greeting 作为被管理的资源。接着创建了HelloAgent   类,它是一个简单的JMX代理。代理将会容纳MBean并提供管理MBean的方式。剩下的工作就是:编译、运行、和代理交互了。

2.4.1 编译代理

为了运行代理,你需要编译源码然后运行HelloAgent   。为了编译源码,在确认你的环境(CLASSPATH和其他)设置正确后,然后执行如下的命令:

javac jmxbook/ch2/*.java


2.4.2 运行代理

下面的命令会运行 HelloAgent :

java jmxbook.ch2.HelloAgent

执行这个命令后,你的代理已经启动了。 命令行将不会返回,因为HelloAgent 进程没有推出。你会看到:“HelloAgent is running,表示代理已经开始运行了。

2.4.3 连接代理

连接代理,你需要一个HTML 客户端,任何浏览器都可以作为一个客户端。例如,HelloAgent 的HTML 适配器默认在端口9092 监听连接。如果你的环境上这个端口不可以用,那么回到源码适配器构造方法下面一行,修改成可用的端口:

adapter.setPort( [port value ] );

确保端口可用,本书的剩余部分我们一直在端口9092 上使用HTML 适配器。

一旦已经有一个可用的端口了,打开你的浏览器,输入http://localhost:9092 (如果你指定了不同的端口,请使用它替换9092 ).

运行在代理上的HTML 适配器现在指定的端口监听HTTP 请求,当你的浏览器连接时,适配器将返回HTML 作为响应。现在你可以管理代理,凭借HTML 和代理来进行交互。

继续之前,你可以恭喜自己了,你已经成功的创建了你的第一个MBean 和代理,下一节你将会使用HTML 适配器和你的代理通信了。

2.5 使用 HTML 适配器

现在HTML 适配器已经OK ,它运行在包含MBean 的代理上,是时候连上它,看看它都为你提供了些什么东东。HTML适配器提供了通过HTML 客户端(任何浏览器)侵入JMX 代理的方式,它包括三个主要页面:

§         代理视图 — 代理视图是你看见的第一个页面,它上面有这个代理容纳的MBean 的总结,从这个页面你能过滤MBean 列表以提供更为精确的视图。

§         MBean 视图 — 这个页面提供了一个特定MBean 的详细信息。通过这个页面,你可以设置、取得MBean 的属性以及调用MBean 的方法。

§         管理视图 — 这个页面使得你可以在代理中注册一个新的MBean 。

2.5.1 代理视图

浏览器连接代理后,代理视图就出现了(图2.5 ). 代理视图是一个从代理的HTML 适配器得到的HTML 页面,它向你展示了所有注册在这个代理上的MBean ,以MBean 的ObjectName 的值显示。从这个页面,你可以迁移到MBean 视图或管理视图。在进入其他页面之前,注意页面顶部的“Filter by object Name ”栏,现在显示*:* ,它告诉代理返回所有的注册在代理中的MBean 列表。页面上MBean 数目显示了每次查询返回的MBean 的总数。

Figure 2.5 代理视图

过滤器允许使用对象名的一部分或是全部来过滤MBean 列表。比如说,你输入HelloAgent :name=helloWorld1 ,列表将只显示HelloWorldMBean. 可以使用下述的规则来输入对象名:

表 2.2 代理视图过滤器规则

MBean   filter rule

Example

使用 * 作为字母和数字的多个字符的通配符,可以作为名值对的通配符

*:name=helloWorld1,*

使用作为 ? 单个字符的通配符

??Agent:name=helloWorld1

如果不指定域名,过滤器将使用默认域名,你必须至少指定一个名值对(或使用 * )

*:*

域名的一部分是合法,但是不可以指定名值对的部分 ( 对 key 和 value均如此 )

??Agent:name=helloWorld1

所有的关键字都精确匹配或使用通配符

??Agent:*

名值对可以以任意顺序指定

 

试着自己使用过滤器,这样有助于你掌握ObjectName 的格式。试完之后,将filter 恢复为*:* ,你就可以看到所有的MBean 列表了。

注意MBean 列表中有一个在2.4 节没有注册过的MBean :MBeanServerDelegate ,它是一个由MBeanServer 创建的MBean ,用来处理特定的任务--- 特别地,是用来为MBeanServer 发送通知用的。MBeanServer 将这个MBean注册在一个独立的域中,以区别其他注册的MBean 。

所有的MBean 都是可见的,单击MBeanServerDelegate 的连接,会跳转到这个MBean 的视图。

2.5.2 MBean 视图

MBean 视图是另一个从HTML 适配器接受到HTML 页面,它显示你点击的MBean 的信息。视图显示了被选择的MBean的所有的包含表2.3 中的信息。

表 2.3 MBean 视图的元素

MBean   detail

Description or example

类名

MBean 的类名 , 比如 HelloWorld

对象名

MBean 的 ObjectName , 比如 HelloAgent: name=helloWorld1

描述

MBean 的描述。对于标准 MBean ,由 MBeanServer 创建这些描述

属性表

MBean 暴露的属性列表,包括属性的类型、侵入和值,如果有的话。属性表运行你更改可写的属性。

暴露的操作

MBean 暴露的操作列表,你可以触发这些属性

重新载入周期

告诉 MBeanServer   是否需要重新实例化 MBean 以及周期

解除注册按钮

告诉 MBeanServer 解除对这个 Mbean 的注册

 

Figure 2.6 MBeanServerDelegate MBean 的视图

 

Figure 2.6 MBean 视图

看一下图2.6 的MBean 属性表,注意Access 列,当前都是RO ,代表着只读(Read Only ),其他可能的值有:WO ( 只写:Write Only) 和 RW ( 可读写:Read/Write) 。如你所猜想的那样,RO 意味着MBean 的接口为此属性值提供了getter 方法,WO 意味只提供了setter 方法,RW 意味这提供了getter 和setter 方法。

HTML 适配器使用反射检查接口的方法名,它去除了每一个方法前的get 和set ,使用剩下的部分作为属性名,余下的方法(那些不以set 或get 开头的方法)被放进MBean 视图的操作部分(即:List of MBean operations )

回看图 2.6 ,你可以看到MBeanServerDelegate 只暴露了可读属性,这些属性描述了正在使用的JMX RI 以及它实现的是那个版本的JMX 规范。

让我们回到代理视图,点击代理视图,这次选择HelloWorldMBean ,如图2.7 看到的那样:

Figure 2.7 H elloWorldMBean   视图

 

除了两个重要的区别外,HelloWorldMBean 视图和MBeanServerDelegateMBean 是一样的。HelloWorldMBean 暴露只暴露了一个属性:greeting ,(并且它有getter 和setter ),意味着这个属性是可读可写的,所以视图的Value 列是可以输入值的。点击Apply 按钮来提交你做的任何改变,之后页面会刷新,文本域显示的当前值反应了你刚才做的改变。

现在看MBean 的操作(Operations ),你只看到一个可用的操作。MBean 视图为每一个操作都构建了一个按钮,按钮的名字就是方法名。对于HelloWorldMBean ,你可以看到一个printGreeting 的按钮,这是HelloWorldMBean 接口剩下的方法(除去getter 和setter )。按钮前是void ,它是这个方法的返回值。如果方法有输入,你将会看到每个输入对应一个输入文本框。

HTML 适配器只为输入参数提供几种特定的类型,只支持String 、基本类型、基本类型的外包类。

当你点击printGreeting 按钮后,可以看到发生了两件事,第一: 页面迁移到了另一个页面,上面显示了方法执行成功并且没有返回值;第二:看看代理的输出,你可以看到你刚才输入的Greeting 属性的值被打印了出来。

恭喜你已经成功的管理了HelloWorld 。还剩下一个视图:管理视图。返回代理视图,然后点击Admin 按钮,就可以进入HelloAgent 的管理视图了。

2.5.3 管理视图

使用上两节的HTML 页面,你能配置和查询注册在代理中的MBean 。然而,如果你想不写任何代码就向代理中添加一个MBean ,该怎么办呢?Admin 视图使得你可以侵入MBeanServer 以移除或添加MBean 。在页面上,你可以指定一个ObjectName 值和一个MBean 的Java 类,MBeanServer 将根据你的输入构建并注册一个相应的MBean 。Admin视图显示了4 个文本域(图2.8 ):

§         Domain — HTML 适配器使用了代理的默认域。当前显示HelloAgent ,这是对象名的第一部分。

§         Keys — 需要符合[name=value ],* 的要求 . 这个框框表示对象名的名值对部分。

§         Java Class — 表示你想创建的MBean 的类的全限定名。

§         Class Loader — 是可选的。 你可以为MBeanServer 在试图加载前面输入的Java 类时,指定一个特定的类加载器。

Figure 2.8  The Admin View presented by the HTML adapter

 

为了更进一步理解这个页面,让我们创建更多的MBean 。在下一节你将会看到,在一个MBeanServer 中,你可以有同一种类型的许多MBean ,只要他们的对象名不同。

2.5.4 在 HelloAgent 是注册 / 解除注册 MBean

让我们通过Admin 视图加载另一个HelloWorldMBean 的实例。域名使用默认的HelloAgent ,在Keys 中输入name=helloWorld2 ,在Java Class 中输入jmxbook.ch2.HelloWorld ,它实现了HelloWorldMBean 接口。

   在Java Class 中指定的任何类都必须对代理的MBeanServer   是可见的。对HelloAgent ,这就要求输入的类必须在CLASSPATH 中。

ClassLoader 空着 , 告诉 HelloAgentMBeanServer   使用默认的类加载器。代理将使用输入的值构建ObjectName ,像这样:

HelloAgent :name=helloWorld2

当这些都就绪后,你已经准备好了新建一个HelloWorld   实例了。在Action选项的下拉框中有三个选项:

§         Create — 告诉 MBeanServer使用无参构造方法新建一个MBean

§         Unregister — 只在你指定的ObjectName   在代理中存在对应的MBean 时有效

§         Constructors — 向你显示MBean的构造函数列表

选择Create选项,点击Send Request按钮,你将会看到一个成功的消息,告诉你已经成功的新建并注册了一个新的MBean。返回Agent视图,确认一下MBean列表。

使用有参数的构造方法

还记得HelloWorldMBean   的实现类HelloWorld 有两个构造方法吗?一个默认构造方法和一个有参数的构造方法,参数代表Greeting属性的初始值。让我们使用第二个构造方法再注册一个HelloWorldHelloWorld 的实例吧。

1.       返 回Admin视图,输入合适的值来创建一个新的HelloWorldMBean 。确保输入一个唯一的值(例如name=helloWorld3)。

2.       这 次选择从Action列表选择Constructors,然后点击Send Request按钮,你会看到构造方法列表(有两个),其中一个显示了文本框用来输入它的一个参数  。

3.       输 Greeting属性的值,然后点击和上面的构造方法关联的Create按钮。如果对象名和类名你都输入对了,你将会再次看到“creation successful”消息。

4.       返 回代理视图,确认MBean列表,这次有三个HelloWorldMBean 实例了。

2.6 使用 MBean 通知

通 过前面几节,你已经创建和注册了自己的MBean,对使用JMX来开始工作你已经具备了足够的知识。你已经学习了怎样创建一个标准MBean,怎样把它添加进JMX代理,怎样通过HTML适配器管理它。但是,你仍然错过了一个关键的部分:通知

JMX通知是Java对象,通过它可以从MBean和代理向那些注册了接收通知的对象发送信息(如图2.9)。对接受事件感兴趣的对象是通知监听器,他们实现了javax.management.NotificationListener   interface接口。

图 2.9 从 MBean 向一个注册的监听器发生通知

通知是JMX很重要的一部分,因为它们允许事件的传输。JMX事件可以是任何事情,从MBean的属性变更到在MBeanServer注册一个新MBean,都可以是JMX事件。

为了给你一个通知的简单介绍,本节将会将通知添加进HelloWorldMBean。在第6章我们将深入学习通知机制。

2.6.1 在 HelloWorldMBean 中添加通知的代码

HelloWorldMBean   发送通知,它得允许其他的对象注册监听器来接受通知。JMX支持两种机制来为MBean提供监听器以注册来接受通知:

§           实现 javax.management.NotificationBroadcaster   接口

§           继承 javax.management.NotificationBroadcasterSupport 类 ( 它实现类了NotificationBroadcaster 接口 )

使用接口的好处是你的类可以继承其他的类,而使用继承的好处是你不需要为 接口写额外的代码。如果你的MBean不需要继承其他的类,那么可以使用继承以使用它里面的实现。HelloWorld 不需要继承其他的类,所以就可以自由的使用继承。如列表2.3所示:

Listing 2.3 HelloWorld .java
package jmxbook.ch2;
 
import java.io.*;
import javax.management.*;

//#1
public class HelloWorld extends NotificationBroadcasterSupport implements HelloWorldMBean {
    //#2
    public HelloWorld () {
      this.greeting = "Hello World! I am a Standard MBean ";
    }

    //#2
    public HelloWorld (String greeting) {
      this.greeting = greeting;  
    }
   
    public void setGreeting(String greeting){
      this.greeting = greeting;
      Notification notification = new Notification("jmxbook.ch2.helloWorld.test", this, -1,System.currentTimeMillis(), greeting);//#3
      sendNotification(notification);//#4
    }

    public String getGreeting(){
      return greeting;  
    }

    public void printGreeting (){
      System.out.println(greeting );  
    }

    private String greeting;
}

( 注解 )<#1 继承 NotificationBroadcasterSupport   类 >

( 注解 )<#2 定义两个构造方法   >

( 注解 )<#3 创建 javax.management.Notification 对象 >

( 注解 )<#4 发送通知 >

#1 通过继承NotificationBroadcasterSupport   类改变了HelloWorld   类的声明,NotificationBroadcasterSupport 类向它的子类MBean提供了注册监听器和发送通知的方法,它实现了javax.management.NotificationBroadcaster 接口。

#2 本例只是发送一个简单的通知,在这步使用构造方法新建了一个通知:

public Notification(java.lang.String type, java.lang.Object source,long sequenceNumber,long timeStamp,java.lang.String message)

构造方法的参数如表 2.4 所示:

表 2.4   Notification 构造方法参数

Parameter

Description

java.lang.String type

点分格式的 String 来标识通知,用于通知的简要描述

java.lang.Object source

产生通知的 MBean , 可以是 MBean 对象的引用或是它的ObjectName  

long sequenceNumber

在一个通知序列中表示本通知

long timestamp

创建通知 的时间戳

java.lang.String message

通知源的消息

 

#3 通过继承NotificationBroadcasterSupport   ,你不仅间接实现了NotificationBroadcaster   接口,还继承了sendNotification()   方法,当MBean需要发送通知时就可以调用这个方法,父类会将它发送给所有对应的监听者。对应的监听者是那些已经在MBean上注册并且它的过滤器接受特定类型通知的一些监听器。

NotificationBroadcaster   接口

前面的例子使用NotificationBroadcasterSupport   类 ,它提供了NotificationBroadcaster 接口子接口(NotificationEmitter)的一个实现 ,接口 NotificationBroadcaster如下:

public interface NotificationBroadcaster {
  public void addNotificationListener(NotificationListener listener,NotificationFilter filter,Object handback) throws IllegalArgumentException;
 
  public MBeanNotificationInfo [] getNotificationInfo() ;
 
  public void removeNotificationListener(NotificationListener listener) throws ListenerNotFoundException;
 
}

 

实现这个接口的MBean通过 addNotificationListener() 方法为其他的对象提供了注册机制,这个方法接受一个NotificationListener 对象、一个NotificationFilter 对象、和一个handback 对象作为参数。

NotificationListener 参数是一个实现了NotificationListener 接口的对象,它指定一个handleNotification()方法。当通知被发送个一个监听器时这个方法会被回调。

NotificationFilter   参数是一个可选参数,它允许MBean来过滤发送哪些消息给监听器。handback   参数在每次通知发送时都会被发送回客户端。

注意通知注册和传输机制和java事件模型的相似性。

2.6.2 修改 HelloAgent

为了发送通知,还需要一些对象来接受它们。本例将HelloAgent 作为通知监听器,只需要对代码最很少改动。首先,HelloAgent需要实现NotificationListener 接口,它还是会新建和注册HTML适配器和HelloWorldMBean,创建完MBean后,它就可以为HelloWorldMBean注册一个通知监听器来接受通知。HelloAgent的代码变更部分在列表2.4中以粗体标注:

//Listing 2.4 HelloAgent .java
package jmxbook.ch2;
 
import javax.management.*;
import com.sun.jdmk.comm.HtmlAdaptorServer;

//#1
public class HelloAgent implements NotificationListener {
    private MBeanServer mbs = null;
 
    public HelloAgent () {
      mbs = MBeanServerFactory.createMBeanServer("HelloAgent ");
      HtmlAdaptorServer adapter = new HtmlAdaptorServer();
      HelloWorld hw = new HelloWorld();
              
      ObjectName adapterName = null;
      ObjectName helloWorldName = null;

      try{
        adapterName = new ObjectName ("HelloAgent:name=htmladapter,port=9092");
        mbs.registerMBean(adapter, adapterName);
        adapter.start();

        helloWorldName = new ObjectName ("HelloAgent:name=helloWorld1");
        mbs.registerMBean(hw, helloWorldName);
        <strong>hw.addNotificationListener(this, null, null);</strong>//#2
      }catch(Exception e) {
          e.printStackTrace();
      }
    }//constructor

    //#3
    <strong>public void handleNotification(Notification notif, Object handback){
       System.out.println("Receiving notification ...");
       System.out.println(notif.getType());
       System.out.println(notif.getMessage());
    }</strong>
 
    public static void main(String args[]) {
      HelloAgent agent = new HelloAgent();
    }
}//class

( 注解 )<#1 实现 NotificationListener   接口 >

( 注解 )<#2 注册接受通知 >

( 注解 )<#3 实现接口方法

 

#1 HelloAgent 代理实现NotificationListener   接口,接口中只有一个方法handleNotification(),在通知传地到时它被调用。

#2 注册MBean之后,HelloAgent   将自己作为一个listener 注册在了HelloWorldMBean上,这是通过将自己作为NotificationListener   参数传递给MBean的addNotificationListener()(继承自父类NotificationBroadcasterSupport )来完成的

#3 前面提到过,对象要想接收到通知,必须实现NotificationListener 接口,它只有一个方法handleNotification( )。本例中只是简单的打印了一些信息。

运行结果

要测试本例,首先需要编译源码然后执行HelloAgent(参考2.5节)。一旦代理开始运行了,就可以打开浏览器,输入http://localhost:9092 ,进入Agent页面,为测试通知,按照以下步骤进行:

1.       迁移至 MBean 视图,点击 HelloWorldMBean

2.       当 HelloWorldMBean 的 greeting 改变时会发送一个通知,因此输入一个新值,然后点击 Apply

3.       查看 HelloAgent 控制台,你会看到如下输出:

 

Receiving notification ...
jmxbook.ch2.helloWorld.test
I have changed my greeting

输出包括你的打印信息 ” Receiving notification … ” ,以及通知类型和通知包含的消息。

2.7 总结

本章让手把手教你学习了几个JMX的例子。其中你开发了一个可管理的资源,创建并运行了一个简单的JMX代理,我们讨论了怎么注册MBean,怎样确保它的对象名唯一,怎么创建MBeanServer等。

另外你还是使用了Sun提供JMX RI中的HTML适配器,通过HelloWorld例子,你应该明白了从JMX的角度来看MBean的开发是很如此简单,MBean只通过简单的几行代码来暴露自己的资源。

最后,为了使JMX的介绍更全面,我们写了一个JMX通知的例子,在第6章我们会详细讨论通知,哪儿你会明白为啥通知是管理系统中的一个基本部分。

第三章,你将会开发一个JMX代理,然后在本书的其余部分,你会加强它的功能,它将会被用于其他的很多章节。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值