http://www.quanlei.com/tag/jmx
http://www.blogjava.net/chengang/category/8015.html
[JMX一步步来] 1、JMX的Hello World
一、JMX简介
什么是JMX?在一篇网文中是这样说的:"JMX(Java Management Extensions)是一个为应用程序植入管理功能的框架。JMX是一套标准的代理和服务,实际上,用户可以在任何Java应用程序中使用这些代理和服务实现管理",这句话我现在看着还是不知所云,云里雾里。
我们还是从JMX能给我们提供什么好处入手来理解吧。举一个应用实例:在一个系统中常常会有一些配置信息,比如服务的IP地址,端口号什么的,那么如何来写这些代码呢?
- 程序初哥一般是写死在程序里,到要改变时就去改程序,然后再编译发布;
- 程序熟手则一般把这些信息写在一个配置文件里(JAVA一般都是*.properties文件),到要改变时只要改配置文件,但还是重新启动系统,以便读取配置文件里的新值;
- 程序好手则会写一个段代码,把配置值缓存起来,系统在读值的时候,先看看配置文件有没有更动。如有更改则重读一遍,否则从缓存里读取值
- 程序高手则懂得取物为我所用,用JMX!把配置属性集中在一个类,然后写一个叫MBean的东东,再配置一下就轻松搞定了。而且JMX自动提供了一个WEB页面来给你来改变这些配置信息。
二、准备工作
JMX是一份规范,SUN依据这个规范在JDK(1.3、1.4、5.0)提供了JMX接口。而根据这个接口的实现则有很多种,比如Weblogic的JMX实现、MX4J、JBoss的JMX实现。在SUN自己也实现了一份,不过在JDK1.4之前,这件JMX实现(一些JAR包)是可选的,你得去它的网站上下载。JDK5.0则内嵌了进来,安装JDK5.0就可以开发基于JMX的代码了。
三、HelloWorld实例
/** * @author ChenGang 2005-12-3 */ public class Hello implements HelloMBean { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public void printHello() { System.out.println("Hello World, " + name); } public void printHello(String whoName) { System.out.println("Hello , " + whoName); } }
/** * @author ChenGang 2005-12-3 */ public interface HelloMBean { public String getName(); public void setName(String name); public void printHello(); public void printHello(String whoName); }
import javax.management.MBeanServer; import javax.management.MBeanServerFactory; import javax.management.ObjectName; import com.sun.jdmk.comm.HtmlAdaptorServer;
public class HelloAgent {
public static void main(String[] args) throws Exception { MBeanServer server = MBeanServerFactory.createMBeanServer();
ObjectName helloName = new ObjectName("chengang:name=HelloWorld"); server.registerMBean(new Hello(), helloName);
ObjectName adapterName = new ObjectName("HelloAgent:name=htmladapter,port=8082"); HtmlAdaptorServer adapter = new HtmlAdaptorServer(); server.registerMBean(adapter, adapterName);
adapter.start(); System.out.println("start....."); } }
- 先创建了一个MBeanServer,用来做MBean的容器
- 将Hello这个类注入到MBeanServer中,注入需要创建一个ObjectName类
- 创建一个AdaptorServer,这个类将决定MBean的管理界面,这里用最普通的Html型界面。AdaptorServer其实也是一个MBean。
- chengang:name=HelloWorld的名字是有一定规则的,格式为:“域名:name=MBean名称”,域名和MBean名称都可以任意取。
依照下面红线的步骤操作之后,在控制台(我用Eclipse就是console视图)得到如下输出:
五、总结
在实际系统中我们可以把name变成决定数库链接池的变量,这样我就可以对系统的运行参数进行实现的监控和配置(管理)。而且也可以对一些方法(如printHello)进行远程调用了。
预告:下一篇将对JMX进行一些介绍,借助本篇的HelloWorld实例来灌输一些概念。
作者简介
陈刚,广西桂林人,著作有《Eclipse从入门到精通》
您可以通过其博客了解更多信息和文章:http://www.chenGang.com.cn
[JMX一步步来] 2、JMX简介
一、JMX简介
JMX是一种JAVA的正式规范,它主要目的是让程序且有被管理的功能,那么怎么理解所谓的“被管理”呢?试想你开发了一个软件(如WEB网站),它是在24小时不简断运行的,那么你可能会想要“监控”这个软件的运行情况,比如收到了多少数据,有多少人登录等等。或者你又想“配置”这个软件,比如现在访问人数比较多,你想把数据连接池设置得大一些。
当然,你也许会专门为这些管理来开发软件,但如果你借助JMX,则会发现创建这样的管理程序是如此简单。因为你无需为管理程序来开发界面,已经有通用的JMX管理软件,如MC4J,或者是用一般都附带提供的HTML网页来管理,你要做的仅仅是将自己要被管理和监控类的按照JMX规范修改一下即可。
中间件软件WebLogic的管理页面就是基于JMX开发的,而JBoss则整个系统都基于JMX构架。下面将JMX的一些概念,从JMX规范转帖如下:
二、JMX构架中的各层及相关的组件
- 工具层(Instrumentation Level)
(a) MBeans(标准的,动态的,开放的和模型MBeans)
(b) 通知模型:Notification、NotificationListener等类
(c) MBean元数据类:Attribute、Opreator等类 - 代理层(Agent Level)
(a) MBean Server
(b) 代理服务。如前一篇的HtmlAdaptorServer等。
以下是从网上找到的两个图:
(图1)
(图2)
MBean中有getter和setter的就是属性,如前一篇的Hello类中Name。如果只有getter则表示该属性只读。一共有四种MBean,如下:
- 标准MBeans(Standard MBeans)设计和实现是最简单的,这类MBean使用自己的方法名作为管理接口;
- 动态MBeans(Dynamic MBeans)必须实现一个指定的接口,由于动态MBeans在运行期间暴露它们的管理接口,因此更为灵活;
- 开放MBeans(Open MBeans)属于动态MBeans,这类MBean依靠基础数据类型来实现通用管理,并为友情用户进行自我声明;
- 模型MBeans(Model MBeans)同样也是动态MBeans,这类MBeans是完全可配置的,在运行期间进行自我声明;它们为资源动态工具提供一个一般性的,有默认行为的MBeans类。
[JMX一步步来] 3、Notification的使用
- Notification 这个相当于一个信息包,封装了需要传递的信息
- Notification broadcaster 这相当于一个广播器,把消息广播出去
- Notification listerner 这是一个监听器,用于监听广播出来的Notification消息
- Notification filter 这是一个过滤器,过滤掉不需要的Notification消息
import javax.management.Notification; import javax.management.NotificationBroadcasterSupport;
public class Jack extends NotificationBroadcasterSupport implements JackMBean { private int seq = 0; public void hi() { Notification n = new Notification(//创建一个信息包 "jack.hi",//给这个Notification起个名称 this, //由谁发出的Notification ++seq,//一系列通知中的序列号,可以设任意数值 System.currentTimeMillis(),//发出时间 "Jack");//发出的消息文本 //发出去 sendNotification(n); } }
- 必需继承NotificationBroadcasterSupport
- 此类只有一个hi方法,方法只有两句:创建一个Notification消息包,然后将包发出去
- 如果你还要在消息包上附加其他数据,Notification还有一个setUserData方法可供使用
public interface JackMBean { public void hi(); }
import javax.management.Notification; import javax.management.NotificationListener; public class HelloListener implements NotificationListener { public void handleNotification(Notification n, Object handback) { System.out.println("type=" + n.getType()); System.out.println("source=" + n.getSource()); System.out.println("seq=" + n.getSequenceNumber()); System.out.println("send time=" + n.getTimeStamp()); System.out.println("message=" + n.getMessage());
if (handback != null) { if (handback instanceof Hello) { Hello hello = (Hello) handback; hello.printHello(n.getMessage()); } } } }
import javax.management.MBeanServer;
import javax.management.MBeanServerFactory;
import javax.management.ObjectName;
import com.sun.jdmk.comm.HtmlAdaptorServer;
public class HelloAgent {
public static void main(String args[]) throws Exception{
MBeanServer server = MBeanServerFactory.createMBeanServer();
ObjectName helloName = new ObjectName("chengang:name=HelloWorld");
Hello hello=new Hello();
server.registerMBean(hello, helloName);
ObjectName adapterName = new ObjectName("HelloAgent:name=htmladapter,port=8082");
HtmlAdaptorServer adapter = new HtmlAdaptorServer();
server.registerMBean(adapter, adapterName);
Jack jack = new Jack();
server.registerMBean(jack, new ObjectName("HelloAgent:name=jack"));
jack.addNotificationListener(new HelloListener(), null, hello);
adapter.start();
System.out.println("start.....");
}
}
四、总结
Notification和Java的事件模型是一样的,另外如果你买了《Eclipse从入门到精通》,你会发现第22.4节也使用了和Notification和Java的事件模型相同的设计方式。Notification在我们的实际项目中也用到了,象我们现在的给移动做的项目中(基于JMX实现),分散在各地方的工作站的日志,就是通过Notification方式,把每条产生的日志封装在Notification中实时发回主控服务器的。有机会我会发这一系统的关于日志的设计方案写一下,它实现了对各地工作站的集中的、实时的监控,非常实用。
作者简介
陈刚,广西桂林人,著作有《Eclipse从入门到精通》
您可以通过其博客了解更多信息和文章:http://www.chenGang.com.cn
[JMX一步步来] 4、动态MBean:DynamicMBean
import java.lang.reflect.Constructor; import java.util.Iterator; import javax.management.Attribute; import javax.management.AttributeList; import javax.management.DynamicMBean; import javax.management.MBeanAttributeInfo; import javax.management.MBeanConstructorInfo; import javax.management.MBeanException; import javax.management.MBeanInfo; import javax.management.MBeanNotificationInfo; import javax.management.MBeanOperationInfo; import javax.management.MBeanParameterInfo; import javax.management.ReflectionException;
/** * @author Sunny Peng * @author change by Chen.Gang, add a feature for dynamic add operation * @version 1.0 */ public class HelloDynamic implements DynamicMBean { //这是我们的属性名称 private String name; private MBeanInfo mBeanInfo = null; private String className; private String description; private MBeanAttributeInfo[] attributes; private MBeanConstructorInfo[] constructors; private MBeanOperationInfo[] operations; MBeanNotificationInfo[] mBeanNotificationInfoArray;
public HelloDynamic() { init(); buildDynamicMBean(); }
private void init() { className = this.getClass().getName(); description = "Simple implementation of a dynamic MBean."; attributes = new MBeanAttributeInfo[1]; constructors = new MBeanConstructorInfo[1]; operations = new MBeanOperationInfo[1]; mBeanNotificationInfoArray = new MBeanNotificationInfo[0]; }
private void buildDynamicMBean() { //设定构造函数 Constructor[] thisconstructors = this.getClass().getConstructors(); constructors[0] = new MBeanConstructorInfo("HelloDynamic(): Constructs a HelloDynamic object", thisconstructors[0]); //设定一个属性 attributes[0] = new MBeanAttributeInfo("Name", "java.lang.String", "Name: name string.", true, true, false); //operate method 我们的操作方法是print MBeanParameterInfo[] params = null;//无参数 operations[0] = new MBeanOperationInfo("print", "print(): print the name", params, "void", MBeanOperationInfo.INFO); mBeanInfo = new MBeanInfo(className, description, attributes, constructors, operations, mBeanNotificationInfoArray); }
//动态增加一个print1方法 private void dynamicAddOperation() { init(); operations = new MBeanOperationInfo[2];//设定数组为两个 buildDynamicMBean(); operations[1] = new MBeanOperationInfo("print1", "print1(): print the name", null, "void", MBeanOperationInfo.INFO); mBeanInfo = new MBeanInfo(className, description, attributes, constructors, operations, mBeanNotificationInfoArray); }
public Object getAttribute(String attribute_name) { if (attribute_name != null) return null; if (attribute_name.equals("Name")) return name; return null; }
public void setAttribute(Attribute attribute) { if (attribute == null) return; String Name = attribute.getName(); Object value = attribute.getValue(); try { if (Name.equals("Name")) { // if null value, try and see if the setter returns any exception if (value == null) { name = null; // if non null value, make sure it is assignable to the attribute } else if ((Class.forName("java.lang.String")).isAssignableFrom(value.getClass())) { name = (String) value; } } } catch (Exception e) { e.printStackTrace(); } }
public AttributeList getAttributes(String[] attributeNames) { if (attributeNames == null) return null; AttributeList resultList = new AttributeList(); // if attributeNames is empty, return an empty result list if (attributeNames.length == 0) return resultList; for (int i = 0; i < attributeNames.length; i++) { try { Object value = getAttribute(attributeNames[i]); resultList.add(new Attribute(attributeNames[i], value)); } catch (Exception e) { e.printStackTrace(); } } return resultList; }
public AttributeList setAttributes(AttributeList attributes) { if (attributes == null) return null; AttributeList resultList = new AttributeList(); // if attributeNames is empty, nothing more to do if (attributes.isEmpty()) return resultList; // for each attribute, try to set it and add to the result list if successfull for (Iterator i = attributes.iterator(); i.hasNext();) { Attribute attr = (Attribute) i.next(); try { setAttribute(attr); String name = attr.getName(); Object value = getAttribute(name); resultList.add(new Attribute(name, value)); } catch (Exception e) { e.printStackTrace(); } } return resultList; }
public Object invoke(String operationName, Object params[], String signature[]) throws MBeanException, ReflectionException { // Check for a recognized operation name and call the corresponding operation if (operationName.equals("print")) { //具体实现我们的操作方法print System.out.println("Hello, " + name + ", this is HellDynamic!"); dynamicAddOperation(); return null; } else if (operationName.equals("print1")) { System.out.println("这是动态增加的一方法print1"); return null; } else { // unrecognized operation name: throw new ReflectionException(new NoSuchMethodException(operationName), "Cannot find the operation " + operationName + " in " + className); }
}
public MBeanInfo getMBeanInfo() { return mBeanInfo; } }
- 实现于接口DynamicMBean
- 借助于各种辅助类完成一个类的构造。构造函数类MBeanConstructorInfo、属性类MBeanAttributeInfo、方法类MBeanOperationInfo
- 这里所有public方法是实现于DynamicMBean的。主要提供:setAttribute设置属性、getAttribute取得属性、setAttributes设置一组属性、getAttributes取得一组属性、invoke方法调用、getMBeanInfo MBeanServer由这个方法得到关键的MBean类的构造信息。
import javax.management.MBeanServerFactory; import javax.management.ObjectName; import com.sun.jdmk.comm.HtmlAdaptorServer; public class HelloAgent { public static void main(String[] args) throws Exception { MBeanServer server = MBeanServerFactory.createMBeanServer(); ObjectName helloName = new ObjectName("chengang:name=HelloDynamic"); HelloDynamic hello = new HelloDynamic(); server.registerMBean(hello, helloName); ObjectName adapterName = new ObjectName("HelloAgent:name=htmladapter,port=8082"); HtmlAdaptorServer adapter = new HtmlAdaptorServer(); server.registerMBean(adapter, adapterName); adapter.start(); System.out.println("start....."); } }
[JMX一步步来] 5、用Apache的commons-modeler来辅助开发JMX
package mbean.modelbean;
import mbean.standard.HelloMBean;
public class Hello implements HelloMBean { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public void printHello() { System.out.println("Hello World, " + name); } public void printHello(String whoName) { System.out.println("Hello , " + whoName); } }
<?xml version="1.0"?> <mbeans-descriptors> <mbean name="Hello" description="the hello bean" domain="chengang" group="helloGroup" type="mbean.modelbean.Hello"> <attribute name="name" description="a name attribute" type="java.lang.String" writeable="true"/> <operation name="printHello" description="a operation to print hello" impact="INFO" returnType="String"/> </mbean> </mbeans-descriptors>
- name mbean在xml中的唯一标识,不一定要和类同名
- description mbean的注释说明信息
- domain mbean所属域
- group mbean所属组
- type mbean的类全名(包名+类名)
- classname 指定实现代理功能的ModelMbean的全名,如果不指定则默认为BaseModelMBean
package mbean.modelbean;
import java.io.InputStream; import javax.management.MBeanServer; import javax.management.ObjectName; import javax.management.modelmbean.ModelMBean; import org.apache.commons.modeler.ManagedBean; import org.apache.commons.modeler.Registry; import com.sun.jdmk.comm.HtmlAdaptorServer;
public class HelloAgent { public static void main(String[] args) throws Exception { //基于xml中的信息构建一个Registry Registry registry = Registry.getRegistry(null, null); InputStream stream = HelloAgent.class.getResourceAsStream("Mbeans-descriptors.xml"); registry.loadMetadata(stream); stream.close(); //由Registry得到一个MBeanServer MBeanServer server = registry.getMBeanServer();
//得到Hello在描述文件中的信息类,对应于xml文件<mbean>标签的name属性。 ManagedBean managed = registry.findManagedBean("Hello"); //创建ObjectName ObjectName helloName = new ObjectName(managed.getDomain() + ":name=HelloWorld"); //得到ModelMBean ModelMBean hello = managed.createMBean(new Hello()); //注册MBean server.registerMBean(hello, helloName);
ObjectName adapterName = new ObjectName("HelloAgent:name=htmladapter,port=8082"); HtmlAdaptorServer adapter = new HtmlAdaptorServer(); server.registerMBean(adapter, adapterName); adapter.start(); System.out.println("start....."); } }
Registry registry = Registry.getRegistry(null, null); InputStream stream = HelloAgent.class.getResourceAsStream("Mbeans-descriptors.xml"); registry.loadMetadata(stream); stream.close();
ObjectName helloName = new ObjectName(managed.getDomain() + ":name=HelloWorld");
以前:ObjectName helloName = new ObjectName("chengang:name=HelloWorld")
由图可见commons Modeler为Hello动态生成了一个MBean接口:BaseModelBean
[JMX一步步来] 6、模型Bean:Model Bean
public class Hello{ private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public void printHello() { System.out.println("Hello World, " + name); } public void printHello(String whoName) { System.out.println("Hello , " + whoName); } }
import javax.management.MBeanServer; import javax.management.MBeanServerFactory; import javax.management.ObjectName; import javax.management.modelmbean.RequiredModelMBean;
import com.sun.jdmk.comm.HtmlAdaptorServer;
public class HelloAgent { public static void main(String[] args) throws Exception { MBeanServer server = MBeanServerFactory.createMBeanServer(); ObjectName helloName = new ObjectName("chengang:name=HelloWorld"); //Hello hello = new Hello(); RequiredModelMBean hello = ModelMBeanUtils.createModlerMBean(); server.registerMBean(hello, helloName); ObjectName adapterName = new ObjectName("HelloAgent:name=htmladapter,port=8082"); HtmlAdaptorServer adapter = new HtmlAdaptorServer(); server.registerMBean(adapter, adapterName); adapter.start(); System.out.println("start....."); } }
import javax.management.modelmbean.ModelMBeanAttributeInfo;
import javax.management.modelmbean.ModelMBeanInfo;
import javax.management.modelmbean.ModelMBeanInfoSupport;
import javax.management.modelmbean.ModelMBeanOperationInfo;
import javax.management.modelmbean.RequiredModelMBean;
private static final boolean READABLE = true;
private static final boolean WRITABLE = true;
private static final boolean BOOLEAN = true;
private static final String STRING_CLASS = "java.lang.String";
RequiredModelMBean model = null;
try {
model = new RequiredModelMBean();
model.setManagedResource(new Hello(), "ObjectReference");
ModelMBeanInfo info = createModelMBeanInfo();
model.setModelMBeanInfo(info);
} catch (Exception e) {
e.printStackTrace();
}
return model;
}
//
// 属性 //
//
// 构造name属性信息
ModelMBeanAttributeInfo nameAttrInfo = new ModelMBeanAttributeInfo(//
"Name", // 属性名
STRING_CLASS, //属性类型
"people name", // 描述文字
READABLE, WRITABLE, !BOOLEAN, // 读写
null // 属性描述子
);
// 方法 //
//
//构造 printHello()操作的信息
ModelMBeanOperationInfo print1Info = new ModelMBeanOperationInfo(//
"printHello", //
null, //
null, //
"void", //
MBeanOperationInfo.INFO, //
null //
);
ModelMBeanOperationInfo print2Info;
MBeanParameterInfo[] param2 = new MBeanParameterInfo[1];
param2[0] = new MBeanParameterInfo("whoName", STRING_CLASS, "say hello to who");
print2Info = new ModelMBeanOperationInfo(//
"printHello", //
null,//
param2,//
"void", //
MBeanOperationInfo.INFO, //
null//
);
// 最后总合 //
//
// create ModelMBeanInfo
ModelMBeanInfo mbeanInfo = new ModelMBeanInfoSupport(//
RequiredModelMBean.class.getName(), // MBean类
null, // 描述文字
new ModelMBeanAttributeInfo[] { // 所有的属性信息(数组)
nameAttrInfo },//只有一个属性
null, // 所有的构造函数信息
new ModelMBeanOperationInfo[] { // 所有的操作信息(数组)
print1Info,
print2Info },//
null, // 所有的通知信息(本例无)
null//MBean描述子
);
return mbeanInfo;
}
}
细心的人会发现动态MBean和这一节的模型Mbean非常相似,但它们还是有很大不同的:动态MBean没有Hello类,它要自己实现Hello类中的方法逻辑。
[JMX一步步来] 7、用JDK5.0的JConsole来连接MBean
import javax.management.MBeanServer; import javax.management.MBeanServerFactory; import javax.management.ObjectName; import javax.management.remote.JMXConnectorServer; import javax.management.remote.JMXConnectorServerFactory; import javax.management.remote.JMXServiceURL;
import com.sun.jdmk.comm.HtmlAdaptorServer;
public class HelloAgent {
public static void main(String args[]) throws Exception {
MBeanServer server = MBeanServerFactory.createMBeanServer();
ObjectName helloName = new ObjectName("chengang:name=HelloWorld");
Hello hello = new Hello();
server.registerMBean(hello, helloName);
ObjectName adapterName = new ObjectName("HelloAgent:name=htmladapter,port=8082");
HtmlAdaptorServer adapter = new HtmlAdaptorServer();
server.registerMBean(adapter, adapterName);
adapter.start();
System.out.println("start.....");
// Create an RMI connector and start it
JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:9999/server");
JMXConnectorServer cs = JMXConnectorServerFactory.newJMXConnectorServer(url, null, server);
cs.start();
System.out.println("rmi start.....");
}
}
[JMX一步步来] 8、编写程序来连接MBean
import java.util.Iterator; import java.util.Set;
import javax.management.Attribute; import javax.management.MBeanInfo; import javax.management.MBeanServerConnection; import javax.management.MBeanServerInvocationHandler; import javax.management.ObjectInstance; import javax.management.ObjectName; import javax.management.remote.JMXConnector; import javax.management.remote.JMXConnectorFactory; import javax.management.remote.JMXServiceURL;
public class Client {
public static void main(String[] args) throws Exception { JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:9999/server"); JMXConnector jmxc = JMXConnectorFactory.connect(url, null); MBeanServerConnection mbsc = jmxc.getMBeanServerConnection(); ObjectName mbeanName = new ObjectName("chengang:name=HelloWorld");
//把所有Domain都打印出来 System.out.println("Domains:---------------"); String domains[] = mbsc.getDomains(); for (int i = 0; i < domains.length; i++) { System.out.println("\tDomain[" + i + "] = " + domains[i]); }
//MBean的总数 System.out.println("MBean count = " + mbsc.getMBeanCount());
//对name属性的操作(属性名的第一个字母要大写) mbsc.setAttribute(mbeanName, new Attribute("Name", "Chen.Gang"));//设值 System.out.println("Name = " + mbsc.getAttribute(mbeanName, "Name"));//取值
//得到proxy代理后直接调用的方式 HelloMBean proxy = (HelloMBean) MBeanServerInvocationHandler.newProxyInstance(mbsc, mbeanName, HelloMBean.class, false); proxy.printHello(); proxy.printHello("陈刚");
//远程调用的方式 mbsc.invoke(mbeanName, "printHello", null, null); mbsc.invoke(mbeanName, "printHello", new Object[] { "子在川上曰" }, new String[] { String.class.getName() });
//得mbean的信息 MBeanInfo info = mbsc.getMBeanInfo(mbeanName); System.out.println("Hello Class: " + info.getClassName()); System.out.println("Hello Attriber:" + info.getAttributes()[0].getName()); System.out.println("Hello Operation:" + info.getOperations()[0].getName());
//得到所有的MBean的ObjectName System.out.println("all ObjectName:---------------"); Set set = mbsc.queryMBeans(null, null); for (Iterator it = set.iterator(); it.hasNext();) { ObjectInstance oi = (ObjectInstance) it.next(); System.out.println("\t" + oi.getObjectName()); }
//注销 //mbsc.unregisterMBean(mbeanName); //关闭MBeanServer连接 jmxc.close(); } }
Domain[0] = HelloAgent
Domain[1] = JMImplementation
Domain[2] = chengang
MBean count = 3
Name = Chen.Gang
Hello Class: mbean.connector.Hello
Hello Attriber:Name
Hello Operation:printHello
all ObjectName:---------------
chengang:name=HelloWorld
JMImplementation:type=MBeanServerDelegate
HelloAgent:name=htmladapter,port=8082
Hello , 陈刚
Hello World, Chen.Gang
Hello , 子在川上曰
[JMX一步步来] 9、基于JBoss来写MBean
一、 HelloWorld实例
1、准备工作
JBOSS实现了JMX规范,这个实例是基于JBOSS来实现的。请先去下载一个JBOSS,我是jboss-3.2.6,下载地址:http://www.jboss.com/downloads/index#as。这个实例需要JBOSS的两个JAR包的支持:jboss-system-3.2.6.jar、jboss-jmx-3.2.6.jar,如果你和我一样用Eclipse来开发(推荐),那么把这个两个包加入到项目的库引用中(加入到库引用的方法参考前面两章)。
2、程序代码
假设我们有一个叫message的属性要需要经常进行改动配置的,那么我们就把它写成一个MBean。
1、HelloWorldServiceMBean接口
在写MBean之前,我们先需要写一个MBean接口,接口里的方法都是属性的set/get方法。这个接口必须继承接口ServiceMBean。
import org.jboss.system.ServiceMBean;
public interface HelloWorldServiceMBean extends ServiceMBean {
String getMessage();
void setMessage(String message);
}
2、HelloWorldService实现类
然后写出HelloWorldServiceMBean接口的实现类HelloWorldService,这个实现类还必须继承ServiceMBeanSupport类。这种类再简单不过了,就是属性和相应的set/get方法,EJB中叫实体类、Hibernate中叫POJO。
import org.jboss.system.ServiceMBeanSupport;
public class HelloWorldService extends ServiceMBeanSupport implements HelloWorldServiceMBean {
private String message;
public String getMessage() {
System.out.println("getMessage()=" + message);
return message;
}
public void setMessage(String message) {
System.out.println("setMessage(" + message + ")");
this.message = message;
}
}
3、配置文件jboss-service.xml
<?xml version="1.0" encoding="UTF-8"?>
<server>
<mbean code="example.mbean.HelloWorldService" name="www.chengang.com.cn:service=HelloWorld">
<attribute name="Message">Hello World</attribute>
</mbean>
</server>
说明:
l code项指向MBean的实现类HelloWorldService
l name项是一个名称,格式一般是:[说明性文字]:service=[类名]
l attribute是为属性设置初始值,这样当JBOSS一加载HelloWorldService类时,message属性就有了一个初始值Hello World。注意Message的第一个字母必须是大写。
二、将实例部署到JBOSS
在jboss-3.2.6\server\default\deploy目录下创建一个hello.sar目录,然后创建如下目录文件结构:
hello.sar
|----example
| |----mbean
| |----HelloWorldService.class (注意:是*.class,不是*.java)
| |----HelloWorldServiceMBean.class
|----META-INF
|----jboss-service.xml
其他说明:
l 也可以将hello.sar目录用zip格式压缩成一个同名的hello.sar文件,放到jboss-3.2.6\server\default\deploy目录下。
l JBOSS支持热部署,也就是说你在布置这个目录时并不需要重启JBOSS。
三、MBean的效果 打开网址:http://127.0.0.1:8080/jmx-console/ ,出现下图
然后单击“service=HelloWorld”项打开详细页面如下:
将“HelloWorld”改成“Hello World,ChenGang”,再单击“Apply Changes”应用修改,得到如下效果:
四、其他类如何使用Messag属性
现在我们可以通过一个自动提供的WEB页面来设置Message属性了,接下来的问题是:“在其他类中应该如何得到Message的属性值”。MBean在JBoss是只保留一个MBean的实例(单例模式?),也就是说问题转成我们如何去取得这个唯一实例。例程如下:
1、创建一个使用到Message属性的类
import org.jboss.mx.util.ObjectNameFactory;
public class Client {
public void go() {
HelloWorldServiceMBean mbean = (HelloWorldServiceMBean) MBeanProxyExt.create(HelloWorldServiceMBean.class, ObjectNameFactory.create(www.chengang.com.cn:service=HelloWorld));
String msg = mbean.getMessage();
System.out.println("Client.go()=" + msg);
}
}
注意:go方法里是三句。第一句比较长,它是根据jboss-service.xml文件中设置的MBean名称,来取得此MBean在JBOSS中的实例。
2、在Mbean中加一个相应的调用Client.go的方法
在HelloWorldServiceMBean接口中加入一句:
void callGo();
在HelloWorldService类中加入现实方法:
public void callGo() {
new Client().go();
}
3、更新布署
将三个类的class文件:Clien.class、HelloWorldServiceMBean.class、HelloWorldService.class,更新到JBOSS的hello.sar\example\mbean目录下。然后重启JBOSS。
4、查看效果
打开JBOSS提供的MBean设置页面,如下,发现多了一个callGo。
单击callGo项后的invoke按钮,得到如下的DOS输出:
五、其他说明
l 本实例仅演示了一个Message属性,你当然可以在HelloWorldService中加入更多属性,别忘了在HelloWorldServiceMBean接口也加入相应的set/get方法。
l 本实例的message属性是String类型的,但JMX也支持其他的类型,甚至是XML信息。对于jboss-service.xml中的XML信息,这时属性类型要求是org.w3c.dom.Element,JMX将它封装成了一个XML的DOM对象。
l 回顾一下,JMX也是将配置信息写在了一个文件(jboss-service.xml文件)里嘛,相对于将配置文件写到*.properties文件的方式,它似乎也没什么新鲜的地方。但通过本章实例我们可以看到JMX的一些好处:我们不用写代码去读配置文件的信息,而且JMX支持的属性类型是多种多样的(如上面说的org.w3c.dom.Element)。更重要的是JMX还提供了一整套的属性之前互相访问、互相调用的功能,这个HelloWorld实例所反映的只是冰山一角而已。
六、参考资料:http://www.huihoo.com/java/jmx/jmx_base.html