什么是MBean
Managed Bean (MBean) 描述一个可管理的资源.是一个java对象,遵循以下一些语义.java对象的属性(property)叫属性(attribute),方法(method)叫操作(operations).
定义MBean
一个MBean的语义
- 必须是公用的,非抽象的类
- 必须有至少一个公用的构造器
- 必须实现它自己的相应的MBean接口或者实现javax.management.DynamicMBean接口
可选的,一个MBean可以实现javax.management.NotificationBroadcaster接口
MBean的类型
- 标准MBean
- 动态MBean
- 模型MBean
- 开放MBean
标准MBean
标准MBean的语义
- 应为一个具体类(非抽象类)
- 至少具有一个公用构造器
- 应该继承它自己相应的MBean接口
标准MBean接口的实现名称必须遵循以下词法模式:MBean.如果MBean名称是Library,他的管理接口应该是LibraryMBean.
在标准MBean,属性和操作将由内省MBean接口获得.
属性
Attributes are the fields or properties of the MBean, and they are present in the management interface of MBeans. Attributes represent the data available for management. Any Java object can be represented as an attribute. For a Java object to be represented as an attribute, there are some semantics to be followed in the code.
任何jafa对象都可以作为属性,在代码中有一些语法需要遵循.
属性的语法
To define a read write attribute with the name "Name" of Java datatype String, the management interface should define the getter and setter methods as mentioned below:
定义一个可读写属性,用名字"Name",java数据类型String,管理接口应该定义getter和setter方法如下:
public String getName(); public void setName(String some_name);
可以只具有getter函数(只读)和只具有setter函数(只写).
如果是boolean属性,则用以下方法代替getter方法:
public boolean is();
操作
操作是JMX可管理的资源的行为,任何在管理接口中没有标志为属性的方法都视为操作.操作的名称就是相应的方法名称.
一个操作可以定义为任意数量的参数和任意类型,可返回任意类型.
如,
public void destroy();
视为一个名为destroy的操作.
编写自己的标准MBean
让我们创建一个标准MBean来管理Web服务器.
以下为属性:
- ServerName: 服务器运行的物理机器名称.
- ServerId: An identifier for the Server. In a same machine, multiple servers may run at different ports.
- ServerStarted: 检查服务器是否开着.
- Port: T服务器监听端口.
以下为操作:
- startService: 启动服务器
- stopService: 停止服务器
作为一个标准MBean,应继承它自己的相应的MBean接口.
首先,我们将定义这个标准MBean的接口.如果要定义标准MBean名称为ServerInfo.因此,管理接口应为ServerInfoMBean.
public abstract interface ServerInfoMBean
{
public String getServerName( );
public String getServerId( );
public boolean isServerStarted( );
public int getPort( );
public void setPort(int port);
public void startService( );
public void stopService( );
}
从以上接口定义,MBean 有getter方法的属性有ServerName, ServerId, ServerStarted, and Port.setter方法只有Port属性有.则只有Port属性是可读写的,其他都为只读.
public class ServerInfo implements ServerInfoMBean
{
private String serverName = null;
private String serverId = null;
private boolean serverStarted;
private int port;
// At least one public constructor is required
public ServerInfo( )
{
serverName = "test-server";
serverId = "test-server_1";
serverStarted = true;
port = 8072;
}
// overloaded public constructor
public ServerInfo(String serverName, String serverId, boolean serverStarted, int port)
{
this.serverName = serverName ;
this.serverId = serverId ;
this.serverStarted = serverStarted ;
this.port = port ;
}
// Implementating the ServerInfoMBean
public String getServerName( )
{
// get the ServerName
return serverName;
}
public String getServerId( )
{
// get the ServerIdentifier
return serverId;
}
public boolean isServerStarted( )
{
// check whether the Server is Started
return serverStarted;
}
public int getPort( )
{
// get the ServerPort
return port;
}
public void setPort(int port)
{
// Stop the server. Set the ServerPort. Start the server.
stopService( );
this.port = port;
startService( );
}
public void startService( )
{
System.out.println("Starting server..... ");
// start the Server
System.out.println("Server started successfully. ");
serverStarted =true;
}
public void stopService( )
{
System.out.println("Stopping server..... ");
// stop the Server
System.out.println("Server stopped ");
serverStarted =false;
}
// Additional methods not exposed for management
public void restart( )
{
stopService( );
startService( );
}
}
属性名称重载
We have already seen in the Standard MBean section that attribute names cannot be overloaded, that is, there cannot be two setters or getter and setter pair for the same name that operates on different types. Let us see this with some examples.
写一个简单的MBean实现类Server实现ServerMBean接口.
Case 1
两个setter已经被重载:
public class Server implements ServerMBean
{
.......
public void setState(int state)
{
// implementation of setState
}
public void setState(boolean flag)
{
// implementation of setState
}
}
在以上的case中,两个setter被重载,它不可能确定属性State是int类型还是boolean类型.于是,上面的类不是合法的类.
Case 2
一对相同名字不同类型的getter和setter方法:
public class Server implements ServerMBean
{
.......
public int getState()
{
// implementation of getState
}
public void setState(boolean flag)
{
// implementation of setState
}
}
For the above case, it is not possible to determine whether State is a readOnly attribute of type int or a writeOnly attribute of type boolean. 于是,上面的类也不是合法的MBean.
特别的类
以下的类是可能的:
Case 1
public class Server implements ServerMBean
{
.......
public void setState(int state)
{
// implementation of setState
}
public void setState(boolean flag, int state)
{
// implementation of setState
}
}
Case 2
public class Server implements ServerMBean
{
.......
public int getState()
{
// implementation of getState
}
public int getState(boolean flag)
{
// implementation of getState
}
}
Case 1中,setState(boolean flag, int state) 将作为一个操作处理.
Case 2中,getState(boolean flag) 也作为一个操作处理.
动态MBean
一个java类直接或者间接继承 javax.management.DynamicMBean 接口即称为动态MBean.在一个动态MBean中,属性和操作在运行期间才能显示.因此,动态MBean比较适用于管理信息不是静止的情况.
优势
- 不需定义你自己的管理接口.
- 属性和操作只在运行时显现,显现的属性和操作可以动态控制.
- 动态MBean和它的属性及方法的描述可以获得.
动态MBean接口
javax.management.DynamicMBean 接口定义了以下6个方法:
- getMBeanInfo
- getAttribute
- setAttribute
- getAttributes
- setAttributes
- invoke
getMBeanInfo
public javax.management.MBeanInfo getMBeanInfo()
这个方法返回一个MBeanInfo对象.从这个对象,属性、操作、构造器和通知信息能被读取.管理信息是从这个方法从MBean获得的.这是一个动态MBean比标准MBean优势的一点.在标准MBean,管理信息将由MBean的内省获得.因此一些标准的方法命名词法模式必须由继承类遵循.动态MBean在这类上比较自由.如果继承的类已经定义方法printName(),没有遵循标准的命名词法模式,这个方法能被mapping到MBean中的一些属性(叫ServerName)的getter或者setter方法.
getAttribute and setAttribute
public Object getAttribute(String AttributeName)
throws javax.management.AttributeNotFoundException,
javax.management.MBeanException,
javax.management.ReflectionException
public void setAttribute(javax.management.Attribute AttributeName)
throws javax.management.AttributeNotFoundException,
javax.management.InvalidAttributeValueException,
javax.management.MBeanException,
javax.management.ReflectionException
getAttributes and setAttributes
public javax.management.AttributeList getAttributes(String[] AttributeNames)
public javax.management.AttributeList setAttributes(javax.management.AttributeList AttributeNames)
invoke
方法调用允许一个动作(方法)在动态MBean中调用.这个方法的声明如下:
public Object invoke(String methodName, Object[] parameters, String[] signature)
throws javax.management.MBeanException,
javax.management.ReflectionException
需记住的几点
- 动态MBean必须是一个具体的类,至少具有一个公共构造器,应该继承 javax.management.DynamicMBean 接口.
- 在一个动态 MBean 中,属性和操作被组成MBeanInfo对象,由 getMBeanInfo 方法返回.
- 没有属性命名要遵循的语义.可以 Mapping 一些方法 foo( ) 到一个名为 ServerName 的属性.
- 描述 MBean 和它的属性和方法变为可能.
什么时候使用动态 MBeans?
以下是实践中使用动态MBean的 使用案例:
Case 1
看看管理一个Web服务器.如果基于角色的服务要被实现,如,只有用户具有管理员权限的时候才能启动或者停止服务器.一个动态MBean能够被写成为包含一个 submitUserInfo 的方法,具有两个参数,用户名和密码.如果用户名和密码正常匹配,那么getMBeanInfo将返回一个MBeanInfo对象,描述操作startService和stopService. 否则,管理操作将不显示.
Case 2
看看配置一个 logger. 很明显在配置它的属性期间,logger将不被激活.一个动态MBean能够写成在logger运行期间显示只读logger属性.它也显示停止logger的操作.一旦logger停止,属性就变为可读写,以便进行logger配置.这类场景能够用动态MBean轻松搞定.动态MBean的例子中提供了这样一个实例.
模型MBean
一个模型MBean其实就是一个动态MBean,只是更复杂一些.模型MBean和动态MBean的主要的区别就是你不需要编写MBean类,因为任何JMX的实现(我想不同公司的JMX的实现都会稍微有些差异吧)都应提供一个缺省的模型MBean接口的实现,叫RequiredModelMBean.
模型MBean比较动态MBean的优势
- JMX已经提供了一个缺省的模型 MBean 的实现RequiredModelMBean
- 当创建一个动态MBean的时候,管理的资源(包含动态MBean实现细节的对象)必须指定.可当是模型MBean时,管理的资源能动态用RequiredModelMBean类中的方法setManagedResource设置.
- 在动态MBean,所有需要的管理信息必须预先知道.此外的一些单独的信息可以动态显示.在模型MBean,自身的管理信息能用setModelMBeanInfo方法动态配置.
- 可以用Descriptors指定关于属性和操作的行为的附加数据.可以添加自定义的Descriptors.
- 可以使用protocolMap描述器转换MBean数据和数据模型到其他管理技术及其模型.
- 值添加服务,如持久化、缓存、属性变更通知和日志都已经被提供.另外一个重要的特性时这些服务能够为每一个属性和操作定制.
- 模型MBean的管理信息能够用文件格式如XML指定.这减少了大量的编码工作.
模型MBean元数据类
属性,方法,通知,这些模型MBean的管理信息 ,能够用ModelMBeanInfo接口显示.简单地, ModelMBeanOperationInfo, ModelMBeanConstructorInfo, 和ModelMBeanNotificationInfo描述一个模型MBean的操作,构造器和通知.这些描述模型MBean的管理信息的类称为模型MBean的元数据类.模型MBean的元数据类是
- ModelMBeanAttributeInfo -- 描述一个属性
- ModelMBeanOperationInfo -- 描述一个操作的符号
- ModelMBeanConstructorInfo -- 描述一个构造器的符号
- ModelMBeanNotificationInfo -- 描述一个通知
- DescriptorSupport -- 提供属性,操作,构造器,通知的附加信息
RequiredModelMBean
JMX提供了一个模型MBean接口的缺省实现,叫RequiredModelMBean.这个模型MBean的实现着意提供方便的使用和这个工具广泛的缺省管理行为.一个模型MBean能够从RequiredModelMBean继承来创建,如果需要也可以覆写(overridden).
RequiredModelMBean能够用没有参数的构造器初始化.这将构造一个带有空的ModelMBeanInfo的RequiredModelMBean.这个RequiredModelMBean的MBeanInfo和Descriptors能够用setModelMBeanInfo(javax.management.modelmbean.ModelMBeanInfo)方法定制.在定制后,RequiredModelMBean能够注册到MBeanServer.管理资源(如对象的实例处理,它的所有在这个模型MBean管理接口的方法都被执行)能够被设置到RequiredModelMBean,用setManagedResource(java.lang.Object, String)方法,取得实际的已管理对象和类型,如它可能是"ObjectReference", "Handle", "EJBHandle", "IOR" (Interoperable Object Reference), or "RMIReference".
Descriptors
Descriptors类实现Descriptor接口,被用在模型MBean的元数据来添加MBeanInfo中定义的管理接口的行为策略,如缓存,持久化和日志到已管理的bean.
Descriptors是一个名称-值列表(每一个都是字符串名称,对象值)提供关于属性,操作,通知的行为的附加信息.当定义MBean属性的管理接口时,可以添加一个描述器,描述属性的行为策略.模型MBean的实现将试图适配匹配的需求行为.这个行为的描述也能够通过一个管理应用程序改变或者在运行时通过已管理资源改变.
消息(Notifications)
总的来说,在管理者(一个管理者可能是协议适配器或者其他监听消息的MBean),JMX代理和已管理资源之间的通信,将
步骤1.管理者提出请求 步骤2.JMX代理转发请求到已管理资源并从已管理资源获得信息 步骤3.JMX代理送回被管理者接收的响应.
这里有一些特别的场景,当有些事件发生时一个JMX代理发送一些信息到管理者.这些事件术语称消息(Notifications);
javax.management.Notification 类
Notification类描述一个由一个MBean发送出来的消息.Notification对象包含以下字段:
Type -- 一个包含小数点的字符串表达式.如Server.started SequenceNumber -- 一个long值表达式系列数字,标志在消息广播上下文中一个特定实例的消息. TimeStamp -- 一个指定何时消息产生的时间戳. Message -- 描述消息的字符串描述. UserData -- 一个哈西表,描述消息广播器希望和它的监听者通信的附加数据.
发送消息
一个MBean或者一个java类能够传送消息,术语称为消息广播者.任何类型的MBean,如标准,动态,模型MBean,都能够广播消息.如一个MBean或者java类广播消息,它必须实现NotificationBroadcaster接口.另一个较简单的方法是MBean或者java类可以扩展NotificationBroadcasterSupport类.NotificationBroadcasterSupport类实现NotificationBroadcaster接口,并提供一个方法叫sendNotification,这个方法带了一个javax.management.Notification 类型的对象.当这个sendNotification方法被调用时,消息将被送到所有注册的监听者.
注册监听者
一个监听者能够是一个MBean或者任意其他实现了NotificationListener接口的对象.
任意数量的监听者都能够被注册到一个消息广播器.一个监听者通过调用广播器上的addNotificationListener 方法注册到广播器.这个方法的语法为
public void addNotificationListener(NotificationListener listener, NotificationFilter filter, Object handback)
当上面的方法被调用,监听者对象或者监听者MBean被注册到广播器,作为总多监听者的一员.当sendNotification方法被调用时,在注册期间监听者MBean将被原样随同消息传回这个对象已被使用..(The Object handback that is used while registering the listener MBean will be sent unmodified along with the Notification when sendNotification is invoked.)
一个监听者在一个广播者中仅能注册一次.要在相同的广播者上注册超过一次,那么对象传回通过当调用addNotificationListener 应该不同.(For registering it more than once with the same broadcaster, then the Object handback passed while invoking the addNotificationListener should be different.)
接收消息
任何对象或者MBean都能够接受任意MBean发送的消息. 为达到这个目的,对象或者MBean将
- 注册自己为监听者一员到将接收消息的MBean或者java类.
- 实现NotificationListener接口.
注册对象或者MBean为监听者一员,参考上面提供的"注册监听者"小节.
实现NotificationListener 接口:
NotificationListener接口只有一个方法叫handleNotification.这个方法的语法为
public void handleNotification(Notification notif, Object handback)
这个方法是个回调方法.当方法sendNotification被调用,广播者发送一个消息到所有已经注册的监听者,每一个监听者的handleNotification方法也被调用.所获得的传回对象将是那个已经被使用在addNotificationListener方法的对象.
过滤消息
当一个消息监听者被自己注册到消息广播者后,任意这个广播者发送的消息都能够接受到.可能有这样的情况,这个监听者只想从广播者接收特定的消息 .
这种情况由当注册监听者到消息广播者时,定义一个消息过滤器并传递它来实现.任何实现NotificationFilter接口的对象都可以作为消息过滤器. NotificationFilter 接口定义一个唯一的方法 isNotificationEnabled ,方法的语法为
public boolean isNotificationEnabled(Notification notif)
比较简易的创建消息过滤器的方法时继承 NotificationFilterSupport 类.NotificationFilterSupport 具有已经定义的方法象 disableType(String type) 和 enableType(String type) .因此,通过 在消息过滤器对象调用 disableType("Server.restart") ,类型 Server.restart 的消息将不被接收.
消息机制
- 一个消息的产生通过在消息广播者MBean上调用 sendNotification 方法产生.
- 消息广播者检查注册在它自身的消息监听者列表.
- 对于每一个已注册的监听者,它检查是否有实施的过滤器,如它调用 isNotificationEnabled(Notification notif).
- 如果isNotificationEnabled返回false , 那么没有消息发送到此监听者.
- 如果isNotificationEnabled返回true, 那么消息对象随同相应的监听者传回的对象将发送到此监听者.这个传回对象将是那个在调用addNotificationListener(... , ..., Object handback) 已经被赋予的对象.
- 当一个消息被送到监听者,监听者的handleNotification方法被调用.
需记住的几点
- 任意类型的MBean,标准,动态,模型或者非MBean类能够是一个消息广播者,消息监听者或者同时是两者.
- 用一个MBean或者java类来作为一个消息广播者,它将实现NotificationBroadcaster接口.简单的方法是继承NotificationBroadcasterSupport.
- 用一个MBean或者一个java类作为一个消息监听者,它将实现NotificationListener接口.为接收消息,它将被作为监听者一员注册被注册到消息广播者.
- 消息能够被过滤掉,当调用addNotificationListener方法时,通过传递一个消息过滤器实现.
- 消息过滤能根据消息类型,或者消息的message消息,或者任意其他东东,取决于怎样实现消息过滤器.
- 转换一个消息到指定协议的实现,如SNMP,是协议适配器的职责.
代理层次
一个JMX代理是这样一个管理实体, 在JVM中运行表现为MBean和管理应用的联络人.一个JMX代理 由一个MBeanServer,一套MBean描述管理资源,一个很小数量的被实现为MBean的代理服务 ,任意典型的最少一个协议适配器或者连接器组成.
代理层次提供一个实现代理的规范.这个层次被建立在工具层,并用于定义一个标准化的代理来管理JMX可管理资源.
这个代理层次包括:
- 一个MBeanServer
- 代理服务,如
- 事件服务
- 监视器服务
- MLet服务(动态加载)
- 相关服务
什么是MBeanServer
MBeanServer是一个包含所有注册MBean的仓库.它是JMX代理层的核心.JMX1.0规范提供一个接口叫 javax.management.MBeanServer. 所有管理的在MBean操作通过MBeanServer执行.使用MBeanServer实例,你能够管理所有MBean.每一个MBean具有一个唯一标志,叫ObjectName.
什么是ObjectName
ObjectName (javax.management.ObjectName)是一个类,唯一标志一个在MBeanServer的MBean.这个对象名称用于管理应用程序来标志MBean以便操作能够在适当的MBean上被调用.一个对象名称包含两部分.它们是
- 一个域名称
- 一个没有经过排序的一个或者多个关键属性集
域名称是一个大小写敏感的字符串,这个字符串可以包括任何除[: , = * ?]之外的字符.
域名称部分可以在一个对象名称里面忽略,如果这样的话,MBeanServer能够提供一个缺省的域名称.缺省的域名称将是DefaultDomain.
关键属性集组织对象名称的第二部分.一个关键属性是一个名称-值对,名称不需对应MBean的任意属性或者操作.关键属性集必须包含至少一个关键属性.它可以包含任意数量的关键属性,这些关键属性的顺序没有意义.
例如,如果我们描述一个海豚为MBean,那么一套对象名称可以是
Animals:name=dolphin,kind=fish
在这个对象名称中,字符串Animals 组成域名称, 关键属性集由两个关键属性描述:名称和类型,它们的值分别为dolphin和fish.
通用的对象名称描述 如以下语法:
[domain name]:property=value[,property=value]
如果域名称被省略, MBeanServer将提供一个缺省的域名称叫[DefaultDomain].至少必须包含一个关键属性.任意数量的关键属性都可以添加进来.
创建一个MBeanServer 实例
MBeanServer实例能够用MBeanServerFactory类提供的方法获取.MBeanServerFactory类提供了以下方法来获取MBeanServer实例:
public static MBeanServer createMBeanServer()
public static MBeanServer createMBeanServer(String domain)
public static MBeanServer newMBeanServer()
public static MBeanServer newMBeanServer(String domain)
在以上声明的方法中,字符串domain描述对象名称(object name)的域部分.createMBeanServer方法和newMBeanServer方法返回一个MBeanServer实例.这两个方法的区别在于createMBeanServer返回一个MBeanServer实例并且保存这个实例的关联以便能够用findMBeanServer方法访问这个实例.newMBeanServer方法将不保存这个被创建的MBeanServer的实例的任何关联.
如果使用一个变量的构造器创建一个MBeanServer实例,这个字符串变量将作为创建的MBeanServer实例的域名.如果没有变量的构造器被用来创建MBeanServer实例,那么域名称将用字符串DefaultDomain.
查找一个MBeanServer 实例
MBeanServerFactory类提供一个方法叫findMBeanServer用于一个指定MBeanServer实例或者所有MBeanServer实例能够被获取的情况.这个方法的声明为
public static ArrayList findMBeanServer(String id) 这里字符串id表明 MBeanServer 的标志符. 如果这个字符串为null, 那么返回所有在此JVM中注册的 MBeanServer . 如果想返回相应指定的MBeanServer实例,那么必须提供这个MBeanServer实例的相应字符串id. 这个字符串id能够通过 MBeanServerDelegateMBean 的StringId 属性获取.
MBeanServerDelegate MBean
无论一个MBeanServer何时被初始化,MBeanServerDelegate类被作为一个MBean注册,名字为JMImplementation:type=MBeanServerDelegate. 这个MBean提供关于这个 MBean 服务器的信息.MBeanServerDelegate MBean 具有七个只读属性,名字为 MBeanServerId, SpecificationName, SpecificationVersion, SpecificationVendor, ImplementationName, ImplementationVersion 和ImplementationVendor.
XMOJO项目5 的MBeanServerDelegate MBean 提供的属性的缺省值为:
属性名称 | 属性值 |
SpecificationVersion | 1.0, Final Release |
SpecificationVendor | Sun Microsystems Inc. |
ImplementationVersion | 5 |
ImplementationVendor | XMOJO (sponsered by AdventNet Inc.) |
ImplementationName | The XMOJO Project |
MBeanServerId | _1 |
SpecificationName | Java Management Extensions Instrumentation and Agent Specification |
MBeanServerDelegate MBean 响应 MBeanServer 发出的广播消息.无论何时一个MBean被注册或者被注销,MBeanServerDelegate MBean 广播类型分别为jmx.mbean.created和jmx.mbean.deleted的消息到所有注册的监听器.
需要记住的几点
- MBeanServer 维护一个注册的MBean.每一个MBean由一个唯一命名的模式叫对象名称(object name)区分.
- MBeanServer 实例能够用MBeanServerFactory类的静态方法获得.
- MBeanServerDelegate MBean 是一个MBeanServer的描述,也是MBeanServer发出消息的广播者.
注册 MBean
一个MBeanServer的主要职责是在一个JMX代理中维护一个MBean的注册表.MBean能够以下面两个方法中的任意一个注册:
1) 创建一个MBean实例并注册它到MBeanServer用方法:
public ObjectInstance registerMBean(Object object, ObjectName name) 这里,object是创建的MBean实例,name部分是MBean的一个唯一标志符.
2) 使用createMBean 方法中的一个:
public ObjectInstance createMBean(String classname, ObjectName name) public ObjectInstance createMBean(String classname, ObjectName name, ObjectName loader) public ObjectInstance createMBean(String classname, ObjectName name, Object[] params, String[] types) public ObjectInstance createMBean(String classname, ObjectName name, ObjectName loader, Object[] params, String[] types) createMBean方法使用java自省来创建一个MBean实例.对所有的createMBean方法,有两个变量是通用的.
- String classname -- 要创建的MBean的实例的类名.
- ObjectName name -- MBean要注册的对象名称.
如果用两个变量的createMBean构造器,缺省的类装载器(class loader)用来装载MBean类,MBean被使用缺省的构造器初始化.如果MBean已经被初始化,这个实例将被MBeanServer用第二个变量的对象名称注册.如果你想使用指定的类装载器,那么可以使用三个变量的构造器.这个类装载器将被MBeanServer注册为一个MBean.这个类装载器的对象名称将用做第三个变量.
当实例化一个类,如果任何参数必须传入,那么可以使用四个参数的createMBean方法.这个createMBean方法的最后的两个参数分别包含对象数组(类的初始化必须的)和他们的署名.如果MBean类必须用其他指定的类装载器装载,那么应该使用五个参数的构造器.
注册一个MBean的多个实例
你可能需要监控十多个Web 服务器.如果所有的web服务器的管理信息是相同的,那么你不必编写十个MBean. 一个MBean足够了.我们能够创建多个实例并注册每一个实例以一个唯一的对象名称.
现在可以看到使用对象名称来避免混淆的重要性.
很明显服务器的端口数字和服务器的名称将是任意服务器的唯一,那是因为,在一个机器上,你不能运行两个服务器在一个端口上.
于是,可以创建对象名称如
Servers:hostName=localhost,portNumber=xxxx
假设超过一个服务器在同一个机器上启动,那么对象名称应该为
Servers:hostName=localhost,portNumber=yyyy
由于对象名称是注册MBean的唯一标志,多个MBean的实例将被注册为不同的对象名称.
对象名称通常用他们包含的关键值对来区分,如一个服务器MBean的主机名和端口名关键字及他们各自的值.同理,唯一标志相似的MBean组,对象名称将用相应的关键字和值对拼装.例如,type=ServerMBean指出这个MBean是一个服务器MBean类型.由此,对象名称值 Server:type=ServerMBean,hostName=localhost,portNumber=8050 提供一个简单的MBean分组,产生为服务器MBean的实现.并且也提供了一个方法来唯一标志在localhost:8050上运行的服务器的MBean实例.
一个简单的示范
让提供管理信息(所有web服务器通用的)的Mbean为动态MBean,并且起名为ServerInfo.
让两个web服务器运行在机器A(machineA)的端口8080和80.让一个web服务器运行在机器B(machineB)的8080端口,另一个web服务器运行在机器C(machineC)的80端口.总之,我们需要管理这四个web服务器.我们能够用ServerInfo 类和创建四个实例来管理每一个web服务器.注册四个MBean的代码片断如下:
ServerInfo srvr1 = new ServerInfo(); ServerInfo srvr2 = new ServerInfo(); ServerInfo srvr3 = new ServerInfo(); ServerInfo srvr4 = new ServerInfo();
MBeanServer mbs = null;
try { mbs = MBeanServerFactory.createMBeanServer( ); mbs.registerMBean(srvr1, new ObjectName("Server:hostName=machineA,portNumber=8080")); mbs.registerMBean(srvr2, new ObjectName("Server:hostName=machineA,portNumber=80")); mbs.registerMBean(srvr3, new ObjectName("Server:hostName=machineB,portNumber=8080")); mbs.registerMBean(srvr4, new ObjectName("Server:hostName=machineC,portNumber=80")); } catch (Exception e) { e.printStackTrace(); }
注销MBean
MBean能够用MBeanServer 的下面方法注销:
public void unregisterMBean(ObjectName name) 这里name 是这个MBean实例注册的对象名称.
注销MBean后, MBeanServer将不再保存任何这个MBean实例的关联.
控制MBeanRegistration
JMX定义了一个接口叫MBeanRegistration.这个接口的目的是允许MBean开发者对在MBeanServer上注册和注销进行一些控制.这通过MBean实现javax.management.MBeanRegistration接口来达到.
MBeanRegistration接口定义注册控制机制的行为.它定义了以下四个方法:
public ObjectName preRegister(MBeanServer mbs, ObjectName name) public void postRegister() public void preDeRegister() public void postDeRegister()
所有以上的方法都是回调方法,
MBeanServer将在恰当的时机调用这些方法.
如果一个MBean实现MBeanRegistration并且这个MBean被注册,MBeanServer在注册前调用 preRegister方法.这个方法返回的对象名称将在MBean注册过程中使用.在成功完成MBean注册后,MBeanServer调用postRegister方法.
如果以上的MBean被注销,在注销前MBeanServer调用preDeRegister方法.如果注销成功,MBeanServer调用postDeRegister方法.
需要记住的几点
- 对于一个单一MBean类,多个实例能够被创建,并能够被注册为注册时MBean提供的对象名称.
- 无论何时一个MBean被注册,MBeanServer创建一个类型为 jmx.mbean.created 的消息. MBeanServerDelegate MBean 广播这个消息到所有注册的监听者.
- MBeanRegistration接口提供注册和注销过程监控的钩点.
代理服务和时间服务
一个JMX代理提供一系列服务,每一个这些服务术语称为代理服务.总的来说,一个代理服务自身被一个MBean实现以便它能够被其他MBean或者管理应用程序访问.
在这里讨论以下代理服务:
- 时间服务
- 监控服务
- M-Let服务(动态加载)
时间服务
时间服务在指定的日期和时间触发消息;也可以在一个固定间隔重复触发消息.时间服务由一个MBean实现并可以管理.时间服务能够发送它指定的TimerNotification类的消息实例.
下面的图形描述时间服务,帮助你更好理解这个服务如何工作:
时间消息
时间服务通过这两个途径管理消息:
- 消息仅触发一次
- 消息根据一个定义的期间和/或者发生的数量重复
如果一个消息必须从一个时间服务触发,这个消息必须添加到时间服务的消息列表.当一个消息添加到时间服务,一个唯一标志数字将被指定到这个消息.
添加时间消息
时间服务维护一个被请求发送的时间消息的内部列表. 可以使用 Timer 类的addNotification方法将消息添加到这个列表.这个addNotification 可以被重载.所有的addNotification方法具有以下消息参数和date对象:
- type -- 消息类型的字符串
- message -- 消息的字符串消息
- userData -- 消息的用户数据对象
- date -- 消息发生的时间
public Integer addNotification ( String type, String message, Object userData, Date date ) throws IllegalArgumentException public Integer addNotification ( String type, String message, Object userData, Date date , long period ) throws IllegalArgumentException public Integer addNotification ( String type, String message, Object userData, Date date , long period , long nbOccurences ) throws IllegalArgumentException public Integer addNotification ( String type, String message, Object userData, Date date , long period , long nbOccurences , boolean fireImmediate ) throws IllegalArgumentException
重载的方法,附加到消息参数和 date ,可以是以下可选参数:
period -- 消息发生的间隔,以微秒计.如果这个值为0或者空值,消息将不重复.
nbOccurences -- 消息发生的总次数.如果为0或者空值,而period为非空或者非0,那么消息将不确定发生.
fireImmediate -- 如果这个标志设为true,那么在消息添加到计时器列表时将产生第一次消息.如果这个标志为false,那么消息将在添加到计时器时开始的period值延迟后产生第一次消息.
当date为空值,period或者nbOccurences被指定为一个非法值时,IllegalArgumentException错误被抛出.还有一些指定的情况IllegalArgumentException错误被抛出:
Case 1
如果date少于当前时间,和如果period指定为0或者空值,IllegalArgumentException错误将被抛出.
Case 2
如果date少于当前时间和period非空.如果 nbOccurences 被指定,计时器为每一个nbOccurence更新date值 . 如果date值不能够在指定的nbOccurence数字里超过当前时间 , IllegalArgumentException错误将被抛出.
addNotification方法返回新的计时器消息的标志.这个标志能够用于从计时器取得关于这个消息的信息 或者 从计时器的消息列表移除这个消息.然而,在一个消息被添加到消息列表后,它的相关参数不能被更新.
一个简单的代码片断
以下代码片断用下面所列属性创建一个时间消息:
Timer Type: | Register |
Timer Message: | Testing timer service |
Timer Date: | Apr 11, 2002 |
Timer Interval: | 5 seconds |
No. of Occurrence: | 2 |
import javax.management.timer.Timer; .... ....
Timer timer = new Timer ( );
server.registerMBean ( timer , new ObjectName("Services:type=Timer") ); server.addNotificationListener ( new ObjectName("Services:type=Timer") , notifImpl , null , new Object( ) );
timer.addNotification ( "Register" , "Testing timer service" , new String( ) , new Date( 1029244438000L ) , 5000 , 2 ); timer.setSendPastNotifications ( false ); timer.start ( );
在以上代码片断, notifImpl指定一个 消息监听者(NotificationListener).
在上面的消息被注册后,时间服务从指定的date和time每隔5秒发送两个消息到所有注册的监听者.
接收时间消息
如果一个类必须接收时间消息,它必须注册它自己作为一个监听者到计时器MBean,这个MBean也是一个消息广播者.当计时器是活动的时候,并且计时器消息的date为预期的时候,时间服务广播这个消息到所有注册的监听者.这个广播的消息包含消息类型,消息,用户数据和消息在计时器的标志符.
移除计时器消息
当一个消息不在重复或者当它已经完成它的发生次数时,它被从计时器的消息列表移除. 消息也能够明确的移除,通过调用下面所列的 Timer 类的任意方法移除:
- removeNotification(integer) -- 消息标志必须指定.匹配这个标志的消息将被从列表移除.
- removeNotifications(type) -- 消息类型必须指定. 所有匹配指定消息类型的消息将被从计时器的消息列表移除.
- removeAllNotifications -- 清空计时器的消息列表.
启动和停止时间服务
时间服务用启动(start)方法激活,用停止(stop)方法停止. 当时间服务处于非活动状态或者已经被停止时, 即使指定的日期和时间,也没有消息被触发.设置sendPastNotifications为true,这些丢失的消息能够在时间服务重新启动的时候被触发,当它被激活的时候也一样.
你能够确定是否计时器在运行,通过调用计时器的方法isActive( ).如果它运行,这个方法返回true.
需要记住的几点
- 用时间服务,消息能够在指定的日期和时间触发.重复消息也能够被触发.
- 时间服务被实现为标准MBean,能够注册到MBeanServer管理.
- 在时间服务启动之前和之后,没有消息会被触发.
- 当时间服务为非活动的时候丢失的消息能够通过设置sendPastNotifications为true来重新获得.