Spring 2.x jmx 及应用(3)client连接和通知


首先让别的程序连接到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的思想和技巧,希望大家能给点意见。



你可能感兴趣的:(spring,jvm,xml,Access,SOAP)