1 Introduction
Java Management Extension (JMX) API定义于JSR
3,用于应用程序管理。这些API对于被管理的应用程序来说是本地的。也就是说,在JSR
160发布之前,如果客户端要通过JMX来管理和监控远程的应用程序,并没有标准的做法。JSR 160扩展了JSR
3,提供了标准的API用于连接到支持JMX的远程应用程序。JSR 255将会把JMX升级到2.0,并可能体现在Java 7中。
目前JSR 160定义了基于RMI(支持RMI/JRMP 和 RMI/IIOP)的连接器,还定义了可选的JMXMP连接器,它基于TCP Socket和Java序列化机制。
2 JMX Remoting API
2.1 JMXServiceURL
JMXServiceURL用于标识JMXConnectorServer,它是采用以下形式的字符串:
service:jmx:<protocol>://[[[ <host>]: <port>]/ <path>]
"protocol" 指定了协议,例如:rmi、iiop、jmxmp 或者 soap。"host"、"port" 和
"path"是可选的。JMXServiceURL并不足以描述所有的用于连接到JMXConnectorServer的配置信息(例如配置
RMIClientSocketFactory 和RMIServerSocketFactory),因此在构造JMXConnectorServer 和
JMXConnector的时候,还可能需要通过一个Map实例指定其它属性。
2.2 JMXConnectorServer
JMXConnectorServer
是MBeanServer端的组件,它需要被关联到MBeanServer:可以通过在创建JMXConnectorServer的时候显式指定
MBeanServer;也可以把JMXConnectorServer(本身是一个MBean)注册到MBeanServer中。
在被关联到MBeanServer之后,需要启动JMXConnectorServer以处理客户端的请求。同样,如果希望停止处理客户端的请求,那么需
要停止JMXConnectorServer。JMXConnectorServer被停止之后,不应该试图重新启动它,而是应该将其丢弃。
最好通过JMXConnectorServerFactory来创建JMXConnectorServer,例如:
-
-
JMXServiceURL address = new JMXServiceURL("service:jmx:rmi://host");
-
-
-
Map environment = null;
-
-
-
MBeanServer server = MBeanServerFactory.createMBeanServer();
-
-
-
JMXConnectorServer cntorServer = JMXConnectorServerFactory.newJMXConnectorServer(address, environment, server);
-
-
-
cntorServer.start();
在以上的例子中,创建JMXConnectorServer的时候指定了MBeanServer。以下是一个将JMXConnectorServer作为MBean注册到MBeanServer的例子:
-
-
JMXServiceURL address = new JMXServiceURL("service:jmx:rmi://host");
-
-
-
Map environment = null;
-
-
JMXConnectorServer cntorServer = JMXConnectorServerFactory.newJMXConnectorServer(address, environment, null);
-
-
-
MBeanServer server = MBeanServerFactory.createMBeanServer();
-
-
-
ObjectName cntorServerName = ObjectName.getInstance("connectors:protocol=rmi");
-
server.registerMBean(cntorServer, cntorServerName);
-
-
-
cntorServer.start();
-
-
-
server.invoke(cntorServerName, "start", null, null);
-
-
-
Object proxy = MBeanServerInvocationHandler.newProxyInstance(server, cntorServerName, JMXConnectorServerMBean.class, true);
-
JMXConnectorServerMBean cntorServerMBean = (JMXConnectorServerMBean)proxy;
-
cntorServerMBean.start();
需要注意的是,在这个例子中使用了三种不同的方式启动JMXConnectorServer:第一种方式是直接调用cntorServer对象的
start方法;第二种方式是通过MBeanServer调用MBean的方法;第三种方式是使用了
MBeanServerInvocationHandler,它继承自InvocationHandler,用于从MBean的管理接口向
MBeanServer中的 MBean
转发方法调用。当调用从newProxyInstance()方法返回的代理对象上的toString()、hashCode()
和equals(Object)等方法时,这些方法调用同样也会被转发到MBeanServer中的MBean ,因此当且仅当 MBean
在其管理接口中声明了这些方法时才能如此操作,否则会抛出异常。
2.3 JMXConnector
JMXConnector是客户端的组件,客户端程序通过它连接到远程的MBeanServer。客户端可以通过JMXConnector获得远程
MBeanServer的MBeanServerConnection
接口,并以类似本地的方式使用它。JMXConnector还支持在远程的MBeanServer上注册本地或者远程的通知监听器,以接收来自远程
MBeanServer的通知。
最好通过JMXConnectorFactory来创建JMXConnector,例如:
-
-
JMXServiceURL address = ...;
-
-
-
Map environment = null;
-
-
-
JMXConnector cntor = JMXConnectorFactory.connect(address, environment);
-
-
-
MBeanServerConnection mbsc = cntor.getMBeanServerConnection();
-
-
-
String domain = mbsc.getDefaultDomain();
-
也可以先实例化JMXConnector,然后在连接到MBeanServer,例如:
-
-
JMXServiceURL address = ...;
-
-
-
Map creationEnvironment = null;
-
-
-
JMXConnector cntor = JMXConnectorFactory.newJMXConnector(address, creationEnvironment);
-
-
-
-
Map connectionEnvironment = null;
-
-
-
cntor.connect(connectionEnvironment);
-
-
-
MBeanServerConnection mbsc = cntor.getMBeanServerConnection();
-
-
-
String domain = mbsc.getDefaultDomain();
需要注意的是,以上的例子中采用了两个不同的Map对象保存相关的属性:一个用于实例化;另外一个用于连接。MBeanServerConnection
接口在JMX 1.2中定义,用于支持remote
API。由于之前版本的MBeanServer接口不能直接被客户端使用(例如其registerMBean(), deserialize(),
getClassLoader()等方法对于客户端来说没有意义),因此在JMX
1.2中,MBeanServer接口继承自MBeanServerConnection接口。
RMIConnector是标准的JMXConnector。由于它使用RMI协议,因此需要一个stub
object来处理RMI调用的细节。stub class通常存在于客户端的类路径上,但是stub
object通常是从命名服务器处下载,因此RMI客户端需要知道stub object被绑定到RMI server上的路径。JSR
160定义了两种获得stub object的方式:
第一种方式不需要命名服务器,而是将stub
object编码为JMXServiceURL的一部分,即"encoded form"。如果采用RMI/JRMP协议,那么对stub
object被序列化之后的所有字节进行Base64编码,然后将编码后的字符添加到JMXServiceURL中,并且以/stub/开始;如果采用
RMI/ IIOP,那么IOR被添加到JMXServiceURL中,并以/ior/开始。以下是个例子:
-
-
service:jmx:rmi:
-
-
-
service:jmx:iiop:
第二种方式是在JMXServiceURL中指定命名服务器和stub object被绑定到的JNDI路径,即"JNDI
form"。在JMXServiceURL中以/jndi/开始。将JNDI的相关配置传递给RMIConnector的方式有多种。例如URL
'rmi://namingHost:1099/jndiPath'指明命名服务器在名为'namingHost'的主机上并监听1099端口,命名服务
器是rmiregistry,路径是'/jndiPath'。同样URL
'iiop://namingHost:900/jndiPath'指明命名服务器在名为'namingHost'的主机上并监听900端口,命名服务器
是COS命名服务,路径是'/jndiPath'。以下是个例子:
-
-
service:jmx:rmi:
-
-
-
service:jmx:iiop:
此外,JNDI属性也可以通过系统属性、类路径上的'jndi.properties'文件、在调用JMXConnector.connect(Map
environment)或者JMXConnectorFactory.connect(JMXServiceURL url, Map
environment)方法时传递的environment参数等不同方式指定。如果JNDI属性通过这些方式指定,那么JMXServiceURL中
可以使用简短的JNDI格式,即只指定JNDI path。以下是个例子:
-
-
service:jmx:rmi:
-
-
-
service:jmx:iiop:
-
-
-
-
Map environment = new HashMap();
-
environment.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.rmi.registry.RegistryContextFactory");
-
environment.put(Context.PROVIDER_URL, "rmi://namingHost:1099");
-
-
JMXServiceURL url = new JMXServiceURL("service:jmx:rmi://rmiHost/jndi/jndiPath");
-
-
JMXConnector connector = JMXConnectorFactory.connect(url, environment);
2.4 Remote Notification
JSR
160定义的连接器可以接收到远程MBean发送的通知。通知被传递的细节依赖于连接器使用的协议。要接收通知,客户端必须要通过调用
MBeanServerConnection.addNotificationListener(...)方法来注册一个监听器,这个方法有两个重载的版
本:
第一个版本接受一个ObjectName实例作为监听器,由于监听器是远程MBeanServer中的一个MBean,因此方法中的filter和handback参数必须被传递到远程的MBeanServer,因此它们必须是可以被序列化的。
另一个版本接受NotificationListener的实例作为监听器。这个监听器是客户端的本地对象,而且监听器对象不会被传递到远程的
MBeanServer,因此它也不必是可以被序列化的。NotificationFilter是否被传递到远程的MBeanServer取决于
connector使用的协议,handback对象不会被传递到远程MBeanServer。在另一方面,远程的MBean发送的通知必须是可以被序列
化的,因为通知会被传递到客户端。以下是在远程的MBean上注册监听器的例子:
-
-
JMXServiceURL address = ...;
-
-
-
JMXConnector connector = JMXConnectorFactory.connect(address);
-
-
-
MBeanServerConnection mbsc = connector.getMBeanServerConnection();
-
-
-
ObjectName delegateName = ObjectName.getInstance("JMImplementation:type=MBeanServerDelegate");
-
-
NotificationListener listener = new NotificationListener()
-
{
-
public void handleNotification(Notification notification, Object handback)
-
{
-
-
}
-
};
-
-
mbsc.addNotificationListener(delegateName, listener, null, null);
3 JMX Remoting Security
JSR
160提供了一种可插拔的认证机制,它基于JMXAuthenticator接口。JMXAuthenticator接口中只包含如下一个方法,它接受从
客户端得到的身份证明信息作为参数,返回javax.security.auth.Subject对象的一个实例。
-
public
Subject authenticate(Object credentials)
throws
SecurityException
以下是在JMXConnectorServer 中使用JMXAuthenticator的例子:
-
-
JMXServiceURL address = ...;
-
-
-
JMXAuthenticator authenticator = ...;
-
-
-
Map environment = new HashMap();
-
environment.put(JMXConnectorServer.AUTHENTICATOR, authenticator);
-
-
-
MBeanServer server = MBeanServerFactory.createMBeanServer();
-
-
-
JMXConnectorServer cntorServer = JMXConnectorServerFactory.newJMXConnectorServer(address, environment, server);
-
-
-
cntorServer.start();
以下是在JMXConnector中使用JMXAuthenticator的例子:
-
-
JMXServiceURL address = ...;
-
-
-
JMXConnector cntor = JMXConnectorFactory.newJMXConnector(address, null);
-
-
-
Object credentials = ...;
-
-
-
Map environment = new HashMap();
-
environment.put(JMXConnector.CREDENTIALS, credentials);
-
-
-
try
-
{
-
cntor.connect(environment);
-
}
-
catch
(SecurityException x)
-
{
-
-
throw x;
-
}
-
-
-
MBeanServerConnection mbsc = cntor.getMBeanServerConnection();
-
-
-
ObjectName delegate = ObjectName.getInstance("JMImplementation:type=MBeanServerDelegate");
-
String id = mbsc.getAttribute(delegate, "MBeanServerId");
当调用从JMXConnector 得到的MBeanServerConnection
接口上的方法时,最终发生在远程MBeanServer的调用会以认证后的Subject
身份执行。如果使用SecurityManager,那么还可以为不同的Subject指定不同的权限。