首先让别的程序连接到mbeanServer必须有个serverConnectior
只需在mbeanServer的配置文件中定义一个
<bean id="serverConnector" class="org.springframework.jmx.support.ConnectorServerFactoryBean"/>
即可完成对连接的监听
这样设定是使用默认的及jmxmp协议service:jmx:jmxmp://localhost:9875
你也可以通过使用其他jmx支持的协议RMI,IIOP, Burlap,Hessian,SOAP等,
只要设定objectName和serviceUrl及可
为了方便我们常用的还是jmxmp.
server配置好到client了。
client同样需要一个连接器
<bean id="clientConnector"
class="org.springframework.jmx.support.MBeanServerConnectionFactoryBean">
<property name="serviceUrl"
value="service:jmx:jmxmp://localhost:9875" />
</bean>
简单直观的配置没什么可以说的。
操作mbean.
无论你使用那种方式注册和描述mbean,访问和操作mbean的方式只有一种。就是通过interface创建的代理来访问。
<bean id="proxy"
class="org.springframework.jmx.access.MBeanProxyFactoryBean">
<property name="objectName" value="mbean:name=testBean" />
<property name="proxyInterface"
value="com.xmlasia.spring.test.jmx.ICommentMBeanManager" />
<property name="server" ref="clientConnector" />
</bean>
同样简单直观,这里注意proxyInterface不是数组类型。你只能设定一个interface,无论server是不是用interface暴露的,只要client的interface于mbeanServer中的匹配就能正确访问
。
如果你不是使用interface暴露的mbean的方法,当你调用不匹配的method时会有
org.springframework.jmx.access.InvalidInvocationException: Operation 'publicMessage' is not exposed on the management interface从客户段抛出。所以在实际应用时最
好能使用interface配置mbean.
测试
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("/jmxClientContext.xml");
ICommentMBeanManager o = (ICommentMBeanManager)context.getBean("proxy");
o.pause("testid");
o.shutDown();
while(true){
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
同样client的连接是不阻塞主线程的,同样需要保持主线程的活着。
调用上述代码就能看到server有调用mbean的方法。
通知
jmx有一种机制和向容器通知属性改变,首先我们需要了解NotificationListener
package com.xmlasia.spring.test.jmx;
import javax.management.Notification;
import javax.management.NotificationListener;
public class TestNotificationListener implements NotificationListener {
@Override
public void handleNotification(Notification notification, Object obj) {
System.out.println(notification);
}
}
实现一个NotificationListener,可以看到NotificationListener是标准的jmx接口
当属性值改变时handleNotification将会调用。
使用MBeanExporter的notificationListenerMappings属性将listener和mbean进行mapping
<bean id="mBeanExporter"
class="org.springframework.jmx.export.MBeanExporter">
<property name="server" ref="mbeanServer" />
<property name="assembler" ref="assembler" />
<property name="beans">
<!-- 将mbean注册到mBeanExporter -->
<map>
<entry key="mbean:name=testBean"
value-ref="mbeanManager" />
</map>
</property>
<property name="registrationBehaviorName"
value="REGISTRATION_REPLACE_EXISTING" />
<property name="notificationListenerMappings">
<map>
<!--将所有listener mapping到所有mbean-->
<entry key="*">
<bean class="com.xmlasia.spring.test.jmx.TestNotificationListener" />
</entry>
</map>
</property>
</bean>
key是同配符,也可以指定到某一个mbean
当我们在client调用.setClientStatus是handleNotification收到的
notification = javax.management.AttributeChangeNotification[source=mbean:name=testBean][type=jmx.attribute.change][message=AttributeChangeDetected]
一般情况下这样的通知没有意义,我们要将他filter掉。
package com.xmlasia.spring.test.jmx;
import javax.management.Notification;
import javax.management.NotificationFilter;
import javax.management.NotificationListener;
public class TestNotificationListener implements NotificationListener,
NotificationFilter {
@Override
public void handleNotification(Notification notification, Object obj) {
System.out.println(notification);
}
@Override
public boolean isNotificationEnabled(Notification notification) {
if (notification instanceof javax.management.AttributeChangeNotification)
return false;
else
return true;
}
}
同样实现标准的jmx接口NotificationFilter
我们通过判断他的类型进行过滤。
改变配置文件。不使用notificationListenerMappings属性,改用notificationListeners
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
<bean id="mbeanManager"
class="com.xmlasia.spring.test.jmx.MBeanManager" />
<bean id="mbeanServer"
class="org.springframework.jmx.support.MBeanServerFactoryBean">
</bean>
<bean id="assembler"
class="org.springframework.jmx.export.assembler.MetadataMBeanInfoAssembler">
<property name="attributeSource" ref="jmxAttributeSource" />
</bean>
<bean id="jmxAttributeSource"
class="org.springframework.jmx.export.annotation.AnnotationJmxAttributeSource" />
<bean id="mBeanExporter"
class="org.springframework.jmx.export.MBeanExporter">
<property name="server" ref="mbeanServer" />
<property name="assembler" ref="assembler" />
<property name="beans">
<map>
<entry key="mbean:name=testBean"
value-ref="mbeanManager" />
</map>
</property>
<property name="notificationListeners">
<list>
<bean
class="org.springframework.jmx.export.NotificationListenerBean">
<constructor-arg ref="testNotificationListener" />
<property name="mappedObjectNames">
<list>
<value>mbean:name=testBean</value>
</list>
</property>
<property name="notificationFilter"
ref="testNotificationListener" />
</bean>
</list>
</property>
</bean>
<bean id="testNotificationListener" class="com.xmlasia.spring.test.jmx.TestNotificationListener"/>
<bean id="serverConnector"
class="org.springframework.jmx.support.ConnectorServerFactoryBean" />
</beans>
完整的配置
我们注入一个spring为我们封装好的NotificationListenerBean,可以将listener,filter,还有objectname方便组织在一起。
这里要特别说明下mappedObjectNames是String[]类的,spring reference中的例子是错误的。
下面我们要自己发布notification
package com.xmlasia.spring.test.jmx;
import javax.management.Notification;
import org.springframework.jmx.export.annotation.ManagedAttribute;
import org.springframework.jmx.export.annotation.ManagedOperation;
import org.springframework.jmx.export.annotation.ManagedOperationParameter;
import org.springframework.jmx.export.annotation.ManagedOperationParameters;
import org.springframework.jmx.export.annotation.ManagedResource;
import org.springframework.jmx.export.notification.NotificationPublisher;
import org.springframework.jmx.export.notification.NotificationPublisherAware;
@ManagedResource
public class MBeanManager implements NotificationPublisherAware {
private int clientStatus;
private NotificationPublisher publisher;
@ManagedOperation(description = "pause a single proccess")
@ManagedOperationParameters( { @ManagedOperationParameter(name = "Name of proccess instance", description = "Mandatory") })
public void pause(String n) {
System.out.println("pause proccess id :" + n);
}
@ManagedOperation(description = "shut down the proccess")
public void shutDown() {
System.out.println("shutting down...");
}
public void publicMessage() {
System.out.println("public Message to monitor server");
}
@ManagedAttribute(description = "client status")
public int getClientStatus() {
return clientStatus;
}
@ManagedAttribute(description = "client status")
public void setClientStatus(int clientStatus) {
this.clientStatus = clientStatus;
publisher.sendNotification(new Notification("set",this,0,"client Status changed"));
}
@Override
public void setNotificationPublisher(
NotificationPublisher notificationPublisher) {
publisher = notificationPublisher;
}
}
我们只需要implements NotificationPublisherAware 的setNotificationPublisher方法就可以方便的使用
自己的publisher,提供有用的信息到listener中。
Spring会在exporter创建mbean时帮我们传入NotificationPublisher。
Spring对jmx的封装已经看完了。从中可以学到很多Spring的思想和技巧,希望大家能给点意见。