Using JSR 160 is very simple; the API is standard, so it does not matter if you use MX4J's JSR 160
implementation or Sun's JSR 160 reference implementation.
You can checkout the JSR 160 examples shipped with MX4J to understand how to use the JSR 160 API.
JSR 160 connector servers are identified by a JMXServiceURL, represented by the class
javax.management.remote.JMXServiceURL.
A JMXServiceURL is a string of the form:
service:jmx: <protocol>://[[[ <host>]: <port>]/ <path>]
where protocol is a short string that represent the protocol such as "rmi", "iiop", "jmxmp" or "soap", while host, port and path are optional.
A JMXServiceURL can be seen as the "address" of a JMXConnectorServer, and it is the mean by which a JMXConnector can connect to a JMXConnectorServer.
However, a JMXServiceURL is not sufficient to express the many possibile configurations of a
JMXConnectorServer (for example, it would be difficult to use a JMXServiceURL to specify - for the
RMIConnectorServer - the RMIClientSocketFactory and the RMIServerSocketFactory).
For this reason JMXConnectorServers and JMXConnector make use of
java.util.Maps to
specify environment properties that a JMXConnectorServer or a JMXConnector may use to setup properly.
A JMXConnectorServer is attached to an MBeanServer.
This can be achieved by explicitely passing the MBeanServer to the JMXConnectorServer at the moment of
creation, or by registering the JMXConnectorServer - an MBean itself - inside the target MBeanServer.
Once a JMXConnectorServer is attached to an MBeanServer, it is not yet ready to accept incoming calls
from clients: it must be
started.
After a JMXConnectorServer has been started successfully, it is ready to accept incoming calls from clients.
Symmetrically, a JMXConnectorServer must be
stopped in order to stop accepting incoming
calls from clients. After a JMXConnectorServer has been stopped, it cannot be restarted, and should be
tossed away.
The preferred way to create a JMXConnectorServer is by using the javax.management.remote.JMXConnectorServerFactory class:
Example 3.1. Creating and starting a standalone JMXConnectorServer
// The address of the connector server JMXServiceURL address = new JMXServiceURL("service:jmx:rmi://host"); // The environment map, null in this case Map environment = null; // The MBeanServer to which the JMXConnectorServer will be attached to MBeanServer server = MBeanServerFactory.createMBeanServer(); // Create the JMXCconnectorServer JMXConnectorServer cntorServer = JMXConnectorServerFactory.newJMXConnectorServer(address, environment, server); // Start the JMXConnectorServer cntorServer.start();
The example above creates a JMXConnectorServer attached to a freshly created MBeanServer.
The JMXConnectorServer - itself an MBean - is however not registered in the MBeanServer.
The following code creates a JMXConnectorServer and registers it in a MBeanServer.
Example 3.2. Creating and starting an MBean JMXConnectorServer
// The address of the connector JMXServiceURL address = new JMXServiceURL("service:jmx:rmi://host"); // The environment map, null in this case Map environment = null; JMXConnectorServer cntorServer = JMXConnectorServerFactory.newJMXConnectorServer(address, environment, null); // The MBeanServer to which the JMXConnectorServer will be registered in MBeanServer server = MBeanServerFactory.createMBeanServer(); // Register the JMXConnectorServer in the MBeanServer ObjectName cntorServerName = ObjectName.getInstance("connectors:protocol=rmi"); server.registerMBean(cntorServer, cntorServerName); // Start the JMXConnectorServer cntorServer.start(); // An alternative way to start the JMXConnectorServer via the MBeanServer server.invoke(cntorServerName, "start", null, null); // Yet another way to start the JMXConnectorServer via the MBeanServer Object proxy = MBeanServerInvocationHandler.newProxyInstance(server, cntorServerName, JMXConnectorServerMBean.class, true); JMXConnectorServerMBean cntorServerMBean = (JMXConnectorServerMBean)proxy; cntorServerMBean.start();
Once a JMXConnectorServer is connected to an MBeanServer and once it has been started, it is possible to
create a JMXConnector on a client host and connect it to the JMXConnectorServer.
We already saw that the mean used by a JMXConnector to connect to a JMXConnectorServer is a JMXServiceURL.
If a JMXConnectorServer is the server-side component that allows to interact with a MBeanServer,
a JMXConnector is the client-side component that allows client code to contact a remote MBeanServer.
A JMXConnector handles the details of registering notification listeners and receiving notifications from the
remote MBeanServer, as well as providing a way to authenticate to the JMXConnectorServer, and eventually
execute operations on behalf of a given
javax.security.auth.Subject.
Finally the JMXConnector allows client code to obtain an implementation of the
javax.management.MBeanServerConnection interface that allows to interact with the
remote MBeanServer as if it is local.
The preferred way to create a JMXConnector is to use the javax.management.remote.JMXConnectorFactory class:
Example 3.3. Connecting a JMXConnector
// The address of the connector server JMXServiceURL address = ...; // The environment map, null in this case Map environment = null; // Create the JMXCconnectorServer JMXConnector cntor = JMXConnectorFactory.connect(address, environment); // Obtain a "stub" for the remote MBeanServer MBeanServerConnection mbsc = cntor.getMBeanServerConnection(); // Call the remote MBeanServer String domain = mbsc.getDefaultDomain();
JMXConnectors can be instantiated, but connected at a later time.
Below is a code snippet that shows how to instantiate a JMXConnector and then connect it
to the JMXConnectorServer.
Note the use of two different environment Maps: one is used to specify creation parameters, the other
to specify connection parameters.
Example 3.4. Creating and connecting a JMXConnector
// The address of the connector server JMXServiceURL address = ...; // The creation environment map, null in this case Map creationEnvironment = null; // Create the JMXCconnectorServer JMXConnector cntor = JMXConnectorFactory.newJMXConnector(address, creationEnvironment); // The connection environment map, null in this case // May contain - for example - user's credentials Map connectionEnvironment = null; // Connect cntor.connect(connectionEnvironment); // Obtain a "stub" for the remote MBeanServer MBeanServerConnection mbsc = cntor.getMBeanServerConnection(); // Call the remote MBeanServer String domain = mbsc.getDefaultDomain();
JSR 160 connectors are able to receive notifications emitted by a remote MBean.
The details of the mechanism of how remote notifications are delivered depends on the protocol
used by the connector; however, few general principles are explained below.
To receive notifications, a client must register a listener by means of the
javax.management.MBeanServerConnection.addNotificationListener(...) method.
There are two overloaded versions of this method: one that takes an ObjectName as listener, and one that
takes a NotificationListener as listener.
In the first case, the listener is remote (an MBean in the remote MBeanServer) and thus both the filter and the handback object are sent over the wire to the server (and of course both must be serializable).
The more interesting case is the second, where the listener is local to the client code.
In this case the listener that receives notifications emitted by a remote MBean always
remains local to the client code that registered it. The NotificationListener object is never sent across
the wire.
NotificationListener objects are usually implemented with anonymous inner classes
(that most of the times are not serializables), and client code should not make any particular attention
on how to implement NotificationListeners that receive remote Notifications: anonymous inner classes are
a good choice.
On the other end, if the remote MBean sends custom subclasses of the Notification class, it must ensure that
the custom Notification objects are
serializable.
The meaning of
serializable depends on the protocol used; in case of RMI, it is the
usual meaning of "Java serializable" (that is, it can be written to a
java.io.ObjectOutputStream).
NotificationFilters may or may not be sent over the wire, depending on the protocol used by the JMXConnector.
It is a good choice to use the standard JMX NotificationFilters such as
javax.management.NotificationFilterSupport,
javax.management.AttributeChangeNotificationFilter and
javax.management.relation.MBeanServerNotificationFilter to perform filtering of
Notifications: these classes are serializable, and known to the server side.
If you want to write your custom NotificationFilter, write it in such a way that will work no matter if it is
run on client side or on server side, and be sure its class is known to the server side (for more
details about server side classloading, refer to the JSR 160 specification).
The handback object always remains on client side.
In the MX4J implementation, notifications are requested by the client to the server, and use a notification
buffer as explained in the JSR 160 specification.
Since it's the client that initiates the notification request, the mechanism can be seen as a polling
mechanism. However, if the server does not have notifications to send to the client, it does not return
an empty result, but instead holds the call for a configurable timeout until a notification is emitted
or the timeout elapses.
This allows to reduce the network traffic (since there is no continuous polling from the client to the
server) and still have a fast reactivity to notification emitted by the server.
Refer to the javadocs of the
mx4j.remote.RemoteNotificationClientHandler
and of the
mx4j.remote.RemoteNotificationServerHandler
for further details.
Take also a look at the examples bundled with the MX4J distribution for code snippets on registering
listeners to remote MBeans.
Below, a quick example of how to register a listener to a remote MBean:
Example 3.5. Registering a NotificationListener to a remote MBean
// The address of the connector server JMXServiceURL address = ...; // The JMXConnector JMXConnector connector = JMXConnectorFactory.connect(address); // The MBeanServerConnection "stub" MBeanServerConnection mbsc = connector.getMBeanServerConnection(); // The MBeanServerDelegate emits notifications about registration/unregistration of MBeans ObjectName delegateName = ObjectName.getInstance("JMImplementation:type=MBeanServerDelegate"); NotificationListener listener = new NotificationListener() { public void handleNotification(Notification notification, Object handback) { // Do something } }; mbsc.addNotificationListener(delegateName, listener, null, null);