當我們需要編寫(xiě)一個(gè)Java程序支持JMX時(shí),可以參考該文章:
1. 整體的框架:
添加JMX的代碼的整體框架比較簡(jiǎn)單
// 創(chuàng )建一個(gè)JMX的Bean Server實(shí)例
// create a JMX MBean Server server instance
MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
如果需要管理的對象叫多,建議可以先創(chuàng )建一個(gè)基礎的MBean封裝類(lèi),例如Mina框架中的org.apache.mina.integration.jmx.ObjectMBean類(lèi)。對不同類(lèi)型的管理類(lèi)型繼承ObjectMBean類(lèi),針對需要管理的對象創(chuàng )建一個(gè)合適的封裝MBean,例如IoServiceMBean或者IoFilterMBean類(lèi)。用這樣的處理方式的好處是很明顯的,就是方便分開(kāi)管理。
將需要管理的實(shí)例取一個(gè)ObjectName,這個(gè)Name將成為該MBean實(shí)例的名字,這里需要遵循MBean的命名規范:
ObjectName acceptorName = new ObjectName( acceptor.getClass().getPackage().getName() +
":type=acceptor,name=" + acceptor.getClass().getSimpleName());
然后將該MBean注冊到MBeanServer上
mBeanServer.registerMBean( acceptorMBean, acceptorName );
這樣,首先就完成了MBean的注冊了。
2. 如何在MBean中添加屬性、操作和通知
我們知道MBean中的對象可以分為屬性、操作和通知三類(lèi),所有的信息都是通過(guò)MBeanInfo來(lái)加入MBean的。下面我們就來(lái)看看三種類(lèi)型的對象是如果添加入MBean中的。
屬性:
在MBeanInfo中添加屬性信息時(shí)代碼如下:
List<ModelMBeanAttributeInfo> attributes = new ArrayList<ModelMBeanAttributeInfo>();
通過(guò)在List中添加ModelMBeanAttributeInfo即可。那么問(wèn)題來(lái)了,如何能夠快速添加呢?
一般來(lái)說(shuō),我們在添加屬性時(shí),將屬性分為兩類(lèi),一類(lèi)是該類(lèi)MBean中默認添加的,一類(lèi)是該類(lèi)中個(gè)別的MBean實(shí)例需要特殊添加的。分別用方法:
addAttributes(attributes, source);
addExtraAttributes(attributes);(這個(gè)方法您看著(zhù)寫(xiě),反正搞一個(gè)ModelMBeanAttributeInfo扔進(jìn)attributes就可以)
這里addAttributes有兩個(gè)傳入值,一個(gè)是attributes,也就是我們之前定義的屬性L(fǎng)ist,另一個(gè)source是一個(gè)泛型,也就是傳入的MBean。
對于傳入的MBean,我們用
PropertyDescriptor[] pdescs = Introspector.getBeanInfo(type).getPropertyDescriptors();來(lái)獲得該Bean中的屬性描述。拿到Bean中的屬性,那么下面干什么就由我們了....
當然,以下的一些過(guò)濾方法還是值得效仿的:
//對于只讀屬性就算了吧...
if (pdesc.getReadMethod() == null) {
continue;
}
// 對于管不了的就算了吧,比如這里是Class的屬性
String attrName = pdesc.getName();
Class<?> attrType = pdesc.getPropertyType();
if (attrName.equals("class")) {
continue;
}
// 對于沒(méi)有讀功能的就算了吧,至于什么是有讀功能?那還不是你說(shuō)啥就是啥:)
if (!isReadable(type, attrName)) {
continue;
}
// 看看這個(gè)屬性能不能繼續往下拆,比如Session啦,能的話(huà)爸爸就放了,只抓最下面的,誰(shuí)讓最下面的有價(jià)值呢:).
if (isExpandable(type, attrName)) {
expandAttribute(attributes, object, prefix, pdesc);
continue;
}
搞了一堆,然后呢,當然是裝進(jìn)List了
attributes.add(new ModelMBeanAttributeInfo(fqan, convertType(object.getClass(), attrName, attrType,
writable).getName(), pdesc.getShortDescription(), true, writable, false));
這里要特別說(shuō)說(shuō)converType方法,這個(gè)方法就是一個(gè)徹徹底底的類(lèi)型大解密,怎么說(shuō)呢,還是看代碼吧。
private Class<?> convertType(Class<?> type, String attrName, Class<?> attrType, boolean writable) {
if ((attrName != null) && ((attrType == Long.class) || (attrType == long.class))) {
if (attrName.endsWith("Time") && (attrName.indexOf("Total") < 0) && (attrName.indexOf("Min") < 0)
&& (attrName.indexOf("Max") < 0) && (attrName.indexOf("Avg") < 0)
&& (attrName.indexOf("Average") < 0) && !propertyDescriptors.containsKey(attrName + "InMillis")) {
return Date.class;
}
}
if (IoFilterChain.class.isAssignableFrom(attrType)) {
return Map.class;
}
if (IoFilterChainBuilder.class.isAssignableFrom(attrType)) {
return Map.class;
}
if (!writable) {
if (Collection.class.isAssignableFrom(attrType) || Map.class.isAssignableFrom(attrType)) {
if (List.class.isAssignableFrom(attrType)) {
return List.class;
}
if (Set.class.isAssignableFrom(attrType)) {
return Set.class;
}
if (Map.class.isAssignableFrom(attrType)) {
return Map.class;
}
return Collection.class;
}
if (attrType.isPrimitive() || Date.class.isAssignableFrom(attrType)
|| Boolean.class.isAssignableFrom(attrType) || Character.class.isAssignableFrom(attrType)
|| Number.class.isAssignableFrom(attrType)) {
if ((attrName == null) || !attrName.endsWith("InMillis")
|| !propertyDescriptors.containsKey(attrName.substring(0, attrName.length() - 8))) {
return attrType;
}
}
}
return String.class;
}
竊以為上面這個(gè)方法還是很值得拿來(lái)直接用的。
到這里,屬性搞定!
2. 操作
在MBeanInfo中添加操作信息如下:
List<ModelMBeanOperationInfo> operations = new ArrayList<ModelMBeanOperationInfo>();
和屬性類(lèi)似,也是分為兩類(lèi)一類(lèi)是該類(lèi)MBean中默認添加的,一類(lèi)是該類(lèi)中個(gè)別的MBean實(shí)例需要特殊添加的。分別用方法:
addOperations(operations, source);
addExtraOperations(operations);
說(shuō)到方法就沒(méi)有屬性那么復雜了,應該類(lèi)本身就有獲得方法的函數object.getClass().getMethods()
當然了,這里也有一些函數可以讓大家來(lái)參考的:
// 忽略Get和Set方法.
if (mname.startsWith("is") || mname.startsWith("get") || mname.startsWith("set")) {
continue;
}
// 忽略從Object那里繼承過(guò)來(lái)的方法.
if (mname.matches("(wait|notify|notifyAll|toString|equals|compareTo|hashCode|clone)")) {
continue;
}
// 忽略一些不想當作方法的函數
if (!isOperation(mname, m.getParameterTypes())) {
continue;
}
和屬性不太一樣的地方,就是操作都有參數的,這點(diǎn)好理解吧
List<MBeanParameterInfo> signature = new ArrayList<MBeanParameterInfo>();
int i = 1;
for (Class<?> paramType : m.getParameterTypes()) {
String paramName = "p" + (i++);
if (getPropertyEditor(source.getClass(), paramName, paramType) == null) {
continue;
}
signature.add(new MBeanParameterInfo(paramName, convertType(null, null, paramType, true).getName(),
paramName));
}
把操作的參數收集一下。
然后將操作注冊到ModelMBeanOperationInfo中
Class<?> returnType = convertType(null, null, m.getReturnType(), false);
operations.add(new ModelMBeanOperationInfo(m.getName(), m.getName(), signature
.toArray(new MBeanParameterInfo[signature.size()]), returnType.getName(),
ModelMBeanOperationInfo.ACTION));
這里我們注意到了在impact這里用了 ModelMBeanOperationInfo.ACTION。那么如果是其它的impack怎么辦?那么就只能請你重寫(xiě)了。。。
最后加上 operations.add(new ModelMBeanOperationInfo("unregisterMBean", "unregisterMBean", new MBeanParameterInfo[0],
void.class.getName(), ModelMBeanOperationInfo.ACTION));
總要有取消注冊的方法吧?不過(guò)我覺(jué)得沒(méi)有必要。。。
最后封裝一下,返回一個(gè)給new ModelMBeanInfoSupport(className, description,
attributes.toArray(new ModelMBeanAttributeInfo[attributes.size()]), constructors,
operations.toArray(new ModelMBeanOperationInfo[operations.size()]), notifications);
3. Notification
Mbean之間的通信是必不可少的,Notification就起到了在Mbean之間溝通橋梁的作用。JMX notification 由四部分組成:
* Notification 這個(gè)相當于一個(gè)信息包,封裝了需要傳遞的信息
* Notification broadcaster 這相當于一個(gè)廣播器,把消息廣播出去
* Notification listerner 這是一個(gè)監聽(tīng)器,用于監聽(tīng)廣播出來(lái)的Notification消息
* Notification filter 這是一個(gè)過(guò)濾器,過(guò)濾掉不需要的Notification消息
Notification broadcaster不需要我們實(shí)現,JMX的內部已經(jīng)有了。Notification filter一般也很少用。
常用接口
NotificationEmitter, 只要實(shí)現此接口,就可以發(fā)出Notification和訂閱Notification. 類(lèi)NotificationBroadcasterSupport則實(shí)現了NotificationEmitter.
NotificationListener, 實(shí)現此接口的可以訂閱JMX的Notification。
Notification, 消息本身。
Notification采用的是觀(guān)察者模式,個(gè)人認為這在正式的應用中采用的機會(huì )不多,因為完全沒(méi)有這樣的必要。但是這里還是需要介紹一下的。
首先MBean需要繼承
NotificationBroadcasterSupport,用來(lái)提供廣播服務(wù)。
在需要通知的方法中(比如值的改變),new一個(gè)Notification,比如AttributeChangeNotification,這個(gè)類(lèi)是javax.management.Notification的子類(lèi),而javax.management.Notification這個(gè)類(lèi)又是java.util.EventObject的子類(lèi),由此可以證實(shí)上邊所說(shuō)的,JMX通知機制使用了
觀(guān)察者設計模式.javax.management.Notification是一個(gè)JMX的通知核心類(lèi),將來(lái)需要擴展或者其他JMX自帶
的消息,均集成自此類(lèi).AttributeChangeNotification根據類(lèi)名可知,是一個(gè)屬性改變的通知,造方法參數如下:Object source, // 事件源,一直傳遞到j(luò )ava.util.EventObject的source
long sequenceNumber, // 通知序號,標識每次通知的計數器
long timeStamp, // 通知發(fā)出的時(shí)間戳
String msg, // 通知發(fā)送的message
String attributeName, // 被修改屬性名
String attributeType, // 被修改屬性類(lèi)型
Object oldValue, // 被修改屬性修改以前的值
Object newValue // 被修改屬性修改以后的值
根據觀(guān)察者模式,由事件與廣播組成,所以這里繼承了
NotificationBroadcasterSupport,來(lái)提供廣播機制,調用NotificationBroadcasterSupportr的sendNotification(notification) 發(fā)送廣播,
廣播會(huì )根據注冊的觀(guān)察者來(lái)對觀(guān)察者進(jìn)行逐一通知.
有廣播,那么肯定有接收者啦。接收者需要繼承NotificationListener接口,然后處理handleNotification(Notification n, Object handback)就可以了。繼承了NotificationBroadcasterSupport是可以注冊L(fǎng)istener的,用addNotificationListener(new HelloListener(), null, null); 就可以了