动态 MBean
但是对于很多已有的 SubAgent 实现,其 Coding Convention 并不符合标准 MBean 的要求。重构所有这些 SubAgent 以符合标准 MBean 标准既费力也不实际。JMX 中给出了动态(Dynamic) MBean 的概念,MBServer 不再依据 Coding Convention 而是直接查询动态 MBean 给出的元数据(meta data)以获得 MBean 的对外接口。
2
3 import javax.management.*;
4 import java.lang.reflect.*;
5 public class ServerMonitor implements DynamicMBean {
6
7 private final ServerImpl target;
8 private MBeanInfo mBeanInfo;
9
10 public ServerMonitor(ServerImpl target){
11 this.target = target;
12 }
13
14 //实现获取被管理的 ServerImpl 的 upTime
15 public long upTime(){
16 return System.currentTimeMillis() - target.startTime;
17 }
18
19 //javax.management.MBeanServer 会通过查询 getAttribute("Uptime") 获得 "Uptime" 属性值
20 public Object getAttribute(String attribute) throws AttributeNotFoundException,
21 MBeanException, ReflectionException {
22 if(attribute.equals("UpTime")){
23 return upTime();
24 }
25 return null;
26 }
27
28 //给出 ServerMonitor 的元信息。
29 public MBeanInfo getMBeanInfo() {
30 if (mBeanInfo == null) {
31 try {
32 Class cls = this.getClass();
33 //用反射获得 "upTime" 属性的读方法
34 Method readMethod = cls.getMethod("upTime", new Class[0]);
35 //用反射获得构造方法
36 Constructor constructor = cls.getConstructor(new Class[]
37 {ServerImpl.class});
38 //关于 "upTime" 属性的元信息:名称为 UpTime,只读属性(没有写方法)。
39 MBeanAttributeInfo upTimeMBeanAttributeInfo = new MBeanAttributeInfo(
40 "UpTime", "The time span since server start",
41 readMethod, null);
42 //关于构造函数的元信息
43 MBeanConstructorInfo mBeanConstructorInfo = new MBeanConstructorInfo(
44 "Constructor for ServerMonitor", constructor);
45 //ServerMonitor 的元信息,为了简单起见,在这个例子里,
46 //没有提供 invocation 以及 listener 方面的元信息
47 mBeanInfo = new MBeanInfo(cls.getName(),
48 "Monitor that controls the server",
49 new MBeanAttributeInfo[] { upTimeMBeanAttributeInfo },
50 new MBeanConstructorInfo[] { mBeanConstructorInfo },
51 null, null);
52 } catch (Exception e) {
53 throw new Error(e);
54 }
55
56 }
57 return mBeanInfo;
58 }
59
60 public AttributeList getAttributes(String[] arg0) {
61 return null;
62 }
63
64 public Object invoke(String arg0, Object[] arg1, String[] arg2)
65 throws MBeanException,
66 ReflectionException {
67 return null;
68 }
69
70 public void setAttribute(Attribute arg0) throws AttributeNotFoundException,
71 InvalidAttributeValueException, MBeanException, ReflectionException {
72 return;
73 }
74
75 public AttributeList setAttributes(AttributeList arg0) {
76 return null;
77 }
78 }
79
其它动态 MBean
另外还有两类 MBean:Open MBean 和 Model MBean。实际上它们也都是动态 MBean。
Open MBean 与其它动态 MBean 的唯一区别在于,前者对其公开接口的参数和返回值有所限制 —— 只能是基本类型或者 javax.management.openmbean 包内的 ArrayType、CompositeType、TarbularType 等类型。这主要是考虑到管理系统的分布,很可能远端管理系统甚至 MBServer 层都不具有 MBean 接口中特殊的类。
Model Bean
然而,普通的动态 Bean 通常缺乏一些管理系统所需要的支持:比如持久化 MBean 的状态、日志记录、缓存等等。如果让用户去一一实现这些功能确实是件枯燥无聊的工作。为了减轻用户的负担,JMX 提供商都会提供不同的 ModelBean 实现。其中有一个接口是 Java 规范中规定所有厂商必须实现的:javax.management.modelmbean.RequiredModelBean。通过配置 Descriptor 信息,我们可以定制这个 Model Bean, 指定哪些 MBean 状态需要记入日志、如何记录以及是否缓存某些属性、缓存多久等等。这里,我们以 RequiredModelBean 为例讨论 ModelBean。比如,我们先来看一个例子,首先是 server 端:
2
3 public class Server {
4
5 private long startTime;
6
7 public Server() { }
8
9 public int start(){
10 startTime = System.currentTimeMillis();
11 return 0;
12 }
13
14 public long getUpTime(){
15 return System.currentTimeMillis() - startTime;
16 }
17 }
18
然后我们对它的监测如下:
2
3 import javax.management.*;
4 import javax.management.modelmbean.*;
5 public class Main {
6
7 public static void main(String[] args) throws Exception{
8 MBeanServer mBeanServer = MBeanServerFactory.createMBeanServer();
9 RequiredModelMBean serverMBean =
10 (RequiredModelMBean) mBeanServer.instantiate(
11 "javax.management.modelmbean.RequiredModelMBean");
12
13 ObjectName serverMBeanName =
14 new ObjectName("server: id=Server");
15 serverMBean.setModelMBeanInfo(getModelMBeanInfoForServer(serverMBeanName));
16 |-------10--------20--------30--------40--------50--------60--------70--------80--------9|
17 |-------- XML error: The previous line is longer than the max of 90 characters ---------|
18 Server server = new Server();
19 serverMBean.setManagedResource(server, "ObjectReference");
20
21 ObjectInstance registeredServerMBean =
22 mBeanServer.registerMBean((Object) serverMBean, serverMBeanName);
23
24 serverMBean.invoke("start",null, null);
25
26 Thread.sleep(1000);
27
28 System.out.println(serverMBean.getAttribute("upTime"));
29 Thread.sleep(5000);
30 System.out.println(serverMBean.getAttribute("upTime"));
31 }
32
33 private static ModelMBeanInfo getModelMBeanInfoForServer(ObjectName objectName)
34 throws Exception{
35 ModelMBeanAttributeInfo[] serverAttributes =
36 new ModelMBeanAttributeInfo[1];
37 Descriptor upTime =
38 new DescriptorSupport(
39 new String[] {
40 "name=upTime",
41 "descriptorType=attribute",
42 "displayName=Server upTime",
43 "getMethod=getUpTime",
44 });
45 serverAttributes[0] =
46 new ModelMBeanAttributeInfo(
47 "upTime",
48 "long",
49 "Server upTime",
50 true,
51 false,
52 false,
53 upTime);
54
55 ModelMBeanOperationInfo[] serverOperations =
56 new ModelMBeanOperationInfo[2];
57
58 Descriptor getUpTimeDesc =
59 new DescriptorSupport(
60 new String[] {
61 "name=getUpTime",
62 "descriptorType=operation",
63 "class=modelmbean.Server",
64 "role=operation"
65 });
66
67 MBeanParameterInfo[] getUpTimeParms = new MBeanParameterInfo[0];
68 serverOperations[0] = new ModelMBeanOperationInfo("getUpTime",
69 "get the up time of the server",
70 getUpTimeParms,
71 "java.lang.Long",
72 MBeanOperationInfo.ACTION,
73 getUpTimeDesc);
74
75 Descriptor startDesc =
76 new DescriptorSupport(
77 new String[] {
78 "name=start",
79 "descriptorType=operation",
80 "class=modelmbean.Server",
81 "role=operation"
82 });
83 MBeanParameterInfo[] startParms = new MBeanParameterInfo[0];
84 serverOperations[1] = new ModelMBeanOperationInfo("start",
85 "start(): start server",
86 startParms,
87 "java.lang.Integer",
88 MBeanOperationInfo.ACTION,
89 startDesc);
90
91 ModelMBeanInfo serverMMBeanInfo =
92 new ModelMBeanInfoSupport(
93 "modelmbean.Server",
94 "ModelMBean for managing an Server",
95 serverAttributes,
96 null,
97 serverOperations,
98 null);
99
100 //Default strategy for the MBean.
101 Descriptor serverDescription =
102 new DescriptorSupport(
103 new String[] {
104 ("name=" + objectName),
105 "descriptorType=mbean",
106 ("displayName=Server"),
107 "type=modelmbean.Server",
108 "log=T",
109 "logFile=serverMX.log",
110 "currencyTimeLimit=10" });
111 serverMMBeanInfo.setMBeanDescriptor(serverDescription);
112 return serverMMBeanInfo;
113 }
114
很明显,和其它 MBean 类似,使用 Model MBean 的过程也是下面几步:
创建一个 MBServer:mBeanServe
获得管理资源用的 MBean:serverBean
给这个 MBean 一个 ObjectName:serverMBeanName
将 serverBean 以 serverMBeanName 注册到 mBeanServer 上去
唯一不同的是,ModelMBean 需要额外两步:
2 2.serverMBean.setManagedResource(server, "ObjectReference");
3
第一步用于提供 serverMBean 的元数据,主要包括以下两类
类似于普通的动态 MBean,需要 MBean 的 Attribute、Invocation、Notification 的类型/反射信息,诸如返回类型、参数类型和相关的 get/set 方法等。这里将不再赘述。
关于缓存、持久化以及日志等的策略。后面我们将介绍一些这方面的信息。
第二步指出了 ServerMBean 管理的对象,也就是说,从元数据中得到的 Method 将施加在哪个 Object 上。需要指出的是 setManagedResource(Object o, String type); 中第二个参数是 Object 类型,可以是 "ObjectReference"、"Handle"、"IOR"、"EJBHandle" 或 "RMIReference"。目前 SE 中的实现只支持 "ObjectReference"。笔者认为后面几种类型是为了将来 JMX 管理对象扩展而设定的,可能将来 Model Bean 不仅可以管理 Plain Java Object(POJO),还可能管理 Native Resource, 并给诸如 EJB 和 RMI 对象的管理提供更多的特性。
Model Bean 与普通动态 Bean 区别在于它的元数据类型 ModelMBeanInfo 扩展了前者的 MBeanInfo,使得 ModelMBeanOperationInfo、ModelMBeanConstructor_Info、ModelMBeanAttributeInfo 和 ModelMBeanNotificationInfo 都有一个额外的元数据:javax.management.Descriptor,它是用来设定 Model Bean 策略的。数据的存储是典型的 "key-value" 键值对。不同的 Model Bean 实现,以及不同的 MBeanFeatureInfo 支持不同的策略特性。下面我们就以 Attribute 为例,看一下 RequiredModelBean 支持的策略。
首先,它最重要的 Descriptor 主要是 name、displayName 和 descriptorType,其中 name 是属性名称。"name" 要与对应 ModelMBeanAttributeInfo 的 name 相同。descriptorType 必须是 "attribute"。
另外,value、default、legalValues "value" 是用来设定初始值的,"default" 指当不能从 resource 中获得该属性时的默认返回值,"legalValues" 是一组合法的属性数据。它并不用来保证 setAttribute 的数据一致性,而是在 UI 系统,如 JConsole 中提示用户可能的数据输入。
在属性访问的 getMethod, setMethod 方法上,事实上所有对属性的访问都会被 delegate 给同一 MBeanInfo 中特定的 Operation。 getMethod/setMethod 给出了对应的 ModelMBeanOperationInfo 名称。
还有一些额外的属性,比如:persistPolicy, persistPeriod 是代表了持久化策略;currencyTimeLimit, lastUpdatedTimeStamp 缓存策略;iterable 属性是否必须使用 iterate 来访问。默认为否;protocolMap 定义了与第三方系统有关的数据转换的 data model;visibility 定义了与第三方 UI 系统有关的 MBean 如何显示的策略;presentationString 也是定义了与第三方 UI 系统有关的 MBean 如何显示策略,比如 "presentation=server.gif"。
事实上,策略特性有两个层次的作用域:整个 Model Bean 和特定的 MBeanFeature。
Model Bean 的策略描述会被施加到该 Model Bean 的所有 MBeanFeature 上去,除非该 MBeanFeature 重写了这个策略特性。
在上面的例子里,这一个语句:
给整个 serverMBeanInfo 设了一个策略描述 serverDescription,其中用 "currencyTimeLimit=10" 指出属性的缓存时间是 10 秒。所以,在 Main 方法中,两次 serverMBean.getAttribute("upTime");之间的间隔小于 10 秒就会得到同样的缓存值。
如果我们不想让 "upTime" 这个属性被缓存,我们可以在它的策略描述中加入 "currencyTimeLimit=-1":
2 new String[] {
3 "name=upTime",
4 "descriptorType=attribute",
5 "displayName=Server upTime",
6 "getMethod=getUpTime",
7 "currencyTimeLimit=-1" //不需要缓存
8 });
9
10 Descriptor getUpTimeDesc =
11 new DescriptorSupport(
12 new String[] {
13 "name=getUpTime",
14 "descriptorType=operation",
15 "class=modelmbean.Server",
16 "role=operation"
17 ,"currencyTimeLimit=-1" //不需要缓存
18 });
19
getUpTimeDesc 也要改动的原因是 RequiredModelBean 会把获取 upTime 属性的工作 delegate 给 getUpTime invocation。只要其中一处使用 MBean 级的缓存策略,就没法获得实时 upTime 数据了。