在本系列文章的 第 1 部分中,我们从以前简单的设备库管理到今天复杂而强大的网络管理系统(NMS)及企业管理系统(EMS),对网络管理的发展做了说明。JMX 已经成熟,它用可管理性方面的特性扩展了 Java 平台。它允许 NMS、EMS 和其它管理/控制应用程序管理基于 Java 平台的软件应用程序、服务和设备。JMX 的现代体系结构是分层且基于组件的,它的所有功能模块都通过良好定义的编程接口分隔。
这一方法利用了当今最新的软件体系结构设计中的最佳实践,并以较低的实现成本提供了兼容的、可扩展的和健壮的 Java 软件可管理性的解决方案。JMX 正被迅速采用,一些最新的开放源服务器产品已经在第二代 JMX 集成中使用了。JMX 也正在成为 J2EE 1.4 草案规范中所描述的几乎所有服务器/服务组件的主要部分。
回忆一下,JMX 体系结构可以分成三层:
- 工具(instrumentation)
- 代理(agent)
- 分布式服务
在最新可用的 JMX 规范(撰写本文时是 1.1)中只定义了工具层和部分代理层。JMX 体系结构剩余部分的详细规范还在制订中。
在本文中,我们将使用 JMX 1.1 参考实现,将工具添加到现有的软件应用程序中。
我们的样本应用程序 ClickMeter 是基于 Swing 的 GUI 实用程序,它以典型的 MVC 设计为特色。它显示了其 GUI 上的“数字计数器”和两个按钮。单击 +按钮递增显示的数字,单击 -按钮则递减显示的数字。表 1 列出了应用程序中的文件:
| 文件名 | 描述 |
| ClickMeter.java | 带有我们将执行的静态
main方法的类;它创建 GUI 框架,将其置中并显示它
|
| ClickFrame.java | 包含
ClickPanel的框架容器
|
| ClickPanel.java | 单一源文件,包含用于 GUI 应用程序的三个类:
ClickPanelModel(模型)、
ClickPanel(视图)和
ClickPanelController(控制器)。
|
负责 GUI 表示的视图类是
ClickFrame,它是
javax.swing.JPanel的子类。它创建 GUI 元素,将它们布置在面板上并管理用户与 GUI 的交互。ClickMeter 的 MVC 操作如图 1 所示:
图 1. ClickMeter 的 MVC 交互
在图 1 中,
ClickPanelModel(模型)保持一个代表计数器瞬时值的
int值(名为
val)。要访问该值,您必须通过
ClickPanelModel类的方法。这些方法包括用于
val变量的
set()、
get()、
increment()和
decrement()。
ClickPanel是典型的
ClickPanelModel的观察器。无论
ClickPanelModel值何时更改,它都会通过调用
NotifyUpdate()方法将更改通知给
ClickPanel。
此时,
ClickPanel取出模型已更改的值并将它重新显示在包含该值的 GUI 面板上。这确保了显示在
ClickPanel上的值始终和
ClickPanelModel中的值保持一致。为完成这一循环,
ClickPanel管理用户和 GUI 的交互(例如,单击两个按钮中的一个),然后将所有操作都传递给
ClickPanelController(使用
onIncButtonClicked或
onDecButtonClicked方法)。
ClickPanelController通过修改
ClickPanelModel的值使 GUI 操作与所期望的计数器操作协调。这是通过调用
ClickPanelController的
incPanelValue()和
decPanelValue()方法实现的。
使用代码分发版中提供的 compile.bat 批处理文件来编译应用程序,或从代码子目录执行下列命令行:
javac -classpath
<jmx install dir>\lib\jmxri.jar;
<jmx install dir>\lib\jmxtools.jar
-d classes src\dwjmxservice\basic\*.java
|
使用 runmeter.bat 文件运行应用程序,或者从代码子目录执行下列命令行:
java -classpath
<jmx install dir>\lib\jmxri.jar;
<jmx install dir>\lib\jmxtools.jar;
classes dwjmxservice.basic.ClickMeter
|
图 2 显示了运行的应用程序。您可以在任何时候单击这两个按钮来递增或递减计数器值。
图 2. ClickMeter GUI 应用程序
要注意的一件很重要的事是,整个应用程序的设计没有考虑可管理性。也就是说,应用程序中完全没有引用任何 JMX 类,并且它不是用任何可使 JMX 代码的添加变得更容易或更困难的方法来构建的。简单地讲,这反映出它是设计良好的 Java GUI 应用程序 - 不关心对 JMX 操作的适应性。而且这也是 JMX 的本质:能很容易地将它添加到任何应用程序或软件服务器/服务,使它们立即成为是可管理的。JMX 的这一“易添加”特性的结果是很低的实现开发成本。接下来,您将看到如何能快速地将工具添加到 ClickMeter 应用程序。
工具能添加到任何 Java 程序(包括任何软件服务、服务器或应用程序)中。工具涉及可管理的属性(特性)、操作(方法)和事件,它们可以向 JMX 代理公开(并通过这些代理向 EMS 和管理应用程序公开)。JMX 设计者首先必须确定要添加的工具。
您可以很容易地添加工具来管理联网设备和软件服务/服务器。对于联网设备来说,属性可以是所用设备的 IP 地址和端口、所支持的协议、协议的参数等等。对设备的操作可以包括复位出厂缺省值、重新引导、固件升级等等。对于 Web 应用程序服务器来说,属性可以是每个单位时间“命中”的总数、用在服务器上的 CPU 时间总量或服务器上执行的应用程序的组合。对应用程序服务器进行的操作可以包括复位、重新引导和应用程序在服务器上的装入和卸载。
但是我们主要关心的是 ClickMeter。ClickMeter 中有什么是要受管的呢?表 2 给出了一些答案:
| 受管的元素 | 类型 | 描述 |
PanelValue
| 属性 | 计数器的瞬时值;和
ClickPanelModel所保持的值一样
|
incPanelValue
| 操作 | 计数器值加 1;和用户单击 +按钮的操作一样 |
decPanelValue
| 操作 | 计数器值减 1;和用户单击 -按钮的操作一样 |
添加工具的第一步是确保可管理元素容易访问。为了帮助做到这一点,我们将一些方法添加到
ClickMeter类中。为了保持原有的 ClickMeter 应用程序 - 让它完全与 JMX 代码无关 - 我们创建
ClickMeter的子类,称为
ClickMeterInstrm。该类的方法如表 3 所示:
表 3. 公开 ClickMeterInstrm 的属性和操作的方法
| 方法 | 描述 |
getPanelValue()
| 通过模型(
ClickPanelModel)获取计数器的当前值
|
setPanelValue()
| 通过模型来设置计数器的当前值,因为我们知道 MVC 模式设计将确保所有视图和模型同步。 |
incPanelValue()
| 通过模型,使计数器值加 1 |
decPanelValue()
| 通过模型,使计数器值减 1 |
单 1 演示了
ClickMeterInstrm类的实现:
清单 1. ClickMeterInstrm 代码
package dwjmxservice.basic;
public class ClickMeterInstrm extends ClickMeter {
public ClickMeterInstrm() {
}
// code added to instrument for JMX Instrumentation
public void setPanelValue(Integer val) {
frame.clickPanel.myModel.setVal(val.intValue());
}
public Integer getPanelValue() {
return new Integer(frame.clickPanel.myModel.getVal());
}
public void incPanelValue() {
frame.clickPanel.myModel.incVal();
}
public void decPanelValue() {
frame.clickPanel.myModel.decVal();
}
}
|
请注意清单 1 中所示的新方法的实现如何使用
ClickPanelModel来更改计数器的值。
setPanelValue()和
getPanelValue()方法/操作提供了符合 JavaBeans 的命名约定,该约定公开了
PanelValue特性/属性。一旦我们添加标准 Mbean 支持,这就会显得很重要。
原有的 GUI 应用程序 ClickMeter 仍是原封不动的,完全不受这次添加的影响。
既然我们已经添加了逻辑来支持对一个属性和两个操作的访问,那么我们可以添加将它们向管理代理公开的机制。要实现这一点,我们将 JMX MBean(受管的 bean,managed bean)支持添加到 ClickMeter 应用程序中。正如我们在 第 1 部分中学到的那样,MBean 实现至少有两种不同的主要样式,如表 4 所示:
| MBean 实现 | 描述 |
| 标准 | 所有属性、操作和事件都在编译时通过固定的管理接口进行定义。 |
| 动态 | Mbean 所支持的受管属性、操作和事件直到运行时才确定 - 并且可以在调用之间或者甚至在一个管理会话期间动态变化 |
将工具添加到 ClickMeter 中最快、最简单的方法是使用标准 MBean 实现。
在一个标准 MBean 中,向管理代理公开的所有属性、操作和事件都在固定的接口中指定。这个接口的名称必须遵循下面这个词法模式: < 服务/应用程序/设备的类名 > MBean。
在我们的示例中,我们已经用标准 MBean 工具创建了一个名叫
ClickMeterStd的
ClickMeterInstrm子类,它将包含 ClickMeter 应用程序。由于应用程序名为
ClickMeterStd,因此标准 MBean 工具必须遵循词法模式,并命名为
ClickMeterStdMBean。清单 2 定义了 ClickMeterStdMBean.java(可在源代码分发版中获得该文件)的接口:
清单 2. ClickMeterStdMBean 管理接口
package dwjmxservice.basic;
public interface ClickMeterStdMBean {
public void setPanelValue(Integer inVal);
public Integer getPanelValue();
public void incPanelValue();
public void decPanelValue();
}
|
在清单 2 中,已公开的属性作为公共的 getter 方法和 setter 方法(用于
PanelValue属性)列出,并且操作也是公共的方法(
incPanelValue()和
decPanelValue()操作)。
JMX 代理将使用标准的 Java 内省(introspection)确定运行时期间 MBean 所支持的属性和操作。现在我们将创建简单的 JMX 代理。
JMX 代理通过 MBeanServer(在 JMX 规范中定义了其接口)装载 MBean。它还提供了一组所需的代理服务(请查阅 本系列文章的第 1 部分以获得更多的详细信息),还提供了协议适配器或直接连接到 JMX 管理器或 EMS/NMS 的连接器。
日益智能化的网络设备、PC 和外围设备的出现,对这些端点的日常管理和监控方面提出了越来越高的智能化要求。而且,通过因特网处理业务的需求日益增长,从而产生了一种新的需要 EMS 支持的端点 - 即智能软件服务器/服务。
图 3 应使您想起 JMX 代理的组件化组成。在 JMX 体系结构中,代理层聚合 MBean 并公开属性、操作和事件让分布式服务层来管理。代理可以直接传递来自其聚合的 MBean 的可管理元素,或者为其自身可管理元素组提供增值特性和公开。
图 3. JMX 代理的组成
参考实现提供了一个简单的代理所需的所有元素。但是,唯一完全实现的与外部世界的连接是 HTML 协议适配器。该协议适配器允许 Web 浏览器查看并访问代理所管理的所有 MBean 的可管理元素。
让我们添加代码以配置代理并使用其协议适配器。我们将对启动 JMX 参考实现代理的支持添加到
ClickMeterStd中。清单 3 演示了负责实例化和启动该代理的代码:
清单 3. 用于实例化和启动 JMX 代理的代码
BaseStdAgent myAgent = new BaseStdAgent();
myAgent.startAgent((Object) cms);
|
请注意
BaseStdAgent类的使用(请参阅源代码分发版中的 BaseStdAgent.java)。本质上,代理中的逻辑包括下列程序步骤:
- 启动 MBeanServer。
- 向 MBeanServer 注册受管的 MBean。
- 启动协议适配器和连接器。
在生产编码中,将 JMX 规范所需求的大多数代理服务也作为 MBean 装入。这意味着步骤 2 还将注册代理服务的所有 MBean。事实上,就我们在此使用的 HTML 协议适配器而言,它也是 MBean。清单 4 演示了 BaseStdAgent.java 中执行步骤 1 的代码:
清单 4. BaseStdAgent 中创建和启动 MBeanServer 的代码
System.out.println("Creating the MBeanServer....");
MBeanServer server = MBeanServerFactory.createMBeanServer();
|
清单 5 演示了执行步骤 2 的代码。在这个例子中,将第一个 MBean 作为
inMBean参数传递给
BaseStdAgent。我们还给它一个用户可读的
ObjectName。
清单 5. 向 MBeanServer 注册受管的 MBean
try {
ObjectName tpMBeanName = new ObjectName("MBean:name=ClickMeter");
server.registerMBean(inMBean, tpMBeanName);
} catch (Exception e) {
System.out.println("Cannot register ClickMeter MBean!");
e.printStackTrace();
return;
}
|
BaseStdAgent中最后部分的代码启动了 HTML 协议适配器,它位于
com.sun.jdmk.comm.HtmlAdaptorServer类中。清单 6 演示了如何设置并启动协议适配器。缺省情况,它监控用于 HTML 浏览器连接的端口 8082。请注意用于注册 HTML 协议适配器的
server.registerMBean()方法的使用,其本身就是 MBean。
清单 6. 创建和启动 HTML 协议适配器
System.out.println("Creating an HTML protocol adaptor..");
HtmlAdaptorServer hadaptor = new HtmlAdaptorServer();
ObjectName adaptorName = null;
try {
adaptorName = new ObjectName("Adaptor:name=hadaptor,port=8082");
server.registerMBean(hadaptor, adaptorName);
} catch(Exception e) {
System.out.println("Cannot create the HTML protocol adaptor!");
e.printStackTrace();
return;
}
hadaptor.start();
|
以上是这个简单代理中的所有代码。如果要支持更多的代理服务,可以用和 HTML 协议适配器同样的方式向 MBeanServer 注册它们。
要注意的是,这个简单 JMX 代理的代码是独立于它要管理的 MBean(
ClickMeterStd和
HtmlAdaptorServer)编写的。同样,MBean 代码是独立于最终用来管理它的代理编写的。这是 JMX 多层体系结构的本质。工具层的 MBean 旨在与代理层的代理逻辑松散去耦合。
使用 compile.bat 文件编译具有全部工具和代理支持的
ClickMeterStd应用程序,或者从代码子目录执行下列命令行:
javac -classpath
<jmx install dir>\lib\jmxri.jar;
<jmx install dir>\lib\jmxtools.jar
-d classes src\dwjmxservice\basic\*.java
|
使用 runstd.bat 文件启动
ClickMeterStd应用程序,或者从代码子目录执行下列命令行:
java -classpath
<jmx install dir>\lib\jmxri.jar;
<jmx install dir>\lib\jmxtools.jar;
classes dwjmxservice.basic.ClickMeterStd
|
这条命令启动了 ClickMeter 应用程序以及 JMX 代理。试一试 ClickMeter 应用程序中的按钮。因为我们添加 JMX 工具时没有更改一行 ClickMeter 代码,所以 ClickMeter 仍然像以前那样工作。但是,ClickMeter 的标准 MBean 已被装入 JMX 代理并且已经启动了 HTML 适配器。这允许我们立即访问代理。启动浏览器并指向:
http://localhost:8082/ |
您将会看到类似于图 4 的代理产生的页面:
图 4. 针对 HTML 协议适配器的主代理视图页面
我们可以看到由这个代理管理的所有 MBean。单击 name=ClickMeter。您将看到类似图 5 的页面:
图 5. 针对 HTML 协议适配器的详细的 ClickMeter MBean 视图
请注意由 MBean 公开的
PanelValue属性以及
incPanelValue和
decPanelValue操作。如果您设置了重新装入时间,那么来自代理的客户机端 JavaScript 代码将定期地轮询
PanelValue属性的值并更新所显示的值。尝试使用 5 到 10 秒的重新装入时间并查看更新情况。您还可以单击 ClickMeter 上的 GUI 按钮来更改该值。
接下来,尝试以下操作:
- 单击
incPanelMeter操作以递增计数器的值。请注意我们是如何轻松地访问该操作的 - 如 JMX 管理应用程序可以通过代理做的那样。
- 单击
decPanelMeter操作。
- 设置
PanelValue属性的值。请注意它在 ClickMeter 中是如何立即被更新的。
尽管在这个例子中我们使用 HTML 协议适配器,而其它协议适配器或连接器(例如 SNMP)也可以容易地使用,并且它们将通过其它应用程序或 EMS 使我们的 ClickMeter 成为可管理的。
还记得我们说过不必将 ClickMeterStd MBean 所支持的属性和操作显式地告诉代理吗。代理实际上在运行时期间通过内省
ClickMeterStdMBean接口发现它自己。这是使用标准 MBean 时 JMX 的标准操作。代理在运行时期间使用内省定位该管理接口(借助于必需的标准 MBean 词法命名模式)。
动态 MBean 是实现
java.management.DynamicMBean接口的 MBean。表 5 演示了该接口必须实现的方法:
| 方法名 | 描述 |
getAttribute()
| 获取属性名称,然后将其当前值作为 Java 对象返回 |
getAttributes()
| 获取一列属性的名称,然后,如果它们的当前值可用就将其作为 Java 对象返回 |
setAttribute()
| 给属性设置特定值 |
setAttributes()
| 设置一列属性的值 |
invoke()
| 调用特定的操作;如果适用,就提供参数并支持返回值 |
和标准 MBean 不同的是,代理依靠
DynamicMBean接口在运行时从 MBean 获得公开的属性、操作和事件的元数据信息。这意味着当您将工具添加到应用程序中时没必要遵循词法模式(JavaBeans 编码约定)。代理使用动态 MBean 时不需要对 MBean 执行内省。它信任从 bean 接收到的元数据,该 bean 由
getMBeanInfo()调用。因为 MBean 在运行时期间生成这个数据,所以有可能无需重新编码静态定义的接口就可以更改该数据。开始使用动态 MBean 的最快捷的方法是使用
ModelMBean。
ModelMBean是存在于所有 JMX 实现之中的必需的缺省 MBean 实现。JMX 规范精确地规定了如何访问
ModelMBean实例。因为它完全由 JMX 实现来实现的,所以模型 MBean 的优点在于它的易于使用和适应性。通常在运行时,应用程序、服务、服务器或设备可以将
ModelMBean实例实例化成其它 JMX 层的表示。
模型 MBean 始终是动态 MBean(它必须是动态的,因为没有办法让它预先知道它的用户是谁,也就是说可以不预先知道公开的属性、操作和事件)。另外,模型 MBean 为任意软件应用程序、服务、服务器或设备提供了现成可用的 MBean 外观(或封装器)。而且模型 MBean 保证是可兼容的,因为它是实现的自身代码的一部分。
让我们研究一下如何使用
ModelMBean快速地将动态 MBean 工具添加到 ClickMeter 中。我们创建一个新的
ClickMeterInstrm子类 - 名为
ClickMeterMod。清单 7 演示了该类的代码:
清单 7. ClickMeterMod - 使用 ModelMBean 的工具
public class ClickMeterMod extends ClickMeterInstrm {
public ClickMeterMod() {
}
public static void main(String[] args) {
setLandF();
ClickMeterMod cms = new ClickMeterMod();
BaseModAgent myAgent = new BaseModAgent();
myAgent.startAgent((Object) cms);
}
}
|
请注意,清单 7 中
ClickMeterMod类不实现任何特定的接口。它和任何编译时 JMX 需求都是完全去耦合的。这是因为我们将在运行时期间使用 JMX 参考实现中已有的模型 MBean 实现将它挂接到 JMX。这实际上是在由
BaseModAgent类代表的代理中执行的。
