Java Dynamic Management Kit 5.1 Tutorial

Part III Remote Management Applications

In Part II, we saw how to access and manage a Java dynamic management agent through the HTML protocol adaptor. Protocol adaptors provide a view of an agent through communication protocols. In this part, we present protocol connectors and proxy MBeans for managing agents programmatically.

The Java Dynamic Management Kit (Java DMK) 5.1 integrates the Java Management Extensions (JMX) Remote API specification to provide the means to develop remote management applications in the Java programming language. These remote applications establish connections with agents through protocol connectors over remote method invocation (RMI) or over a new custom protocol, the JMX messaging protocol (JMXMP). Connections are established between a client object and a server object, via one of these protocols. The connector client object exposes a remote version of the MBean server interface. The connector server object in the agent transmits management requests to the MBean server and forwards any replies.

Connectors enable you to develop a management application that is both protocol independent and location-independent. Once the connection is established, the communication layer is transparent, and the manager can issue requests as if it were directly calling the MBean server. Using proxy objects that represent MBeans simplifies the design of the management application and reduces development time.

This homogeneity of the API makes it possible to develop portable management applications that can run either in an agent or in a remote management application. This simplifies the development and testing of applications, and it also allows functionality to evolve along with the management solution. As your agent and manager platforms evolve, management policies can be implemented at higher levels of management, and intelligent logic for monitoring and processing can be moved down into agents.

This part contains the following chapters:

Chapter 9 Protocol Connectors

Protocol connectors provide a point-to-point connection between a Java dynamic management agent and a management application. Each connector relies on a specific communication protocol, but the API that is available to the management application is identical for all connectors and is entirely protocol-independent.

A connector consists of a connector server component registered in the agent and a connector client object instance in the management application. The connector client exposes a remote version of the MBean server interface. Each connector client represents one agent to which the manager wants to connect. The connector server replies to requests from any number of connections and fulfills them through its MBean server. Once the connection is established, the remoteness of the agent is transparent to the management application, except for any communication delays.

Connectors rely on the Java serialization package to transmit data as Java objects between client and server components. Therefore, all objects needed in the exchange of management requests and responses must be instances of a serializable class. However, the data encoding and sequencing are proprietary, and the raw data of the message contents in the underlying protocol are not exposed by the connectors.

Java Dynamic Management Kit (Java DMK) version 5.1 implements the new connectors defined by Java Management Extensions (JMX) Remote API. The connectors implemented in previous versions of Java DMK are retained for backwards compatibility, and are described in Part VI. The two new connectors are based on the remote method invocation (RMI) protocol, and a new protocol called the JMX messaging protocol (JMXMP). These new connector protocols allow more sophisticated security than was previously possible to be implemented on the connectors, and are also less complicated to implement.

The code samples in this chapter are taken from the files in the current/Connectors example directory located in the main examplesDir (see Directories and Classpath in the Preface).

This chapter covers the following topics:

9.1 Connector Servers

Connector servers on the agent side listen for management requests issued through a corresponding connector client. The connector server transmits these requests to its MBean server and forwards any response back to the management application. The connector server also forwards notifications, when the management application has registered to receive them through its connector client.

A connector server listens for incoming requests from its corresponding connector client, decodes that request and encodes the reply. Several connector clients can establish connections with the same connector server, and the connector server can handle multiple requests simultaneously. There only needs to be one connector server MBean per protocol to which the agent needs to respond. However, several connector servers for the same protocol can coexist in an agent for processing requests on different ports.

9.1.1 Connector Server Factories

The simplest way to create connector servers is to use the JMXConnectorServerFactory constructor defined by JMX Remote API. No instances of this class are ever created, but its single method, newJMXConnectorServer() is called to create instances of the JMXConnectorServer class. New JMXConnectorServer instances are created when newJMXConnectorServer() is passed the following parameters:

  1. A JMXServiceURL, that serves as the address for the connector server; this URL identifies the connector protocol to be implemented, and the machine name and port number (or path or URL) at which this connector server will be bound.

  2. The environment Map for newJMXConnectorServer(); this can be null.

  3. A reference to the MBean server that will be exposed for remote management through this connector server. If null is given at creation time, the MBean server that will be exposed to remote management will be the MBean server in which the connector server is later registered as an MBean.

    Note that if you supply an MBean server at creation time, you can optionally register the connector server as an MBean. In that case, the MBean server in which you choose to register the connector server can optionally be the same MBean server as the one that was supplied at creation time. However, if you supply a null value for this parameter, you will need to register the connector server as an MBean in the MBean server that you wish to expose through that connector server.

In the JMXServiceURL, the connector protocol specified can be either RMI or JMXMP. Depending which protocol is specified, the connector server created will be either an instance of the RMIConnectorServer or JMXMPConnectorServer classes. Both of these classes inherit from the JMXConnectorServer class.

9.1.2 RMI Connector Server

The Java DMK RMI connector server supports the standard RMI transports, Java Remote Method Protocol (JRMP) and the Internet Inter-Object Request Broker (ORB) Protocol (IIOP). An example of an RMI connector is provided in the examplesDir that demonstrates an RMI connection between a server and a remote client.

The Server class from the RMI connector example is is shown in Example 9–1.


Example 9–1 RMI Connector Server

public static void main(String[] args) {  
      try {  
          // Instantiate the MBean server  
          //  
          MBeanServer mbs = MBeanServerFactory.createMBeanServer();  
 
          // Create an RMI connector server  
          //  
          JMXServiceURL url = new JMXServiceURL(  
            "service:jmx:rmi:///jndi/rmi://localhost:9999/server");  
          JMXConnectorServer cs =  
               JMXConnectorServerFactory.newJMXConnectorServer(url,  
               null, mbs);  
          cs.start();  
        } catch (Exception e) {  
          e.printStackTrace();  
        }  
   }  
}  

Firstly, the Server class creates a new MBean server called mbs by calling the createMBeanServer() method of the MBeanServerFactory class. A call to JMXServiceURL creates a new service URL called url, which serves as an address for the connector server. This service URL defines the following:

Finally, an RMI connector server named cs is created by calling the JMXConnectorServerFactory constructor, with the service URL url, a null environment map, and the MBean server mbs as parameters. The connector server cs is launched by calling the start() method of JMXConnectorServer, whereupon the instance of RMIConnectorServer that is created exports its underlying RMI server stub server to the RMI registry.

9.1.3 JMXMP Connector Server

The JMXMP connector protocol defined by Java DMK 5.1 is based on Java serialization over transmission control protocol (TCP) sockets. The JMXMP protocol is a custom protocol for JMX Remote API, and offers a more complete security solution than the RMI connector, as it can implement both the secure sockets layer (SSL) and the simple authentication and security layer (SASL) technologies. These optional security features are described in Chapter 11, Connector Security.

In the JMXMP connector, communication between server and client happens over a single TCP connection, and every message is a serialized Java object. Communication between server and client is performed in two separate streams, one for each direction, allowing multiple concurrent requests over the connection at any given time.

The JMXMP connector example is contained in the directory examplesDir/current/Connectors/jmxmp.

The code for a JMXMP connector server is shown in Example 9–2.


Example 9–2 JMXMP Connector Server

public class Server { 
 
   public static void main(String[] args) { 
      try { 
         // Instantiate the MBean server 
         // 
         MBeanServer mbs = MBeanServerFactory.createMBeanServer(); 
 
         // Create a JMXMP connector server 
         // 
         JMXServiceURL url = 
              new JMXServiceURL("jmxmp", null, 5555); 
         JMXConnectorServer cs = 
              JMXConnectorServerFactory.newJMXConnectorServer(url, 
              null, mbs); 
         cs.start(); 
       } catch (Exception e) { 
         e.printStackTrace(); 
       } 
    } 
} 
 

Firstly, the Server class creates a new MBean server named mbs by calling the createMBeanServer() method of the MBeanServerFactory class.

A call to JMXServiceURL creates a new service URL called url, which serves as an address for the connector server. This service URL defines the following:

  1. The connector will use the JMXMP protocol, denoted by jmxmp.

  2. No environment map is specified, as denoted by null.

  3. The connector server will listen for client connections on port 5555 on the local host.

Finally, a JMXMP connector server named cs is created by calling the constructor JMXConnectorServerFactory, with the service URL url, the null environment map, and the MBean server mbs as parameters. The connector server cs is launched by calling the start() method of JMXConnectorServer, whereupon JMXMPConnectorServer, which inherits from JMXConnectorServer, starts listening for client connections.

9.2 Connector Clients

The manager application interacts with a connector client to access an agent through an established connection. A connector client provides methods to handle the connection and access the agent.

Through the connector, the management application sends management requests to the MBeans located in a remote agent. Components of the management application access remote MBeans by calling the methods of the connector client for getting and setting attributes and calling operations on the MBeans. The connector client then returns the result, providing a complete abstraction of the communication layer.

9.2.1 Connector Factories

The simplest way to create connectors is to use the JMXConnectorFactory constructor defined by JMX Remote API. No instances of this class are ever created, but its method, connect() can be called to create a connected JMX connector using a JMXServiceURL that you pass it as a parameter. New, unconnected, JMXConnector instances can also be created by calling the other JMXConnectorFactory method, newJMXConnector(), and passing it the connector server's JMXServiceURL. An environment map can also be supplied to provide additional parameters.

The Client in these examples uses the JMXConnectorFactory.connect() method.

9.2.2 RMI Connector Client

The RMI connector Client example is shown in Example 9–3.


Example 9–3 RMI Connector Client

public class Client { 
 
  public static void main(String[] args) { 
    try { 
      // Create an RMI connector client 
      // 
      JMXServiceURL url = new JMXServiceURL( 
         "service:jmx:rmi:///jndi/rmi://localhost:9999/server"); 
      JMXConnector jmxc = JMXConnectorFactory.connect(url, null); 
      MBeanServerConnection mbsc = jmxc.getMBeanServerConnection(); 
       
      // Get domains from MBeanServer 
      // 
      String domains[] = mbsc.getDomains(); 
      for (int i = 0; i < domains.length; i++) { 
          System.out.println("Domain[" + i + "] = " + domains[i]); 
      } 
 
      String domain = mbsc.getDefaultDomain();       
   
      // Create SimpleStandard MBean and perform simple MBean operations 
      // 
      ObjectName mbeanName =  
             new ObjectName("MBeans:type=SimpleStandard"); 
      mbsc.createMBean("SimpleStandard", mbeanName, null, null); 
      System.out.println("\nMBean count = " + mbsc.getMBeanCount()); 
      System.out.println("\nState = " + 
             mbsc.getAttribute(mbeanName, "State")); 
      mbsc.setAttribute(mbeanName, 
             new Attribute("State", "changed state")); 
             
      SimpleStandardMBean proxy = (SimpleStandardMBean) 
          MBeanServerInvocationHandler.newProxyInstance( 
                                       mbsc, 
                                       mbeanName, 
                                       SimpleStandardMBean.class, 
                                       false); 
      System.out.println("\nState = " + proxy.getState()); 
 
      ClientListener listener = new ClientListener(); 
      mbsc.addNotificationListener(mbeanName, listener, null, null); 
 
      mbsc.invoke(mbeanName, "reset", null, null); 
 
      mbsc.removeNotificationListener(mbeanName, listener); 
      mbsc.unregisterMBean(mbeanName); 
      jmxc.close(); 
    } catch (Exception e) {      e.printStackTrace(); 
    } 
  } 
} 

In this example, the Client creates an RMI connector client that is configured to connect to the RMI connector server created by Server in 9.1.2 RMI Connector Server.

As you can see, the Client defines the same service URL url as that defined by Server. This allows the connector client to retrieve the RMI connector server stub named server from the RMI registry running on port 9999 of the local host, and to connect to the RMI connector server.

With the RMI registry thus identified, the connector client can be created. The connector client, jmxc, is an instance of the JMX Remote API interface JMXConnector, created by the connect() method of JMXConnectorFactory. The connect() method is passed the parameters url and a null environment map when it is called.

An instance of MBeanServerConnection, named mbsc, is then created by calling the getMBeanServerConnection() method of the JMXConnector instance jmxc.

The connector client is now connected to the MBean server created by Server, and can create MBeans and perform operations on them with the connection remaining completely transparent to both ends.

In the examples directory, there is an MBean interface and a class to define an MBean called SimpleStandard. As the name suggests, this is a very basic MBean of the type described in Chapter 1, Standard MBeans. The connector client creates an instance of this SimpleStandard MBean and registers it in the MBean server with a call to the createMBean() method of MBeanServerConnection. The client then activates notifications by calling addNotificationListener(), and performs the operations defined by SimpleStandard as if they were local MBean operations.

Finally, the client unregisters the SimpleStandard MBean and closes the connection.

9.2.3 JMXMP Connector Client

The Client.java class is shown in Example 9–4. The only difference between the client in this example and that used in the RMI connector example is in the JMX service URL. The operations this example performs on the SimpleStandard MBean are identical to those performed in the RMI connector example. Consequently, the code has been abridged.


Example 9–4 JMXMP Connector Client

public class Client { 
 
   public static void main(String[] args) { 
     try { 
        // Create a JMXMP connector client 
        // 
        System.out.println("\nCreate a JMXMP connector client"); 
        JMXServiceURL url = 
            new JMXServiceURL("service:jmx:jmxmp://localhost:5555"); 
        JMXConnector jmxc = JMXConnectorFactory.connect(url, null); 
        MBeanServerConnection mbsc = jmxc.getMBeanServerConnection(); 
 
        // Get domains from MBeanServer, create SimpleStandard MBean 
        // 
        String domains[] = mbsc.getDomains(); 
        for (int i = 0; i < domains.length; i++) { 
            System.out.println("Domain[" + i + "] = " + domains[i]); 
        ObjectName mbeanName = new ObjectName(
             "MBeans:type=SimpleStandard"); 
        mbsc.createMBean("SimpleStandard", mbeanName, null, null); 
 
       // Perform simple MBean operations, add listener, reset MBean, 
       // remove listener, unregister MBean 
 
       [...] 
        
       // Close MBeanServer connection 
       // 
       jmxc.close(); 
     } catch (Exception e) { 
       e.printStackTrace(); 
     } 
   } 
} 

The Client.java class creates a JMXMP connector client that is configured to connect to the JMXMP connector server created by Server.

As you can see, Client defines a service URL url that allows the connector client to find the connector server.

The connector client, jmxc, is an instance of the JMXConnector interface, created by the connect() method of JMXConnectorFactory. The connect() method is passed the parameter url when it is called.

An instance of MBeanServerConnection, named mbsc, is then created by calling the getMBeanServerConnection() method of the JMXConnector instance jmxc.

The connector client is now connected to the MBean server created by Server, and can create MBeans and perform operations on them with the connection remaining completely transparent to both ends.

The client creates and registers the SimpleStandard MBean in the MBean server with a call to the createMBean() method of MBeanServerConnection, and performs the operations defined by SimpleStandard as if they were local MBean operations.

Finally, the client unregisters SimpleStandard and closes the connection.

9.3 Examples of Connector Servers

The connector server examples demonstrate the following:

  1. The server:

    1. Creates an MBean server

    2. Creates an RMI or a JMXMP connector server

  2. The remote client:

    1. Creates an RMI or a JMXMP connector

    2. Creates a simple standard MBean in the MBean server, via the RMI connection

    3. Creates a generic notification listener.

  3. Basic operations are performed on the MBean registered in the MBean server, via the RMI connection

9.3.1 RMI Connector Server Example

The RMI connector server example is found in the examplesDir/current/Connectors/rmi directory.

To Run the RMI Connector Server Example
  1. Compile the Java classes.


    $ javac -classpath classpath *.java 
  2. Start an RMI registry on port 9999 of the local host.


    $ export CLASSPATH=.:classpath ; rmiregistry 9999 &
  3. Start the RMI connector server:


    $ java -classpath .:classpath Server &

    You will see confirmation of the creation of the MBean server and the RMI connector server.

  4. Start the RMI connector client:


    $ java -classpath .:classpath Client

    You will see confirmation of the creation of the RMI connector client and of the connection with the connector server. You will also be informed of the domain name, and the creation and registration of SimpleStandard MBean. The client will then perform operations on SimpleStandard MBean, before unregistering it.

9.3.2 JMXMP Connector Server Example

The JMXMP connector server example is found in the examplesDir/current/Connectors/jmxmp directory.

To Run the JMXMP Connector Server Example
  1. Compile the Java classes.


    $ javac -classpath classpath *.java 
  2. Start the JMXMP connector server:


    $ java -classpath .:classpath Server &

    You will see confirmation of the creation of the MBean server and the JMXMP connector server.

  3. Start the JMXMP connector client:


    $ java -classpath .:classpath Client

    You will see confirmation of the creation of the RMI connector client and of the connection with the connector server. You will also be informed of the domain name, and the creation and registration of SimpleStandard MBean. The client will then perform operations on SimpleStandard MBean, before unregistering it.

9.4 Remote Notifications and Heartbeat Mechanism

In previous versions of Java DMK, notification forwarding over remote connectors and the heartbeat mechanism to monitor the health of the connections were performed by custom implementations written specifically for Java DMK. These mechanisms have now been standardized by the implementation of JMX Remote API in Java DMK 5.1, and consequently are integrated into the RMI and JMXMP connectors described in the preceding sections of this chapter.

9.5 Wrapping Legacy Connectors

Although it is recommended that you use the new RMI and JMXMP connector protocols defined by the JMX Remote API, it is possible for you to continue to use your existing legacy connectors alongside the new ones. This is achieved by wrapping the legacy connector so that it appears in a form that is compatible with the new standard connectors. Wrapping your Java DMK 5.0 RMI and HTTP(S) connectors allows applications created using Java DMK 5.1 to interoperate with existing Java DMK applications. In addition, if you want to use HTTP(S) connectors, you must wrap them.

The JmxConnectorServerFactory and JmxConnectorFactory classes are used to create wrapped legacy Java DMK connector servers and clients. These connector servers and clients expose the same interfaces as standard JMX connectors. For the JmxConnectorServerFactory to create a wrapped connector, you must ensure that the jdmkrt.jar is either in your CLASSPATH environment variable, or in the context of the thread that is used to create the wrapped connector.

Java DMK 5.1 defines a new interface, JdmkLegacyConnector, that is used to obtain the wrapped connectors.

The JdmkLegacyConnector interface specifies a new protocol name for each of the legacy connectors. These protocol names are passed into the JMXServiceURL when it is created, in the same way the RMI connector and JMXMP connectors are identified as rmi and jmxmp respectively in the service URLs. The new protocol names are listed below.

The JdmkLegacyConnector also specifies a list of properties to allow the factory to obtain information defined by the user to create the legacy connectors, as follows.

The creation of wrapped legacy RMI and HTTP connector servers is shown in Example 9–5. The code extracts in this section are taken from the classes in the examplesDir/current/Connectors/wrapping directory.


Example 9–5 Wrapping Legacy Connector Servers

public class Server {

    public static void main(String[] args) {
	     try {
           MBeanServer mbs = MBeanServerFactory.createMBeanServer();

           JMXServiceURL httpURL = new JMXServiceURL("jdmk-http", 
                                                      null, 6868);
           JMXConnectorServer httpCS =
              JMXConnectorServerFactory.newJMXConnectorServer(httpURL, 
                                                              null, mbs);
           ObjectName httpON = 
               new ObjectName("legacyWrapper:protocol=jdmk-http,port=6868");
	           mbs.registerMBean(httpCS, httpON);

            httpCS.start();

            JMXServiceURL rmiURL = 
               new JMXServiceURL("jdmk-rmi", null, 8888, "/myRMI");
            JMXConnectorServer rmiCS =
                JMXConnectorServerFactory.newJMXConnectorServer(rmiURL, 
                                                                null, mbs);

	            ObjectName rmiON = 
                new ObjectName("legacyWrapper:protocol=jdmk-rmi,port=8888");
	            mbs.registerMBean(rmiCS, rmiON);
	
            rmiCS.start();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

In Example 9–5, we see that the connector servers are created in a similar way to standard connector servers defined by the JMX Remote API. The only difference is that the connector protocols used to create the JMX service URLs are jdmk-rmi and jmdk-http. For simplicity, this example does not create an HTTPS connector.


Note –

As you can see above, the wrapped connector server is registered in the MBean server before it is started. This order of events must always be respected, otherwise the wrapped connector will not work.



Example 9–6 Wrapping Legacy Connector Clients

public class Client {

  public static void main(String[] args) {
	   try {
	       JMXServiceURL url = new JMXServiceURL("jdmk-http", 
                                              null, 6868);
	       connect(url);
	    
	       url = new JMXServiceURL("jdmk-rmi", null, 8888, 
                                "/myRMI");
	       connect(url);
    } catch (Exception e) {
        e.printStackTrace();
    }
	   System.exit(0);
  }
  
  [...]

As for the creation of the connector servers, in Example 9–6, the legacy connector clients are created by passing the jdmk-http and jdmk-rmi property strings to the JMX service URLs. The rest of the Client example is identical to a standard JMX Remote API connector client.

To Run the Legacy Connector Wrapping Example

The following example uses the classes in the examplesDir/current/Connectors/wrapping directory.

  1. Compile the Java classes


    $ javac -classpath classpath *.java
  2. Start the Server


    $ java -classpath .:classpath Server &

    You will see confirmation of the creation of the wrapped connector servers, and will be prompted to start the Client.

  3. When prompted, start the Client


    $ java -classpath .:classpath Client

    You will see the wrapped HTTP connector client connect to the HTTP server, and perform various MBean operations via the connection. Once the MBean operations have completed, the Client closes the HTTP connection, before opening a connection to the legacy RMI connector server, and performing further MBean operations.

9.5.1 Limitations of Wrapped Legacy Connectors

The wrapped legacy connectors do not provide the full functionality of either their JMX Remote API or their Java DMK 5.0 counterparts. The limitations of the wrapped legacy connectors are listed below.

Chapter 10 Lookup Services

The Java Management Extensions (JMX) Remote API specification defines three bindings to lookup services, using existing lookup technologies, as described in the following sections:

This chapter provides an introduction to the lookup services provided by Java DMK using three sets of examples. For a full description of the lookup services, see the JMX Remote API 1.0 Specification document.

10.1 Initial Configuration

As shown simply in Chapter 9, Protocol Connectors, if you are using remote method invocation (RMI) connectors, you can choose to use an external directory to register the connector server stubs you want to look up. The following cases are presented in the lookup service examples relating to RMI connectors:

If you choose to register the RMI connector stubs in an external directory, some initial configuration is required, to set up your RMI registry, CORBA naming service or LDAP server. If you do not use an external directory, the RMI connector stub is encoded into the JMX service URL.

The lookup service examples for the JMX messaging protocol (JMXMP) connector do not use any external directories.

The following sections describe the external directories that you can use in conjunction with the lookup service examples that use RMI connectors. These external directories are referred to when running the three examples of lookup services that are given in the subsequent sections in this chapter.

In these examples, the addresses of the different registries are given as URLs in JNDI form. For an explanation of JNDI form, see the API documentation for the javax.management.remote.rmi package. If you want to run the external directories on a machine other than the local machine, you must specify that machine's host name instead of localhost.

10.1.1 External RMI Registry

To register the RMI connector server stubs in an external RMI registry, for use by connectors implementing the JRMP transport, start an RMI registry with the following command:


$ rmiregistry 9999 &

For your convenience when typing commands, create an environment variable for the address of the RMI registry.


$ jndirmi="rmi://localhost:9999"

10.1.2 External CORBA Naming Service

To register connector stubs in the CORBA naming service, you must start an ORB daemon, with the following commands:


$ rm -rf ./orb.db
$ orbd -ORBInitialPort 7777 &

For your convenience when typing commands, create an environment variable for the address of the CORBA naming service.


$ jndiiiop="iiop://localhost:7777"

10.1.3 External LDAP registry

To register connector stubs in an LDAP registry, you must start an LDAP server. The LDAP server you use is your choice, although the schema for representing Java objects in an LDAP directory must be known to the server. See the Request For Comments (RFC) document RFC 2713for details.

Once you have started your LDAP server, in to create a directory context under which you have the permission to create new nodes, create a new component suffix:


dc=Test

See the documentation accompanying your LDAP server for details of how to configure the server and create this suffix. Alternatively, if you already have the appropriate rights of node creation under an existing node, you can use that node instead. In that case, you must replace dc=Test with the name of your node wherever dc=Test appears in these examples.

For your convenience, set the following LDAP parameters as environment variables:

You are now ready to run the different lookup service examples.

10.2 Service Location Protocol (SLP) Lookup Service

Java DMK 5.1 specifies how to register RMI connectors or JMXMP connectors with the SLP lookup service.

The purpose of this example is to demonstrate how a connector client can find and connect to a connector server that has registered with the SLP lookup service. This example performs the following operations:

  1. The agent:

    • Creates an MBean server

    • Gets a pointer to the SLP lookup service

    • Creates a connector server

    • Registers the connector address with the SLP lookup service

  2. The client:

    • Gets a pointer to the SLP lookup service

    • Looks for any connector servers registered in the SLP lookup service

    • Creates a JMX Remote API connector

    • Retrieves information about the MBeans in the MBean server


Note –

This example assumes that you are already familiar with SLP technology. The code provided for this example conforms to Sun Microsystems' implementation of SLP, as defined by RFC 2614. Sun Microsystems' implementation of SLP is available in the Solaris operating environment in the directory /usr/share/lib/slp. If you are not running the Solaris operating environment, you must obtain a version of SLP that is compliant with RFC 2614, section 5.


The SLP lookup example is contained in the directory examplesDir/current/Lookup/slp. For explanations of the SLP code used in this example, see RFC 2614 and the API documentation for SLP; the explanations below concentrate on the Java DMK implementation.

10.2.1 Registering the Connector Server with SLP

Example 10–1 shows the registration of a connector server's URL with the SLP lookup service's Advertiser. This code is taken from the Server class in the SLP example directory.


Example 10–1 Registering the Connector Server's Address with the SLP Advertiser

public class Server { 
   public final static int JMX_DEFAULT_LEASE = 300; 
   public final static String JMX_SCOPE = "DEFAULT"; 
 
   private final MBeanServer mbs; 
   public Server() { 
       mbs = MBeanServerFactory.createMBeanServer(); 
   } 
    
[...] 

   public static void register(JMXServiceURL jmxUrl, String name) 
     throws ServiceLocationException { 
     ServiceURL serviceURL = 
          new ServiceURL(jmxUrl.toString(), 
                         JMX_DEFAULT_LEASE); 
     debug("ServiceType is: " + serviceURL.getServiceType()); 
     Vector attributes = new Vector(); 
     Vector attrValues = new Vector(); 
     attrValues.add(JMX_SCOPE); 
     ServiceLocationAttribute attr1 = 
          new ServiceLocationAttribute("SCOPE", attrValues); 
     attributes.add(attr1); 
     attrValues.removeAllElements(); 
     attrValues.add(name); 
     ServiceLocationAttribute attr2 = 
          new ServiceLocationAttribute("AgentName", attrValues); 
     attributes.add(attr2); 
     final Advertiser slpAdvertiser = 
          ServiceLocationManager.getAdvertiser(Locale.US); 
     slpAdvertiser.register(serviceURL, attributes); 
      
   }  
 
[...] 

Examining this code excerpt, we see that the SLP lease JMX_DEFAULT_LEASE is set to a default lease of 300 seconds, which corresponds to the length of time the URL will be registered, and the initial creation of the MBean server mbs with a call to MBeanServerFactory.createMBeanServer(). In code that is not shown here, an SLP advertiser slpAdvertiser, and an SLP service URL url are defined. The slpAdvertiser is used to register the service URL in the SLP lookup service.

The service URL jmxUrl is the address of the connector server, and is obtained by a call to the getAddress() method of JMXConnectorServer when the connector server is started.

The SLP lookup attribute, namely the agent name under which the connector server address is to be registered (name), is then specified by the SLP class ServiceLocationAttribute. The AgentName attribute is mandatory, but other optional attributes, such as ProtocolType, AgentHost, and Property can also be registered in the SLP lookup service.

Finally, the JMX connector server address is registered in the SLP advertiser service with a call to the register() method of the Advertiser interface, with the serviceURL and the attributes passed in as parameters.

Now that the connector server's address has been advertised, the connector server itself is created and registered with SLP, as shown in Example 10–2.


Example 10–2 Registering the Connector Server in the SLP Lookup Service

[...] 
 
   public JMXConnectorServer rmi(String url) throws 
     IOException, 
     JMException, 
     NamingException, 
     ClassNotFoundException, 
     ServiceLocationException { 
     JMXServiceURL jurl = new JMXServiceURL(url); 
     final HashMap env = new HashMap(); 
     // Environment map attributes 
     [...] 
 
  
     JMXConnectorServer rmis = 
        JMXConnectorServerFactory.newJMXConnectorServer(jurl, env, mbs); 
     final String agentName = System.getProperty("agent.name", 
                                                 "DefaultAgent"); 
     start(rmis, agentName); 
 
     return rmis; 
  } 
 
   public void start(JMXConnectorServer server, String agentName) 
      throws IOException, ServiceLocationException { 
      server.start(); 
      final JMXServiceURL address = server.getAddress(); 
      register(address,agentName); 
   } 
    

The service URL jurl is constructed from the string url that will be included in the command used to launch the Server at the command line. An RMI connector server named rmis is then created with the system properties defined by the environment map and the address jurl.

The connector server is then started, and the RMI connector server address is registered in the SLP lookup service under the name agentName. Subsequent code not shown here creates a corresponding JMXMP connector server named jmxmp, that is also registered with the SLP service.

10.2.2 Looking up the Connector Server

The following code examples are taken from the Client class in the examplesDir/current/Lookup/slp directory.


Example 10–3 Retrieving the List of Connector Servers

public class Client { 
 
    public final static String JMX_SCOPE = "DEFAULT"; 
 
    public static Locator getLocator() throws ServiceLocationException { 
      final Locator slpLocator = 
          ServiceLocationManager.getLocator(Locale.US); 
      return slpLocator; 
    } 
     
      public static List lookup(Locator slpLocator, String name) 
          throws IOException, ServiceLocationException { 
 
   
          final ArrayList list = new ArrayList(); 
          Vector scopes = new Vector(); 
 
          scopes.add(JMX_SCOPE); 
          String query =  
              "(&(AgentName=" + ((name!=null)?name:"*") + "))"; 
 
          ServiceLocationEnumeration result = 
              slpLocator.findServices(new ServiceType("service:jmx"), 
                                      scopes, query); 
 
          while(result.hasMoreElements()) { 
                final ServiceURL surl = (ServiceURL) result.next(); 
                 
 
             JMXServiceURL jmxUrl = new JMXServiceURL(surl.toString()); 
             try { 
                  JMXConnector client = 
                     JMXConnectorFactory.newJMXConnector(jmxUrl,null); 
                  if (client != null) list.add(client); 
             } catch (IOException x ) {  
             [...] 
             } 
          } 
      } 
      return list; 
    } 

Example 10–3 first of all obtains the SLP service Locator by calling the getLocator method of the SLP class ServiceLocationManager. Client then retrieves all the connector servers registered in the SLP service under a given agent name, or under agent names that match a certain pattern. If no agent name is specified when the Client is started, all agent names will be considered.

A JMX Remote API service URL, jmxUrl, is generated for each of the agents retrieved by SLP, with each agent's SLP service URL, surl, passed as a parameter into the JMXServiceURL instance. The URL jmxUrl is then passed to the newJMXConnector() method of JMXConnectorFactory, to create a new connector client named client for each agent that is registered in the SLP service.

The connector clients retrieved are stored in an array list called list.


Example 10–4 Accessing the MBeans in the Remote MBean Server

public static void listMBeans(MBeanServerConnection server) 
     throws IOException { 
 
     final Set names = server.queryNames(null,null); 
     for (final Iterator i=names.iterator(); i.hasNext(); ) { 
          ObjectName name = (ObjectName)i.next(); 
          System.out.println("Got MBean: "+name); 
          try { 
               MBeanInfo info = 
                  server.getMBeanInfo((ObjectName)name); 
               MBeanAttributeInfo[] attrs = info.getAttributes(); 
               if (attrs == null) continue; 
               for (int j=0; j<attrs.length; j++) { 
                    try { 
                         Object o = 
                         server.getAttribute(name,attrs[j].getName()); 
                         System.out.println("\t\t" + attrs[j].getName() + 
                         " = "+o); 
                    } catch (Exception x) { 
                         System.err.println("JmxClient failed to get " + 
                                             attrs[j].getName() + x); 
                         x.printStackTrace(System.err); 
                    } 
     } 
} 
 

In Example 10–4, a reference to the MBeanServerConnection is retrieved for every connector client that is created from the connector server address stored in the SLP service. A list of all the MBeans and their attributes is retrieved.


Example 10–5 Connecting to the Remote Agents

public static void main(String[] args) { 
      try { 
           final String agentName = System.getProperty("agent.name"); 
           final Locator slpLocator = getLocator(); 
           List l = lookup(slpLocator,agentName); 
           int j = 1; 
           for (Iterator i=l.iterator();i.hasNext();j++) { 
                JMXConnector c1 = (JMXConnector) i.next(); 
                if (c1 != null) { 
                    try { 
                         c1.connect(env); 
                    } catch (IOException x) { 
                         System.err.println ("Connection failed: " + x); 
                         x.printStackTrace(System.err); 
                         continue; 
                    } 
 
                    MBeanServerConnection conn = 
                         c1.getMBeanServerConnection(); 
 
                    try { 
                         listMBeans(conn); 
                    } catch (IOException x) { 
                         x.printStackTrace(System.err); 
                    } 
                    try { 
                         c1.close(); 
                    } catch (IOException x) { 
                         x.printStackTrace(System.err); 
                    } 
                } 
           } 
      } catch (Exception x) { 
           x.printStackTrace(System.err); 
      } 
} 

In Example 10–5, the agent.name property is retrieved by calling the getProperty() method of the System class, and the SLP lookup service is found by calling the getLocator() method of Locator.

All the agents named agentName are then looked up, and connections are made to the agents discovered. If no agent is specified, then all agents are lookup up. Connections are made to the MBean server created by Server, and all the MBeans in it are listed, before the connection is closed down.

10.2.3 Running the SLP Lookup Service Example

In addition to the actions you performed in 10.1 Initial Configuration, before you can run the lookup service examples that use the SLP, you must perform some further set—up actions that are specific to this example. You can then start looking up connectors using SLP in conjunction with the two connectors supported by Java DMK.

When you run the examples, to help you keep track of which agent has been created with which connector and transport, the agent names include a letter suffix. For example, the agent from the example of an RMI connector over JRMP, without an external directory, is called test-server-a.

To Set up the SLP Lookup Service Example

The following steps are required by all of the different connector/transport combinations you can run in this example.

  1. For convenience when compiling and running the classes, define an additional environment variable.

    In addition to the common environment variables that were set in 10.1 Initial Configuration, you need to add the path to the SLP service. If you are using the Solaris operating environment, add the following variables:


    $ SLP_LIB=/usr/share/lib/slp
    

    If you are using another platform, set SLP_LIB appropriately for the platform you are using.

  2. Define and export the classp environment variable.

    This example requires a classpath that includes the Java archive (JAR) files for SLP, as well as the JARs for Java DMK, the mandatory and optional parts of the JMX Remote API, and the JMX API reference implementation.


    classp=$SLP_LIB/slp.jar:classpath:.
  3. Start the SLP daemon.

    If you are using the Solaris operating environment, type the following command, which requires you to know your superuser password:


    $ su root -c "java -cp $SLP_LIB/slpd.jar com.sun.slp.slpd &"
    Password: [type superuser password] 

    If you are not running a Solaris system, start the SLP daemon according to the implementation of SLP you are using.

To Run the SLP Lookup Service Example With an RMI Connector

This example demonstrates the use of the SLP lookup service to look up RMI connector servers that use RMI's default transport, JRMP, as well as the IIOP transport. In addition, as described in 10.1 Initial Configuration, different external directories are used to register the RMI connector stubs.

The combinations of transports and external directories demonstrated here are:

Perform the following steps to run the example:

  1. Start the Server.

    The command you use to start the Server varies according to which external directory you are using. You can start one or more of the following instances of Server with different transports and external registries before starting the Client.

    • RMI connector over JRMP, without an external directory:


      $ java -classpath .:$classp -Ddebug=true \ 
        -Dagent.name=test-server-a \ 
        -Durl ="service:jmx:rmi://" \ 
        slp.Server & 
      

      In this command:

      • debug is set to true to provide more complete screen output when the Server runs

      • The name of the agent to be created is test-server-a

      • The service URL specifies that the chosen connector is an RMI connector, running over the RMI default transport JRMP.

      When Server is launched, you will see confirmation of the creation of the RMI connector, and the registration of its URL in the SLP service.

    • RMI connector over JRMP, using an RMI registry as an external directory:


      $ java -classpath .:$classp -Ddebug=true \ 
        -Dagent.name=test-server-b \ 
        -Durl="service:jmx:rmi:///jndi/${jndirmi}/server" \ 
        slp.Server & 
      

      In this command:

      • The name of the agent created is test-server-b

      • The service URL specifies the chosen connector as RMI over JRMP, and the external directory in which the RMI connector stub, server, is stored is the RMI registry you identified as jndirmi in 10.1 Initial Configuration.

      When Server is launched, you will see confirmation of the creation of the RMI connector, and the registration of its URL in the SLP service.

    • RMI connector over JRMP, using LDAP as the external directory:


      $ java -classpath .:$classp -Ddebug=true \
           -Dagent.name=test-server-c \
           -Durl="service:jmx:rmi:///jndi/${jndildap}/cn=x,dc=Test" \
           -Djava.naming.security.principal="$principal" \
           -Djava.naming.security.credentials="$credentials" \
           slp.Server &
      

      In this command:

      • The name of the agent created is test-server-c

      • The service URL specifies the chosen connector as RMI over JRMP, and the external directory in which the RMI connector stub is stored is the LDAP server you identified as jndildap in 10.1 Initial Configuration

      • The stub is registered in the Test domain component in the LDAP server.

      • The common name attribute principal and password credentials are given to gain access to the LDAP server.

      When Server is launched, you will see confirmation of the creation of the RMI connector, and the registration of its URL in the SLP service under the agent name test-server-c.

    • RMI connector over IIOP, without an external directory:


      $ java -classpath .:$classp -Ddebug=true \
           -Dagent.name=test-server-d \
           -Durl="service:jmx:iiop://" \
           slp.Server &
      

      In this command:

      • The name of the agent created is test-server-d

      • The service URL specifies the chosen connector as RMI connector over IIOP.

      When the Server is launched, you will see confirmation of the creation of the RMI connector, and the registration of its automatically generated URL in the SLP service.

    • RMI connector over IIOP, using CORBA naming as the external directory.


      $ java -classpath .:$classp -Ddebug=true \
           -Dagent.name=test-server-e \
           -Durl="service:jmx:iiop:///jndi/${jndiiiop}/server" \
           slp.Server &
      

      In this command:

      • The name of the agent created is test-server-e

      • The service URL specifies the chosen connector as RMI connector over IIOP. The external directory in which the RMI connector stub server is stored is the CORBA naming service you identified as jndiiiop in 10.1 Initial Configuration.

    • RMI connector over IIOP, using LDAP as the external directory.


      $ java -classpath .:$classp -Ddebug=true \
           -Dagent.name=test-server-f \
           -Durl="service:jmx:iiop:///jndi/${jndildap}/cn=x,dc=Test" \
           -Djava.naming.security.principal="$principal" \
           -Djava.naming.security.credentials="$credentials" \
           slp.Server &
      

      In this command:

      • The name of the agent created is test-server-f

      • The service URL specifies the chosen connector as RMI over IIOP, and the external directory in which the RMI connector stub is stored is the LDAP server you identified as jndildap in 10.1 Initial Configuration.

      • The stub is registered in the Test domain component in the LDAP server.

      • The common name attribute principal and password credentials are given to gain access to the LDAP server.

      When Server is launched, you will see confirmation of the creation of the RMI connector, and the registration of its URL in the SLP service under the agent name test-server-f.

  2. Start the Client.

    After starting the Server using the transport and external directory of your choice, start the Client:


    $ java -classpath .:$classp -Ddebug=true \
         -Djava.naming.security.principal="$principal" \
         -Djava.naming.security.credentials="$credentials" \
         slp.Client
    

    You will see output confirming the detection of the agents created by the Server and registered in the lookup service. You will also see the identification and confirmation of the connection made to the agents.

    To look up a specific agent, type the following command:


    $ java -classpath .:$classp -Ddebug=true \  
         -Djava.naming.security.principal="$principal" \ 
         -Djava.naming.security.credentials="$credentials" \ 
         -Dagent.name="agentName" \ 
        slp.Client
    

    In the command shown above, agentName is the name of the agent you want to look up. You can also specify a partial agent name by using *; for example, x* for all agent names beginning with the letter x.

To Run the SLP Lookup Service Example With a JMXMP Connector

This example demonstrates the use of the SLP lookup service to look up JMXMP connector servers.

  1. Start the Server.


    $ java -classpath .:$classp -Ddebug=true \
         -Dagent.name=test-server-g \
         -Durl="service:jmx:jmxmp://" \
         slp.Server &
    

    In this command:

    • The name of the agent created is test-server-g

    • The service URL specifies the chosen connector as the JMXMP connector, running on the first available port.

    When the Server is launched, you will see confirmation of the creation of the JMXMP connector, and the registration of its automatically generated URL in the SLP service. JMXMP connector servers can be used alongside RMI connectors, and will be detected by the Client in exactly the same way as RMI connector servers.

  2. Start the Client.

    After starting the Server, start the Client:


    $ java -classpath .:$classp -Ddebug=true slp.Client 
    

    You will see output confirming the detection of the agents created by the Server and registered in the lookup service. You will also see the identification and confirmation of the connection made to the agents.

    To look up a specific agent, type the following command:


    $ java -classpath .:$classp -Ddebug=true \  
      -Dagent.name="agentName" \ 
      slp.Client 
    

    In the command shown above, agentName is the name of the agent you want to look up. You can also specify a partial agent name by using *; for example, x* for all agent names beginning with the letter x.

10.3 Jini Lookup Service

The purpose of this example is to demonstrate how a connector client can find and connect to a connector server that has registered with the Jini lookup service. This example performs the following operations:

The Jini lookup service example is contained in the directory examplesDir/current/Lookup/jini.


Note –

These examples assume that you are already familiar with the Jini network technology. The documentation for the Jini network technology is available at http://wwws.sun.com/software/jini/specs/index.html. You can download the Jini network technology from the Sun Microsystems Community Source Licensing page, at http://wwws.sun.com/software/communitysource/jini/download.html. This example has been implemented using the Jini Technology Starter Kit Version 1.2.1_001.


10.3.1 Registering the Connector Server with the Jini Lookup Service

The following code extract is taken from the Server class in the Jini Lookup Service examples directory.


Example 10–6 Creating Connector Servers for Registration in the Jini Lookup Service

public class Server { 
   private final MBeanServer mbs; 
   private static boolean debug = false; 
   public Server() { 
     mbs = MBeanServerFactory.createMBeanServer(); 
   } 
 
  public JMXConnectorServer rmi(String url) 
      throws IOException, JMException, ClassNotFoundException { 
      JMXServiceURL jurl = new JMXServiceURL(url); 
      final HashMap env = new HashMap(); 
      // Environment map attributes 
     [...] 
     JMXConnectorServer rmis = 
        JMXConnectorServerFactory.newJMXConnectorServer(jurl, env, mbs); 
 
     final String agentName = System.getProperty("agent.name", 
                                                 "DefaultAgent"); 
 
     start(rmis,env,agentName); 
 
     return rmis; 
   } 
    
[...] 

Example 10–6 shows the creation of an MBean server mbs. As was the case for the SLP examples, the JMX service URL and the agent name are passed to Server when it is launched at the command line.

We then see the creation of an RMI connector server named rmis, using the system properties defined by the environment map env and the address jurl. The RMI connector server rmis is started. The RMI connector server address will be registered in the Jini lookup service under the name agentName. Subsequent code not shown here creates a corresponding JMXMP connector server named jmxmp, that will also be registered with the Jini lookup service, as shown in the following code extract.


Example 10–7 Registering the Connector Server with the Jini Lookup Service

public void start(JMXConnectorServer server, Map env, String agentName) 
      throws IOException, ClassNotFoundException { 
      server.start(); 
      final ServiceRegistrar registrar=getRegistrar(); 
      final JMXConnector proxy = server.toJMXConnector(env); 
      register(registrar,proxy,agentName); 
   } 
    
   public static ServiceRegistrar getRegistrar() 
      throws IOException, ClassNotFoundException, 
         MalformedURLException { 
      final String jurl = 
         System.getProperty("jini.lookup.url","jini://localhost"); 
      final LookupLocator lookup = new LookupLocator(jurl); 
      final ServiceRegistrar registrar = lookup.getRegistrar(); 
      if (registrar instanceof Administrable) 
          debug("Registry is administrable."); 
      return registrar; 
   } 
    
   public static ServiceRegistration register(ServiceRegistrar registrar, 
                                              JMXConnector proxy, 
                                              String name) 
      throws IOException { 
      Entry[] serviceAttrs = new Entry[] { 
              new net.jini.lookup.entry.Name(name) 
                       }; 
		        
      System.out.println("Registering proxy: AgentName=" + name ); 
      debug("" + proxy); 
      ServiceItem srvcItem = new ServiceItem(null, proxy, serviceAttrs); 
      ServiceRegistration srvcRegistration = 
             registrar.register(srvcItem, Lease.ANY); 
      debug("Registered ServiceID: " + 
                              srvcRegistration.getServiceID().toString()); 
      return srvcRegistration; 
   } 

Example 10–7 shows the creation of a connector server named server with the environment map env and the service URL jurl. The connector server instance server then gets a pointer to the Jini lookup service by calling the Jini lookup service method LookupLocator.getRegistrar().

The connector server is registered in the Jini lookup service in the form of a proxy, using the Jini lookup service locator registrar and the agent name under which the connector server will be registered. The proxy is in fact a client stub for the connector server, obtained by a call to the toJMXConnector() method of JMXConnectorServer.

The registration itself is performed by a call to the register() method of the Jini lookup service class ServiceRegistrar, with an array of service items.

10.3.2 Looking up the Connector Server with the Jini Lookup Service

The following code extract is taken from the Client class in the Jini lookup service examples directory.


Example 10–8 Looking up the Connector Server with the Jini Lookup Service

public class Client { 
 
   private static boolean debug = false; 
   public static ServiceRegistrar getRegistrar() 
       throws IOException, ClassNotFoundException, MalformedURLException { 
       final String jurl = 
                  System.getProperty("jini.lookup.url","jini://localhost"); 
       final LookupLocator lookup = new LookupLocator(jurl); 
       final ServiceRegistrar registrar = lookup.getRegistrar(); 
       if (registrar instanceof Administrable) 
               debug("Registry is administrable."); 
       return registrar; 
 } 
 
   public static List lookup(ServiceRegistrar registrar, 
           String name) throws IOException { 
       final ArrayList list = new ArrayList(); 
       final Class[] classes = new Class[] {JMXConnector.class}; 
       final Entry[] serviceAttrs = new Entry[] { 
           new net.jini.lookup.entry.Name(name) 
   }; 
    
   ServiceTemplate template = 
        new ServiceTemplate(null,classes,serviceAttrs); 
   ServiceMatches matches = 
        registrar.lookup(template, Integer.MAX_VALUE); 
   for(int i = 0; i < matches.totalMatches; i++) { 
        debug("Found Service: " + matches.items[i].serviceID); 
        if (debug) { 
           if (matches.items[i].attributeSets != null) { 
                   final Entry[] attrs = matches.items[i].attributeSets; 
                   for (int j = 0; j < attrs.length ; j++) { 
                       debug("Attribute["+j+"]=" + attrs[j]); 
               } 
           } 
        } 
 
 
        if(matches.items[i].service != null) { 
            JMXConnector c = (JMXConnector)(matches.items[i].service); 
            debug("Found a JMXConnector: " + c); 
            list.add(c); 
        } 
   } 
   return list; 
} 
 
[...] 

Example 10–8 shows how the connector client obtains a pointer to the Jini lookup service with a call to lookup.getRegistrar(). The client then obtains the list of the connectors registered as entries in the Jini lookup service with the agent name name. Unlike in the SLP example, the agent name you pass to Client when it is launched must be either an exact match of an existing agent name, or null, in which case the Jini lookup service will look up all the agents.

Once the list of connectors has been obtained, in code that is not shown here, the client connects to the MBean server started by Server, and retrieves the list of all the MBeans registered in it.

10.3.3 Running the Jini Lookup Service Example

In addition to the actions you performed in 10.1 Initial Configuration, before you can run the lookup service examples that use the Jini lookup service, you must perform some further initial actions that are specific to this example. You can then start looking up connectors using the Jini network technology, in conjunction with the two connectors supported by Java DMK.

When you run the examples, to help you keep track of which agent has been created with which connector and transport, the agent names include a letter suffix. For example, the agent from the example of an RMI connector over JRMP, without an external directory is called test-server-a.

To Set up the Jini Lookup Service Example

The following steps are required by all of the different connector/transport combinations you can run in this example.

  1. For your convenience when compiling and running the example classes, define some additional environment variables.

    In addition to the common environment variables that you set in 10.1 Initial Configuration you can add the path to the Jini lookup service. The directory where you have installed the Jini networking technology is referred to as jini_dir.


    $ JINI_HOME=jini_dir
    $ JINI_LIB=$JINI_HOME/lib 
    
  2. Define the classp environment variable.

    In addition to the JAR files for the Java DMK runtime, the JMX specification and the JMX Remote API, this example requires the JAR files for the Jini lookup services core and extensions.


    $ classp=$JINI_LIB/jini-core.jar:$JINI_LIB/jini-ext.jar:classpath
    
  3. Create ajava.policy file.

    Thejava.policy file is a Java technology security policy file. A template java.policy file for is provided in the same directory as the classes for this example. You must complete the file to include all the necessary paths for your system. You must also rename the file from java.policy.template to java.policy.

  4. Create a jini.properties file.

    A properties file for UNIX platforms is provided in the same directory as the classes for this example. If you are not running a UNIX platform, you can obtain a properties file for your platform in the following directory:


    jini_dir/example/launcher/jini12_platform.properties 
  5. Update the jini.properties file.

    You must complete the file to include all the necessary paths, host names and port numbers for your system. Even if you are not running a UNIX platform, you can use the template provided as a guide.

  6. Start the Jini networking technology StartService.


    $ java -cp $JINI_LIB/jini-examples.jar 
     com.sun.jini.example.launcher.StartService &
    

    This will open the StartService graphical user interface.

  7. Load your jini.properties file into StartService.

    Click on File, Open Property File and then select your properties file from within examplesDir/current/Lookup/jini.

  8. Start the Jini lookup services.

    Start the required Jini lookup services by clicking on the Run tab and then pressing the START button for each of the following:

    • RMID

    • WebServer

    • Reggie

    • LookupBrowser

  9. Compile the Client and Server classes.


    $ javac -d . -classpath $classp Server.java Client.java
    
To Run the Jini Lookup Service Example With an RMI Connector

This example demonstrates the use of the Jini lookup service to look up RMI connector servers that use RMI's default transport, JRMP, as well as the IIOP transport. In addition, as described in 10.1 Initial Configuration, different external directories are used to register the RMI connector stubs.

The combinations of transports and external directories demonstrated here are:

  1. Start the Server.

    The command you use to start the Server varies according to which external directory you are using. You can start one or more of the following instances of Server with different transports and external registries before starting the Client.

    • RMI connector over JRMP, without an external directory:


      $ java -classpath .:$classp -Ddebug=true \
           -Dagent.name=test-server-a \
           -Durl="service:jmx:rmi://" \
           -Djava.security.policy=java.policy \
           jini.Server & 
      

      In this command:

      • debug is set to true to provide more complete screen output when the Server runs

      • The name of the agent to be created is test-server-a

      • The service URL specifies that the chosen connector is an RMI connector, running over the RMI default transport JRMP.

      When Server is launched, you will see confirmation of the creation of the RMI connector, and the registration of its URL in the Jini lookup service.

    • RMI connector over JRMP, using an RMI registry as an external directory:


      $ java -classpath .:$classp -Ddebug=true \
           -Dagent.name=test-server-b \
           -Durl="service:jmx:rmi:///jndi/${jndirmi}/server" \
           -Djava.security.policy=java.policy \
           jini.Server &
      

      In this command:

      • The name of the agent created is test-server-b

      • The service URL specifies the chosen connector as RMI over JRMP, and the external directory in which the RMI connector stub, server, is stored is the RMI registry you identified as jndirmi in 10.1 Initial Configuration

      When Server is launched, you will see confirmation of the creation of the RMI connector, and the registration of its URL in the Jini lookup service.

    • RMI connector over JRMP, using LDAP as the external directory:


      $ java -classpath .:$classp -Ddebug=true \
           -Dagent.name=test-server-c \
           -Durl="service:jmx:rmi:///jndi/${jndildap}/cn=x,dc=Test" \
           -Djava.security.policy=java.policy \
           -Djava.naming.security.principal="$principal" \
           -Djava.naming.security.credentials="$credentials" \
           jini.Server &
      

      In this command:

      • The name of the agent created is test-server-c

      • The service URL specifies the chosen connector as RMI over JRMP, and the external directory in which the RMI connector stub is stored is the LDAP server you identified as jndildap in 10.1 Initial Configuration

      • The stub is registered in the Test domain component in the LDAP server.

      • The common name attribute principal and password credentials are given to gain access to the LDAP server.

      When Server is launched, you will see confirmation of the creation of the RMI connector, and the registration of its URL in the Jini lookup service under the agent name test-server-c.

    • RMI connector over IIOP, without an external directory:


      $ java -classpath .:$classp -Ddebug=true \
           -Dagent.name=test-server-d \
           -Durl="service:jmx:iiop://" \
           -Djava.security.policy=java.policy \
           jini.Server &
      

      In this command:

      • The name of the agent created is test-server-d

      • The service URL specifies the chosen connector as RMI connector over IIOP.

      When the Server is launched, you will see confirmation of the creation of the RMI connector, and the registration of its automatically generated URL in the Jini lookup service.

    • RMI connector over IIOP, using CORBA naming as the external directory.


      $ java -classpath .:$classp -Ddebug=true \
           -Dagent.name=test-server-e \
           -Durl="service:jmx:iiop:///jndi/${jndiiiop}/server" \
           -Djava.security.policy=java.policy \
           jini.Server &
      

      In this command:

      • The name of the agent created is test-server-e

      • The service URL specifies the chosen connector as RMI connector over IIOP. The external directory in which the RMI connector stub server is stored is the CORBA naming service you identified as jndiiiop in 10.1 Initial Configuration.

    • RMI connector over IIOP, using LDAP as the external directory.


      $ java -classpath .:$classp -Ddebug=true \
           -Dagent.name=test-server-f \
           -Durl="service:jmx:iiop:///jndi/${jndildap}/cn=x,dc=Test" \
           -Djava.security.policy=java.policy \
           -Djava.naming.security.principal="$principal" \
           -Djava.naming.security.credentials="$credentials" \
           jini.Server &
      

      In this command:

      • The name of the agent created is test-server-f

      • The service URL specifies the chosen connector as RMI over IIOP, and the external directory in which the RMI connector stub is stored is the LDAP server you identified as jndildap in 10.1 Initial Configuration.

      • The stub is registered in the Test domain component in the LDAP server.

      • The common name attribute principal and password credentials are given to gain access to the LDAP server.

      When Server is launched, you will see confirmation of the creation of the RMI connector, and the registration of its URL in the Jini lookup service under the agent name test-server-f.

  2. Start the Client.

    After starting the Server using the transport and external directory of your choice, start the Client:


    $ java -classpath .:$classp -Ddebug=true \
         -Djava.security.policy=java.policy \
         jini.Client
    

    You will see output confirming the detection of the agents created by the Server and registered in the lookup service. You will also see the identification and confirmation of the connection made to the agents.

    To look up a specific agent, type the following command:


    $ java -classpath .:$classp -Ddebug=true \
         -Djava.security.policy=java.policy \
         -Dagent.name="agentName" \ 
         jini.Client
    

    In the command shown above, agentName is the name of the agent you want to look up. You can also specify a partial agent name by using *; for example, x* for all agent names beginning with the letter x.

To Run the Jini Lookup Service Example With a JMXMP Connector

This example demonstrates the use of the Jini lookup service to look up JMXMP connector servers.

  1. Start the Server.


    $ java -classpath .:$classp -Ddebug=true \
         -Dagent.name=test-server-g \
         -Durl="service:jmx:jmxmp://" \
         -Djava.security.policy=java.policy \
         jini.Server &
    

    In this command:

    • The name of the agent created is test-server-g

    • The service URL specifies the chosen connector as the JMXMP connector, running on the first available port.

    When the Server is launched, you will see confirmation of the creation of the JMXMP connector, and the registration of its automatically generated URL in the Jini lookup service. JMXMP connector servers can be used alongside RMI connectors, and will be detected by the Client in exactly the same way as RMI connector servers.

  2. Start the Client.

    After starting the Server, start the Client:


    $ java -classpath .:$classp -Ddebug=true \
         -Djava.security.policy=java.policy \
         jini.Client 
    

    You will see output confirming the detection of the agents created by the Server and registered in the lookup service. You will also see the identification and confirmation of the connection made to the agents.

    To look up a specific agent, type the following command:


    $ java -classpath .:$classp -Ddebug=true \  
      -Djava.security.policy=java.policy \
      -Dagent.name="agentName" \ 
      jini.Client 
    

    In the command shown above, agentName is the name of the agent you want to look up. You can also specify a partial agent name by using *; for example, x* for all agent names beginning with the letter x.

10.4 Java Naming and Directory Interface (JNDI) / LDAP Lookup Service

You can register RMI connectors or JMXMP connectors with a JNDI lookup service using an LDAP registry as a backend. This example performs the following operations:

The JNDI/LDAP lookup example is contained in the directory examplesDir/current/Lookup/ldap.


Note –

Some code provided in this example is specific to Sun's implementation of the Java 2 Platform, Standard Edition (J2SE) SDK 1.4. If you are not using Sun's implementation, you might need to adapt the code to your implementation.


10.4.1 Registering the Connector Server with the JNDI/LDAP Lookup Service

The following code extracts are taken from the Server class in the Jini Lookup Service examples directory.


Example 10–9 Creating Connector Servers for Registration in the JNDI/LDAP Lookup Service

public class Server { 
   public final static int JMX_DEFAULT_LEASE = 60; 
   private static boolean debug = false; 
   private final MBeanServer mbs; 
   public Server() { 
      mbs = MBeanServerFactory.createMBeanServer(); 
   } 
 
  public static DirContext getRootContext() throws NamingException { 
      final Hashtable env = new Hashtable(); 
 
      final String factory = 
        System.getProperty(Context.INITIAL_CONTEXT_FACTORY, 
                           "com.sun.jndi.ldap.LdapCtxFactory"); 
      final String ldapServerUrl = 
        System.getProperty(Context.PROVIDER_URL); 
      final String ldapUser = 
        System.getProperty(Context.SECURITY_PRINCIPAL, 
                           "cn=Directory Manager"); 
      final String ldapPasswd = 
        System.getProperty(Context.SECURITY_CREDENTIALS); 
      debug(Context.PROVIDER_URL + "=" + ldapServerUrl); 
      debug(Context.SECURITY_PRINCIPAL + "=" + ldapUser); 
      if (debug) { 
                  System.out.print(Context.SECURITY_CREDENTIALS + "="); 
                  final int len = (ldapPasswd==null)?0:ldapPasswd.length(); 
                  for (int i=0;i
                  for (int i=0;i<len;i++) System.out.print("*"); 
                  System.out.println(); 
      } 
      env.put(Context.INITIAL_CONTEXT_FACTORY,factory); 
      env.put(Context.SECURITY_PRINCIPAL, ldapUser); 
      if (ldapServerUrl != null) 
           env.put(Context.PROVIDER_URL, ldapServerUrl); 
      if (ldapPasswd != null) 
           env.put(Context.SECURITY_CREDENTIALS, ldapPasswd); 
      InitialContext root = new InitialLdapContext(env,null); 
      return (DirContext)(root.lookup("")); 
  } 
   
[...] 

Example 10–9 shows the initial creation of an MBean server mbs, and the obtainment of a pointer to the root context of the LDAP directory tree in which the connector server address is to be registered. All the relevant LDAP access variables, such as the provider URL, the LDAP user name and the security credentials, are given here and passed into the environment map env. The environment map env is then passed as a parameter into a call to InitialLdapContext, from which the initial LDAP context is obtained.

Code that is not shown retrieves the agent name under which the connector will be registered in the LDAP server.


Example 10–10 Registering the Connector Server Address in the LDAP Registry

[...] 
 
public static void register(DirContext root, 
                           JMXServiceURL jmxUrl, 
                           String name) 
   throws NamingException, IOException { 
 
   final String mydn = System.getProperty("dn","cn="+name); 
 
   debug("dn: " + mydn ); 
 
   Object o = null; 
   try { 
       o = root.lookup(mydn); 
   } catch (NameNotFoundException n) { 
       Attributes attrs = new BasicAttributes(); 
       Attribute objclass = new BasicAttribute("objectClass"); 
       objclass.add("top"); 
       objclass.add("javaContainer"); 
       objclass.add("jmxConnector"); 
       attrs.put(objclass); 
       attrs.put("jmxAgentName", name); 
       o = root.createSubcontext(mydn,attrs); 
   } 
   if (o == null) throw new NameNotFoundException(); 
   final Attributes attrs = root.getAttributes(mydn); 
   final Attribute oc = attrs.get("objectClass"); 
   if (!oc.contains("jmxConnector")) { 
       final String msg = "The supplied node [" + mydn +  
         "] does not contain the jmxConnector objectclass"; 
       throw new NamingException(msg); 
   } 
    
   final Attributes newattrs = new BasicAttributes(); 
   newattrs.put("jmxAgentName",name); 
   newattrs.put("jmxServiceURL",jmxUrl.toString()); 
   newattrs.put("jmxAgentHost",InetAddress.getLocalHost().getHostName()); 
   newattrs.put("jmxProtocolType",jmxUrl.getProtocol()); 
   newattrs.put("jmxExpirationDate", 
                getExpirationDate(JMX_DEFAULT_LEASE)); 
   root.modifyAttributes(mydn,DirContext.REPLACE_ATTRIBUTE,newattrs); 
} 
 
[...] 

Example 10–10 shows the registration of the JMX connector server service URL in the LDAP directory. The domain name in which the URL is registered can be passed on the command line through the dn System property, namely, -Ddn=mydn. If the dn system property is not specified, then you can use cn=name, in which name is the agent name. This is not mandatory, however.

The location in which the URL is registered is not important, because the client code never uses that DN directly, but instead performs an LDAP search to find the nodes that have an auxiliary JMX connector ObjectClass. What is important however, is that each URL is registered in its own LDAP node. How to name these nodes is left to the LDAP administrator. In this example, it is assumed that you have configured your LDAP server by creating a root context under which the node cn=name can be created, and that this root context has been passed to the LDAP initial context through the Context.PROVIDER_URL property (see Example 10–9).

The code shown above checks whether the node in which you will register the server URL already exists. If it does not, you try to create it (this will fail if the parent node does not exist). Since the ObjectClass is a simple auxiliary class, you can use the javaContainer ObjectClass as structural class if you need to create a new context. Once again, this is optional.

Any structural class to which the jmxConnector auxiliary class can be added is acceptable. It then checks whether the node in which you will register the server already has the jmxConnector auxiliary class. Otherwise, an exception is thrown.

At this point you are sure that the node in which you will register the URL exists, and has the appropriate jmxConnector auxiliary class. So you need only to replace the values of the attributes defined by the LDAP schema for JMX, (see jmx-schema.txt):


Example 10–11 Registering the Connector Servers in the LDAP Server

[...] 
 
   public JMXConnectorServer rmi(String url) 
     throws IOException, JMException, 
        NamingException, ClassNotFoundException { 
 
     JMXServiceURL jurl = new JMXServiceURL(url); 
     final HashMap env = new HashMap(); 
     // Prepare the environment Map 
     
[...] 
 
     JMXConnectorServer rmis = 
        JMXConnectorServerFactory.newJMXConnectorServer(jurl, env, mbs); 
 
     final String agentName = System.getProperty("agent.name", 
                                                 "DefaultAgent"); 
     start(rmis,env,agentName); 
     return rmis; 
   } 
     
[...] 

In Example 10–11, a new RMI connector server named rmis is created with the JMX service URL jurl and the appropriate LDAP properties passed to its environment map env. The connector server rmis is launched by calling JMXConnectorServer.start() and is registered in the LDAP server.

Subsequent code not shown here creates and registers a corresponding JMXMP connector server named jmxmp.


Example 10–12 Creating the JMX Connector Server

[...] 
 
    public void start(JMXConnectorServer server, Map env, String agentName) 
       throws IOException, NamingException { 
       server.start(); 
       final DirContext root=getRootContext(); 
       final JMXServiceURL address = server.getAddress(); 
       register(root,address,agentName); 
    } 
     
[...] 

Example 10–12 shows the creation of a JMX connector server server, the obtainment of a pointer to the LDAP server root directory root and the creation of a URL for server named address. The root directory, the URL and an agent name are passed as parameters to register() and are registered in the LDAP server.

10.4.2 Looking up the Connector Servers with the JNDI/LDAP Lookup Service

The following code extract is taken from the Client class in the Jini Lookup Service examples directory.


Example 10–13 Looking up the Connector Servers with the JNDI/LDAP Lookup Service

[...] 
 
public class Client { 
 
   private static boolean debug = false; 
 
   public static void listAttributes(DirContext root, String dn) 
     throws NamingException { 
     final Attributes attrs = root.getAttributes(dn); 
     System.out.println("dn: " + dn); 
     System.out.println("attributes: " + attrs); 
   } 
    
   public static DirContext getRootContext() throws NamingException { 
      final Hashtable env = new Hashtable(); 
      // Prepare environment map 
      [...] 
       
      InitialContext root = new InitialLdapContext(env,null); 
      return (DirContext)(root.lookup("")); 
   } 
    
   // Confirm URL has not expired  
  [...]  
 
   public static List lookup(DirContext root, String protocolType, 
                             String name) 
      throws IOException, NamingException { 
      final ArrayList list = new ArrayList(); 
      String queryProtocol = 
         (protocolType==null)?"":"(jmxProtocolType="+protocolType+")"; 
      String query = 
          "(&" + "(objectClass=jmxConnector) " + 
          "(jmxServiceURL=*) " + 
          queryProtocol + 
          "(jmxAgentName=" + ((name!=null)?name:"*") + "))"; 
 
      SearchControls ctrls = new SearchControls(); 
      ctrls.setSearchScope(SearchControls.SUBTREE_SCOPE); 
      final NamingEnumeration results = root.search("", query, ctrls); 
      while (results.hasMore()) {  
           final SearchResult r = (SearchResult) results.nextElement(); 
           debug("Found node: " + r.getName()); 
           final Attributes attrs = r.getAttributes(); 
           final Attribute attr = attrs.get("jmxServiceURL"); 
           if (attr == null) continue; 
           final Attribute exp = attrs.get("jmxExpirationDate"); 
           if ((exp != null) && hasExpired((String)exp.get())) { 
               System.out.print(r.getName() + ": "); 
               System.out.println("URL expired since: " + exp.get()); 
               continue; 
           }   
           final String urlStr = (String)attr.get(); 
           if (urlStr.length() == 0) continue; 
 
    
           debug("Found URL: "+ urlStr); 
 
    
           final JMXServiceURL url = new JMXServiceURL(urlStr); 
           final JMXConnector conn = 
               JMXConnectorFactory.newJMXConnector(url,null); 
           list.add(conn); 
           if (debug) listAttributes(root,r.getName()); 
      } 
 
      return list; 
 
   }   
} 
 

In Example 10–13, the Client firstly returns a pointer root to the LDAP directory DirContext, then it searches through the directory for object classes of the type jmxConnector. The service URL and expiry date attributes, attr and exp respectively, for the jmxConnector object classes are obtained, exp is checked to make sure that the URL has not expired and a call is made to JMXConnectorFactory to create a new connector conn. The connector conn is added to the list of connectors and is used to access the MBeans in the MBean server created by Server.

10.4.3 Running the JNDI/LDAP Lookup Service Example

In addition to the actions you performed in 10.1 Initial Configuration, before you can run the lookup service examples that use the JNDI/LDAP lookup service, you must perform some further initial actions that are specific to this example. You can then start looking up connectors using the JNDI/LDAP network technology.

When you run the examples, to help you keep track of which agent has been created with which connector and transport, the agent names include a letter suffix. For example, the agent from the example of an RMI connector over JRMP, without an external directory is called test-server-a.

To Set up the JNDI/LDAP Lookup Service Example

The following steps are required by all of the different connector/transport combinations you can run in this example.

  1. Stop the LDAP server you started in 10.1 Initial Configuration.

    Do this according to the type of LDAP server you are using.

  2. Copy the JMX schema into your LDAP server's schema directory.

    For example, if you are using Sun ONE Directory Server 5.0, you would type:


    $ cp 60jmx-schema.ldif /var/ds5/slapd-hostname/config/schema
    

    Otherwise, do this according to the type of LDAP server you are using.

  3. Restart the LDAP server.

    Do this according to the type of LDAP server you are using.

  4. Define the root under which the Server will register its service URL.

    You must provide the Server with the path to the domain component suffix dc=Test that you created in 10.1 Initial Configuration.


    $ provider="ldap://$ldaphost:$ldapport/dc=Test" 
    
  5. Compile the Client and Server classes.


    $ javac -d . -classpath classpath Server.java Client.java
    
To Run the JNDI/LDAP Lookup Service Example with an RMI Connector

This example demonstrates the use of the JNDI/LDAP lookup service to look up RMI connector servers that use RMI's default transport, JRMP, as well as the IIOP transport. In addition, as described in 10.1 Initial Configuration, different external directories are used to register the RMI connector stubs.

The combinations of transports and external directories demonstrated here are:

Perform the following steps to run the example:

  1. Start the Server.

    The command you use to start the Server varies according to which external directory you are using. You can start one or more of the following instances of Server with different transports and external registries before starting the Client.

    • RMI connector over JRMP, without an external directory:


      $ java -classpath classpath -Ddebug=true \
           -Dagent.name=test-server-a \
           -Durl="service:jmx:rmi://" \
           -Djava.naming.provider.url="$provider" \
           -Djava.naming.security.principal="$principal" \
           -Djava.naming.security.credentials="$credentials" \
           jndi.Server & 
      

      In this command:

      • debug is set to true to provide more complete screen output when the Server runs

      • The name of the agent to be created is test-server-a

      • The service URL specifies that the chosen connector is an RMI connector, running over the RMI default transport JRMP.

      When Server is launched, you will see confirmation of the creation of the RMI connector, and the registration of its URL in the JNDI/LDAP lookup service.

    • RMI connector over JRMP, using an RMI registry as an external directory:


      $ java -classpath classpath -Ddebug=true \
           -Dagent.name=test-server-b \
           -Durl="service:jmx:rmi:///jndi/${jndirmi}/server" \
           -Djava.naming.provider.url="$provider" \
           -Djava.naming.security.principal="$principal" \
           -Djava.naming.security.credentials="$credentials" \
           jndi.Server &
      

      In this command:

      • The name of the agent created is test-server-b

      • The service URL specifies the chosen connector as RMI over JRMP, and the external directory in which the RMI connector stub, server, is stored is the RMI registry you identified as jndirmi in 10.1 Initial Configuration

      When Server is launched, you will see confirmation of the creation of the RMI connector, and the registration of its URL in the JNDI/LDAP lookup service.

    • RMI connector over JRMP, using LDAP as the external directory:


      $ java -classpath classpath -Ddebug=true \
           -Dagent.name=test-server-c \
           -Durl="service:jmx:rmi:///jndi/${jndildap}/cn=x,dc=Test" \
           -Djava.naming.provider.url="$provider" \
           -Djava.naming.security.principal="$principal" \
           -Djava.naming.security.credentials="$credentials" \
           jndi.Server &
      

      In this command:

      • The name of the agent created is test-server-c

      • The service URL specifies the chosen connector as RMI over JRMP, and the external directory in which the RMI connector stub is stored is the LDAP server you identified as jndildap in 10.1 Initial Configuration

      • The stub is registered in the Test domain component in the LDAP server.

      • The common name attribute principal and password credentials are given to gain access to the LDAP server.

      When Server is launched, you will see confirmation of the creation of the RMI connector, and the registration of its URL in the JNDI/LDAP lookup service under the agent name test-server-c.

    • RMI connector over IIOP, without an external directory:


      $ java -classpath classpath -Ddebug=true \
           -Dagent.name=test-server-d \
           -Durl="service:jmx:iiop://" \
           -Djava.naming.provider.url="$provider" \
           -Djava.naming.security.principal="$principal" \
           -Djava.naming.security.credentials="$credentials" \
           jndi.Server &
      

      In this command:

      • The name of the agent created is test-server-d

      • The service URL specifies the chosen connector as RMI connector over IIOP.

      When the Server is launched, you will see confirmation of the creation of the RMI connector, and the registration of its automatically generated URL in the JNDI/LDAP lookup service.

    • RMI connector over IIOP, using CORBA naming as the external directory.


      $ java -classpath classpath -Ddebug=true \
           -Dagent.name=test-server-e \
           -Durl="service:jmx:iiop:///jndi/${jndiiiop}/server" \
           -Djava.naming.provider.url="$provider" \
           -Djava.naming.security.principal="$principal" \
           -Djava.naming.security.credentials="$credentials" \
           jndi.Server &
      

      In this command:

      • The name of the agent created is test-server-e

      • The service URL specifies the chosen connector as RMI connector over IIOP. The external directory in which the RMI connector stub server is stored is the CORBA naming service you identified as jndiiiop in 10.1 Initial Configuration.

    • RMI connector over IIOP, using LDAP as the external directory.


      $ java -classpath classpath -Ddebug=true \
           -Dagent.name=test-server-f \
           -Durl="service:jmx:iiop:///jndi/${jndildap}/cn=x,dc=Test" \
           -Djava.naming.provider.url="$provider" \
           -Djava.naming.security.principal="$principal" \
           -Djava.naming.security.credentials="$credentials" \
           jndi.Server &
      

      In this command:

      • The name of the agent created is test-server-f

      • The service URL specifies the chosen connector as RMI over IIOP, and the external directory in which the RMI connector stub is stored is the LDAP server you identified as jndildap in 10.1 Initial Configuration.

      • The stub is registered in the Test domain component in the LDAP server.

      • The common name attribute principal and password credentials are given to gain access to the LDAP server.

      When Server is launched, you will see confirmation of the creation of the RMI connector, and the registration of its URL in the JNDI/LDAP lookup service under the agent name test-server-f.

  2. Start the Client.

    After starting the Server using the transport and external directory of your choice, start the Client:


    $ java -classpath classpath -Ddebug=true \
         -Djava.naming.provider.url="$provider" \
         -Djava.naming.security.principal="$principal" \
         -Djava.naming.security.credentials="$credentials" \
         jndi.Client
    

    You will see output confirming the detection of the agents created by the Server and registered in the lookup service. You will also see the identification and confirmation of the connection made to the agents.

    To look up a specific agent, type the following command:


    $ java -classpath classpath -Ddebug=true \
         -Djava.naming.provider.url="$provider" \
         -Djava.naming.security.principal="$principal" \
         -Djava.naming.security.credentials="$credentials" \
         -Dagent.name=agentName \
         jndi.Client
    

    In the command shown above, agentName is the name of the agent you want to look up. You can also specify a partial agent name by using *; for example, x* for all agent names beginning with the letter x.

To Run the JNDI/LDAP Lookup Service Example with a JMXMP Connector

This example demonstrates the use of the JNDI/LDAP lookup service to look up JMXMP connector servers.

  1. Start the Server.


    $ java -classpath classpath -Ddebug=true \
         -Dagent.name=test-server-g \
         -Durl="service:jmx:jmxmp://" \
         -Djava.naming.provider.url="$provider" \
         -Djava.naming.security.principal="$principal" \
         -Djava.naming.security.credentials="$credentials" \
         jndi.Server &
    

    In this command:

    • The name of the agent created is test-server-g

    • The service URL specifies the chosen connector as the JMXMP connector, running on the first available port.

    When the Server is launched, you will see confirmation of the creation of the JMXMP connector, and the registration of its automatically generated URL in the JNDI/LDAP lookup service. JMXMP connector servers can be used alongside RMI connectors, and will be detected by the Client in exactly the same way as RMI connector servers.

  2. Start the Client.

    After starting the Server, start the Client:


    $ $ java -classpath classpath -Ddebug=true \
         -Djava.naming.provider.url="$provider" \
         -Djava.naming.security.principal="$principal" \
         -Djava.naming.security.credentials="$credentials" \
         jndi.Client 
    

    You will see output confirming the detection of the agents created by the Server and registered in the JNDI/LDAP lookup service. You will also see the identification and confirmation of the connection made to the agents.

    To look up a specific agent, type the following command:


    $ $ java -classpath classpath -Ddebug=true \
         -Djava.naming.provider.url="$provider" \
         -Djava.naming.security.principal="$principal" \
         -Djava.naming.security.credentials="$credentials" \
         -Dagent.name="agentName" \ 
         jndi.Client 
      
    

    In the command shown above, agentName is the name of the agent you want to look up. You can also specify a partial agent name by using *; for example, x* for all agent names beginning with the letter x.

10.5 Running the Lookup Examples Over Microsoft Active Directory

All the examples presented in this chapter can be run using the Microsoft Active Directory service. For details of how to configure Active Directory for use with these examples, consult the following file.


examplesDir/current/Lookup/msad/README

You will find the required utilities to configure Active Directory in same the same directory as the README file.

Chapter 11 Connector Security

Whenever considering a distributed architecture, security issues are often an added factor in the complexity of the design. This is not the case with the Java Dynamic Management Kit (Java DMK), whose security features are built into the modularity of the components.

Management solutions can evolve from basic password-based protection all the way to secure connections using cryptography simply by adding components to your connector protocols. The rest of the architecture is unchanged because it relies on the interface that is common to all connectors.

The code samples in this chapter are taken from the files in the current/Security example directory located in the main examplesDir (see “Directories and Classpath” in the Preface).

This chapter covers the following topics:


Note –

Most of the examples in this chapter implement a SASL mechanism. For more information about SASL, see the The Java SASL API Programming and Deployment Guide at the following URL.

http://java.sun.com/j2se/1.5.0/docs/guide/security/sasl/sasl-refguide.html


11.1 Simple Security

The simplest type of connector security you can use with the Java DMK is based upon encryption, user name and password authentication, and file access control. The exact implementation of these security features depends on whether you are using an RMI or a JMXMP connector, as explained in the following sections. The examples make use of the secure sockets layer (SSL) RMI socket factories and the SASL classes that are provided with Java DMK.

11.1.1 RMI Connectors With Simple Security

You can find an example of an RMI connector with simple security in the directory examplesDir/current/Security/rmi/simple. The properties files access.properties and password.properties are found in the config directory inside examplesDir/current/Security/rmi/simple.

The RMI connector server is created by the Server class, shown below.


Example 11–1 Creating an RMI Connector Server with Simple Security

public class Server { 
 
  public static void main(String[] args) { 
  try { 
       MBeanServer mbs = MBeanServerFactory.createMBeanServer(); 
 
       HashMap env = new HashMap(); 
 
       SslRMIClientSocketFactory  csf =  
                  new SslRMIClientSocketFactory(); 
       SslRMIServerSocketFactory ssf =  
                  new SslRMIServerSocketFactory(); 
       env.put(RMIConnectorServer. 
                  RMI_CLIENT_SOCKET_FACTORY_ATTRIBUTE,csf); 
       env.put(RMIConnectorServer. 
                  RMI_SERVER_SOCKET_FACTORY_ATTRIBUTE,ssf); 
 
       env.put("jmx.remote.x.password.file", 
                 "config" + File.separator + "password.properties"); 
       env.put("jmx.remote.x.access.file", 
                 "config" + File.separator + "access.properties"); 
 
       JMXServiceURL url = new JMXServiceURL( 
        "service:jmx:rmi:///jndi/rmi://localhost:9999/server"); 
         JMXConnectorServer cs = 
            JMXConnectorServerFactory.newJMXConnectorServer(url,  
                                                            env,  
                                                            mbs); 
       cs.start(); 
     } catch (Exception e) { 
       e.printStackTrace(); 
     } 
  } 
} 

The Server class shown in Example 11–1 creates an MBean server mbs, and populates an environment map env with a secure RMI client socket factory csf, a secure RMI server socket factory ssf, and the properties files password.properties and access.properties.

The properties file password.properties contains a username and password and is accessed using the JMXAuthenticator interface. Using the property jmx.remote.x.password.file is the same as creating a password-based JMXAuthenticator and passing it into the environment map through the jmx.remote.authenticator property.

The properties file access.properties contains a username and a level of access permission that can be either readwrite or readonly. This represents the level of access this user can have to MBean server operations. This file-based access control is implemented using the interface MBeanServerForwarder, which wraps the real MBean server inside an access controller MBean server. The access controller MBean server only forwards requests to the real MBean server after performing the appropriate checks.

Because the connector uses SSL, when you start the Server class, you will have to provide it with a pointer to the SSL keystore file and provide the password expected by the SSL RMI server socket factory ssf. This is shown in Example 11–2 below.

Server creates a service URL, named url, for an RMI connector that will operate over the default JRMP transport, and register an RMI connector stub in an RMI registry on port 9999 of the local host.

The MBean server mbs, the environment map env and the service URL url are all passed to JMXConnectorServer to create a new, secure connector server named cs.


Example 11–2 Creating an RMI Connector Client with Simple Security

public class Client { 
 
  public static void main(String[] args) { 
  try { 
      HashMap env = new HashMap(); 
 
      String[] credentials = new String[] { "username" , "password" }; 
      env.put("jmx.remote.credentials", credentials); 
      JMXServiceURL url = new JMXServiceURL( 
         "service:jmx:rmi:///jndi/rmi://localhost:9999/server");       
      JMXConnector jmxc = JMXConnectorFactory.connect(url, env); 
      MBeanServerConnection mbsc = jmxc.getMBeanServerConnection(); 
      String domains[] = mbsc.getDomains(); 
      for (int i = 0; i < domains.length; i++) { 
         System.out.println("Domain[" + i + "] = " + domains[i]); 
      } 
       
      ObjectName mbeanName =  
          new ObjectName("MBeans:type=SimpleStandard"); 
      mbsc.createMBean("SimpleStandard", mbeanName, null, null); 
      // Perform MBean operations 
      [...] 
      
      mbsc.removeNotificationListener(mbeanName, listener); 
      mbsc.unregisterMBean(mbeanName); 
      jmxc.close(); 
    }  catch (Exception e) { 
      e.printStackTrace(); 
    } 
  } 
} 
 

The Client class shown in Example 11–2 populates an environment map env with a set of credentials, namely the username and password expected by the Server. These credentials are then given to an instance of JMXConnector named jmxc when the service URL of the connector stub and the environment map are passed to JMXConnectorFactory.connect(). Through jmxc, the Client connects to the MBean server started by Server, and performs operations on the MBean SimpleStandard.

When the connection is established, the credentials supplied in the environment map env are sent to the server. The server then calls the authenticate() method of the JMXAuthenticator interface, passing the client credentials as parameters. The authenticate() method authenticates the client and returns a subject that contains the set of principals upon which the access control checks will be performed.

Because the connector uses SSL, when you start the Client class, you will have to provide it with a pointer to the SSL truststore file and provide the password, trustword, that is expected by the SSL RMI server socket factory ssf. This is shown in To Run the RMI Connector Example With Simple Security below.

To Run the RMI Connector Example With Simple Security

Run this example from within the examplesDir/current/Security/rmi/simple directory. Before running the example, make sure your classpath contains the runtime library for SSL RMI (see Directories and Classpath).

  1. Compile the example classes.


    $ javac -classpath classpath \
          mbeans/SimpleStandard.java \
          mbeans/SimpleStandardMBean.java \
          server/Server.java \
          client/Client.java \
          client/ClientListener.java
    
  2. Start an RMI registry on port 9999 of the local host.


    $ export CLASSPATH=server:classpath ; rmiregistry 9999 &
    
  3. Start the Server.

    You have to provide the SSL keystore and its password when you start the Server


    $ java -classpath server:mbeans:classpath \
         -Djavax.net.ssl.keyStore=config/keystore \
         -Djavax.net.ssl.keyStorePassword=password \
         Server &  
    

    You will see confirmation of the creation of the MBean server and of the RMI connector.

  4. Start the Client.

    The Client requires the SSL truststore and its password when it is launched.


    $ java -classpath client:server:mbeans:classpath \
         -Djavax.net.ssl.trustStore=config/truststore \
         -Djavax.net.ssl.trustStorePassword=trustword \
         Client 
    

    You will see confirmation of the creation of the connector client, the various MBean operations and finally the closure of the connection.

    As you can see, all the above appears to proceed in exactly the same manner as the basic RMI connector example shown in Chapter 9, Protocol Connectors. However, if you were to open password.properties and change the password, you would see a java.lang.SecurityException when you launched the Client, and the connection would fail.

11.1.2 JMXMP Connectors With Simple Security

You can find an example of a JMXMP connector with simple security in the directory examplesDir/current/Security/jmxmp/simple. JMXMP connectors can be secured using Java DMK's own implementation of the Simple Authentication and Security Layer (SASL), which provides a greater level of security than the SSL security implemented by the RMI connector.


Example 11–3 Creating a JMXMP Connector Server with Simple Security

public class Server { 
 
    public static void main(String[] args) { 
      try { 
           MBeanServer mbs = MBeanServerFactory.createMBeanServer(); 
           HashMap env = new HashMap(); 
          	     
           Security.addProvider(
                 new com.sun.jdmk.security.sasl.Provider()); 
           env.put("jmx.remote.profiles", "TLS SASL/PLAIN"); 
           env.put("jmx.remote.sasl.callback.handler", 
               new PropertiesFileCallbackHandler("config" +  
                                               File.separator +  
                                               "password.properties")); 
           env.put("jmx.remote.x.access.file", 
                 "config" + File.separator + "access.properties"); 
 
           JMXServiceURL url = new JMXServiceURL("jmxmp", null, 5555); 
           JMXConnectorServer cs = 
              JMXConnectorServerFactory.newJMXConnectorServer(url, 
                                                              env, 
                                                              mbs); 
           cs.start(); 
 
         } catch (Exception e) { 
           e.printStackTrace(); 
         } 
      } 
  } 
 

Example 11–3 shows the creation of an MBean server mbs, and an environment map env. A security provider is defined by a call to the addProvider() method of the Security class, implementing server support for the PLAIN SASL mechanism to Java DMK.

The environment map env is populated with the TLS and PLAIN SASL profiles. A callback handler, an instance of the PropertiesFileCallbackHandler class, is also passed to the environment map. The PropertiesFileCallbackHandler class is an implementation of the CallbackHandler interface, and is used by Server to check that the user name and password supplied by the client against the user name and password supplied in the password.properties file.

Finally, the access properties file, access.properties, is passed into the environment map to define the names of the users who will be authorized to access the connector server, and their level of access.

When the security provider and the environment map have been configured, a service URL, named url, is created. This URL is defined by the constructor JMXServiceURL, with its three arguments specifying JMXMP as the connector protocol, a null host name, and port number 5555 as the port upon which the connector server will listen for connections. The URL is then provided, along with the environment map env, to create an instance of JMXConnectorServer, named cs. The connector server cs is started by calling the start() method of JMXConnectorServer.


Example 11–4 Creating a JMXMP Connector Client with Simple Security

public class Client { 
 
   public static void main(String[] args) { 
     try { 
          HashMap env = new HashMap(); 
          Security.addProvider(new com.sun.security.sasl.Provider()); 
          env.put("jmx.remote.profiles", "TLS SASL/PLAIN"); 
          env.put("jmx.remote.sasl.callback.handler", 
             new UserPasswordCallbackHandler("username", "password")); 

          JMXServiceURL url = new JMXServiceURL("jmxmp", null, 5555); 
          JMXConnector jmxc = JMXConnectorFactory.connect(url, env); 
          MBeanServerConnection mbsc = jmxc.getMBeanServerConnection(); 
          String domains[] = mbsc.getDomains(); 
          for (int i = 0; i < domains.length; i++) { 
          System.out.println("Domain[" + i + "] = " + domains[i]); 
          } 
 
          ObjectName mbeanName = 
              new ObjectName("MBeans:type=SimpleStandard"); 
          mbsc.createMBean("SimpleStandard", mbeanName, null, null); 
          // Perform MBean operations 
          // 
          [...] 
          mbsc.removeNotificationListener(mbeanName, listener); 
          mbsc.unregisterMBean(mbeanName); 
          jmxc.close(); 
        } catch (Exception e) { 
          e.printStackTrace(); 
        } 
     } 
 } 

In Example 11–4, we see the creation of an environment map env, and a security provider is defined by a call to the addProvider() method of the Security class, implementing the client support for the PLAIN SASL mechanism.

A callback handler, an instance of the UserPasswordCallbackHandler class, is passed to the environment map for use by the PLAIN SASL client mechanism to retrieve the user name and password of the remote user connecting to the server.

Once the security provider and the environment map have been configured, a service URL named url is created. This URL defines JMXMP as the connector protocol, and port number 5555 as the port upon which the connector server will make connections. The URL is then provided, along with the environment map env, to create an instance of JMXConnector, named jmxc. An MBean server connection, mbsc, is made by calling the getMBeanServerConnection of the connector jmxc.

In code that is not shown here, when the secure connection to the MBean server has been established, the Client creates an MBean called SimpleStandard and performs various operations on it. Once these MBean operations have completed, the Client unregisters the MBean, and closes down the connection jmxc.

To Run the JMXMP Connector Example with Simple Security

Run this example from within the examplesDir/current/Security/jmxmp/simple directory. Before running the example, make sure your classpath contains the runtime library for SASL (see Directories and Classpath).

  1. Compile the example classes.


    $ javac -classpath classpath \
          mbeans/SimpleStandard.java \
          mbeans/SimpleStandardMBean.java \
          server/Server.java \
          server/PropertiesFileCallbackHandler.java \
          client/Client.java \
          client/ClientListener.java \
          client/UserPasswordCallbackHandler.java
    
  2. Start the Server:

    The Server requires the SSL keystore file and its password when you launch it.


    $ java -classpath server:mbeans:classpath \
         -Djavax.net.ssl.keyStore=config/keystore \
         -Djavax.net.ssl.keyStorePassword=password \
         Server &
    

    You will see confirmation of the creation of the MBean server, the initialization of the environment map and the launching of the JMXMP connector.

  3. Start the Client:

    The Client requires the SSL truststore and its password when it is launched.


    $ java -classpath client:mbeans:classpath \
         -Djavax.net.ssl.trustStore=config/truststore \
         -Djavax.net.ssl.trustStorePassword=trustword \
         Client
    

    You will see confirmation of the creation of the JMXMP connector client, the initialization of the environment map, the connection to the MBean server and the performance of the various MBean operations followed by the closure of the connection.

    As you can see, all the above appears to proceed in exactly the same manner as the basic JMXMP connector example shown in Chapter 9, Protocol Connectors. However, if you were to open access.properties and change the access to readonly rather than readwrite, you would see a java.lang.SecurityException when you launched the Client, and the connection would fail.

11.2 Subject Delegation

If your implementation requires the client end of the connection to perform different operations on behalf of multiple users or applications, if you were to use the security mechanisms demonstrated in 11.1 Simple Security, each different user would require one secure connection for every operation it performs. If you expect your connector clients to interact with numerous users, you can reduce the load on your system by implementing subject delegation. Subject delegation establishes a single secure connection for a user, and this connection can be used to perform related operations on behalf of any number of users. The connection itself is made by an authenticated user. If the authenticated user has been granted a SubjectDelegationPermission that allows it to act on behalf of another user, then operations can be performed over the connection on behalf of that user.

11.2.1 Secure RMI Connectors With Subject Delegation

You can find an example of a secure RMI connector that implements subject delegation in the directory examplesDir/current/Security/rmi/subject_delegation.

The Server class used in this example is identical to the one used in 11.1 Simple Security. However, unlike the simple security example, this example implements a java.policy file to grant permission to certain users to perform certain actions.


Example 11–5 A java.policy File

grant codeBase "file:installDir/lib/jmxremote.jar" {
    permission javax.management.remote.SubjectDelegationPermission 
             "javax.management.remote.JMXPrincipal.delegate";
};

grant codeBase "file:server" {
    permission javax.management.remote.SubjectDelegationPermission 
              "javax.management.remote.JMXPrincipal.delegate";
};

grant principal javax.management.remote.JMXPrincipal "username" {
     permission javax.management.remote.SubjectDelegationPermission 
              "javax.management.remote.JMXPrincipal.delegate";
};

The java.policy file grants a user named username a SubjectDelegationPermission so it can perform operations on behalf of the user delegate, an instance of JMXPrincipal created by the Client class. The java.policy file is required when launching the Server class. The creation of the JMXPrincipal instance by the Client is shown below.


Example 11–6 Creating a Delegation Subject

[...]

JMXConnector jmxc = JMXConnectorFactory.connect(url, env); 
Subject delegationSubject = 
            new Subject(true, 
                Collections.singleton(new JMXPrincipal("delegate")), 
                Collections.EMPTY_SET, 
                Collections.EMPTY_SET); 
 
MBeanServerConnection mbsc = 
          jmxc.getMBeanServerConnection(delegationSubject); 
[...]

The Client class is identical to the one used in the simple security example, except for the creation of the delegation subject shown in Example 11–6.

As before, the Client creates an environment map env that is populated with a user name username and a password password. These strings match the user name and password stored in the password.properties file that is held by the Server to authenticate users accessing the connector server.

A connector client jmxc is created in the same way as in the previous RMI connector examples, with the user name and password passed into the environment map env.

The Client then creates an instance of Subject, called delegationSubject, with a Principal that is an instance of JMXPrincipal, named delegate.

An MBean server connection, named mbsc, is created by calling the getMBeanServerConnection() method of JMXConnector, with delegationSubject passed in as a parameter. This MBean server connection therefore allows operations to be performed on the remote MBean server on behalf of the principals stored in the delegationSubject, which in this example is the JMXPrincipal named delegate.

The example continues by creating and registering the SimpleStandard MBean in the MBean server, and performing operations on it, in exactly the same way as in the previous examples.

To Run the Secure RMI Connector Example With Subject Delegation

Run this example from within the examplesDir/current/Security/rmi/subject_delegation directory.

  1. Compile the example classes.


    $ javac -classpath classpath \ 
      mbeans/SimpleStandard.java \ 
      mbeans/SimpleStandardMBean.java \ 
      server/Server.java \ 
      client/Client.java \ 
      client/ClientListener.java 
    
  2. Start an RMI registry on port 9999 of the local host.


    $ export CLASSPATH=server:classpath ; rmiregistry 9999 &
    
  3. Create a java.policy file from the java.policy.template file in the config directory.

    You must replace @INSTALL_HOME_FOR_JDMK@ with your installDir.

  4. Start the Server.

    As was the case with the simple secure RMI connector example, you have to provide the SSL keystore and its password when you start the Server, as well as the a pointer to the java.policy that grants permission to the delegate subject.


    $ java -classpath server:mbeans:classpath \
         -Djavax.net.ssl.keyStore=config/keystore \
         -Djavax.net.ssl.keyStorePassword=password \
         -Djava.security.policy=config/java.policy Server &
    

    You will see confirmation of the creation of the MBean server, the initialization of the environment map, the creation of the RMI connector, and the registration of the connector in the MBean server.

  5. Start the Client.

    Again, the Client requires the SSL truststore and its password when it is launched.


    $ java -classpath client:server:mbeans:classpath \
         -Djavax.net.ssl.trustStore=config/truststore \
         -Djavax.net.ssl.trustStorePassword=trustword \
         Client
    

    You will see confirmation of the creation of the connector client, the creation of the delegation subject, the connection to the MBean server and the various MBean operations followed by the closure of the connection.

11.2.2 Secure JMXMP Connectors With Subject Delegation

The example of JMXMP connectors with subject delegation is mostly identical to the example of a simple secure JMXMP connector. The only differences are in the client end of the connection, in which the delegation subject is defined, and in the java.policy file used to grant permission to the delegation subjects created.

In this example, the Server class creates an MBean server, and a connector server cs, again protected by an SSL password, as was the case in the simple secure JMXMP connector example.

The Client creates a connector client named jmxc in the same way as in the previous JMXMP connector examples. The Client then creates an instance of Subject, called delegationSubject, with a Principal that is an instance of JMXPrincipal, named delegate.

An MBean server connection, named mbsc, is created by calling the getMBeanServerConnection() method of JMXConnector, with delegationSubject passed in as a parameter. This MBean server connection therefore allows operations to be performed on the remote MBean server on behalf of the principals stored in the delegationSubject, which in this example is the JMXPrincipal named delegate.

To Run the Secure JMXMP Connector Example With Subject Delegation

Run this example from within the examplesDir/current/Security/jmxmp/subject_delegation directory.

  1. Compile the example classes.


    $ javac -classpath classpath \
          mbeans/SimpleStandard.java \
          mbeans/SimpleStandardMBean.java \
          server/Server.java \
          server/PropertiesFileCallbackHandler.java \
          client/Client.java \
          client/ClientListener.java \
          client/UserPasswordCallbackHandler.java
    
  2. Create a java.policy file from the java.policy.template file in the config directory.

    You must replace @INSTALL_HOME_FOR_JDMK@ with your installDir.

  3. Start the Server.

    The Server requires the SSL keystore file and its password, and a pointer to the java.policy file when you launch it.


    $ java -classpath server:mbeans:classpath \
         -Djavax.net.ssl.keyStore=config/keystore \
         -Djavax.net.ssl.keyStorePassword=password \
         -Djava.security.policy=config/java.policy Server &
    

    You will see confirmation of the creation of the MBean server, the initialization of the environment map and the launching of the JMXMP connector.

  4. Start the Client.

    Again, the Client requires the SSL truststore and its password when it is launched.


    $ java -classpath client:mbeans:classpath \
         -Djavax.net.ssl.trustStore=config/truststore \
         -Djavax.net.ssl.trustStorePassword=trustword \
         Client
    

    You will see confirmation of the creation of the JMXMP connector client, the initialization of the environment map, the creation of the delegation subject, the connection to the MBean server and the performance of the various MBean operations followed by the closure of the connection.

11.3 Fine-Grained Security

You can implement a more fine-grained level of security in your connectors by managing user access through the Java Authentication and Authorization Service (JAAS) and Java 2 platform Standard Edition (J2SE) Security Architecture. JAAS and J2SE security is based on the use of security managers and policy files to allocate different levels of access to different users. Consequently, you can decide more precisely which users are allowed to perform which operations.

The two examples in this section are very similar to those shown in 11.1 Simple Security, with the difference being that, in addition to SSL encryption, the simple, file-based access control has been replaced by policy-based access control.

11.3.1 RMI Connector With Fine-Grained Security

You can find an example of an RMI connector with fine-grained security in the directory examplesDir/current/Security/rmi/fine_grained.

The Server class used in this example is very similar to the one used in the RMI connector example with simple security. The only difference is that there is no access.properties file to map into the environment map in the fine-grained example. This was omitted so as not to make the example overly complicated. Otherwise, all the other classes and files used in this example are the same as those used in 11.1.1 RMI Connectors With Simple Security, with the exception of the java.policy file, which is shown below.


Example 11–7 A java.policy File for an RMI Connector With Fine-Grained Security

grant codeBase "file:installDir/lib/jmx.jar" {
    permission java.security.AllPermission;
};

grant codeBase "file:installDir/lib/jmxremote.jar" {
    permission java.security.AllPermission;
};

grant codeBase "file:server" {
    permission java.security.AllPermission;
};

grant codeBase "file:mbeans" {
    permission javax.management.MBeanTrustPermission "register";
};

grant principal javax.management.remote.JMXPrincipal "username" {
    permission javax.management.MBeanPermission "*", "getDomains";
    permission javax.management.MBeanPermission 
             "SimpleStandard#-[-]",  "instantiate";
    permission javax.management.MBeanPermission 
             "SimpleStandard#-[MBeans:type=SimpleStandard]", 
             "registerMBean";
    permission javax.management.MBeanPermission 
             "SimpleStandard#State[MBeans:type=SimpleStandard]", 
             "getAttribute";
    permission javax.management.MBeanPermission 
             "SimpleStandard#State[MBeans:type=SimpleStandard]", 
             "setAttribute";
    permission javax.management.MBeanPermission 
             "SimpleStandard#-[MBeans:type=SimpleStandard]", 
             "addNotificationListener";
    permission javax.management.MBeanPermission 
             "SimpleStandard#reset[MBeans:type=SimpleStandard]", 
             "invoke";
    permission javax.management.MBeanPermission 
              "SimpleStandard#-[MBeans:type=SimpleStandard]", 
              "removeNotificationListener";
    permission javax.management.MBeanPermission 
              "SimpleStandard#-[MBeans:type=SimpleStandard]", 
              "unregisterMBean";
    permission javax.management.MBeanPermission 
              "javax.management.MBeanServerDelegate#
              -[JMImplementation:type=MBeanServerDelegate]", 
              "addNotificationListener";
    permission javax.management.MBeanPermission 
              "javax.management.MBeanServerDelegate#
              -[JMImplementation:type=MBeanServerDelegate]", 
              "removeNotificationListener";
};

The java.policy file shown in Example 11–7 grants the following permissions:

To Run the RMI Connector Example With Fine-Grained Security

Run this example from within the examplesDir/current/Security/rmi/fine_grained directory.

  1. Compile the example classes.


    $ javac -classpath classpath \
          mbeans/SimpleStandard.java \
          mbeans/SimpleStandardMBean.java \
          server/Server.java \
          client/Client.java \
          client/ClientListener.java
    
  2. Start an RMI registry on port 9999 of the local host.


    $ export CLASSPATH=server:classpath ; rmiregistry 9999 &
    
  3. Create a java.policy file from the java.policy.template file in the config directory.

    You must replace @INSTALL_HOME_FOR_JDMK@ with your installDir.

  4. Start the Server.

    You need to provide the Server with a pointer to the SSL keystore, the SSL password, the JAAS security manager and the java.policy file when you start the Server class.


    $ java -classpath server:mbeans:classpath \
         -Djavax.net.ssl.keyStore=config/keystore \
         -Djavax.net.ssl.keyStorePassword=password \
         -Djava.security.manager \
         -Djava.security.policy=config/java.policy Server &
    

    You will see confirmation of the initialization of the environment map, the creation of the MBean server and of the RMI connector.

  5. Start the Client.

    Again, the Client requires the SSL truststore and its password when it is launched.


    $ java -classpath client:server:mbeans:classpath \
         -Djavax.net.ssl.trustStore=config/truststore \
         -Djavax.net.ssl.trustStorePassword=trustword \
         Client
    

    You will see confirmation of the creation of the connector client, the connection to the RMI server and the various MBean operations followed by the closure of the connection.

11.3.2 JMXMP Connectors With Fine-Grained Security

The example of JMXMP connectors with fine-grained security is mostly identical to the example of a simple secure JMXMP connector. The only difference is in the java.policy file used to grant permissions. The java.policy file is in turn mostly identical to the one used in 11.3.1 RMI Connector With Fine-Grained Security, except for the addition of a codebase for SASL, as shown below.


Example 11–8 A java.policy File for a JMXMP Connector With Fine-Grained Security

grant codeBase "file:installDir/lib/jmx.jar" {
    permission java.security.AllPermission;
};

grant codeBase "file:installDir/lib/jmxremote.jar" {
    permission java.security.AllPermission;
};

grant codeBase "file:installDir/lib/jmxremote_optional.jar" {
    permission java.security.AllPermission;
};

grant codeBase "file:installDir/lib/sasl.jar" {
    permission java.security.AllPermission;
};

grant codeBase "file:installDir/lib/sunsasl.jar" {
    permission java.security.AllPermission;
};

grant codeBase "file:installDir/lib/jdmkrt.jar" {
    permission java.security.AllPermission;
};

grant codeBase "file:server" {
    permission java.security.AllPermission;
};

[...]

This java.policy file grants the following permissions:

To Run the JMXMP Connector Example With Fine-Grained Security

Run this example from within the examplesDir/current/Security/jmxmp/fine_grained directory.

  1. Compile the example classes.


    $ javac -classpath classpath \
          mbeans/SimpleStandard.java \
          mbeans/SimpleStandardMBean.java \
          server/Server.java \
          server/PropertiesFileCallbackHandler.java \
          client/Client.java \
          client/ClientListener.java \
          client/UserPasswordCallbackHandler.java 
    
  2. Create a java.policy file from the java.policy.template file in the config directory.

    You must replace @INSTALL_HOME_FOR_JDMK@ with your installDir.

  3. Start the Server.

    You need to provide the Server with a pointer to the SSL keystore, the SSL password, the JAAS security manager and the java.policy file when you start the Server class.


    $ java -classpath server:mbeans:classpath \
         -Djavax.net.ssl.keyStore=config/keystore \
         -Djavax.net.ssl.keyStorePassword=password \
         -Djava.security.manager \
         -Djava.security.policy=config/java.policy Server &
    

    You will see confirmation of the creation of the MBean server, the initialization of the environment map and the launching of the JMXMP connector and its registration in the MBean server.

  4. Start the Client.

    Again, the Client requires the SSL truststore and its password when it is launched.


    $ java -classpath client:mbeans:classpath \
         -Djavax.net.ssl.trustStore=config/truststore \
         -Djavax.net.ssl.trustStorePassword=trustword \
         Client
    

    You will see confirmation of the creation of the JMXMP connector client, the initialization of the environment map, the connection to the MBean server and the performance of the various MBean operations followed by the closure of the connection.

11.4 Advanced JMXMP Security Features

In addition to the simple SASL authentication demonstrated in 11.1.2 JMXMP Connectors With Simple Security above, the JMXMP connector can also implement a more complete security solution based on the DIGEST-MD5 SASL mechanism. This allows you to make communication private. These examples also show how to customize your SASL implementation by adding your own version of the SASL provider. A third implementation allows you to supply a custom configuration of the TLS socket factory.

The security features described in this section all relate exclusively to the JMXMP connector.

11.4.1 SASL Privacy

As stated above, Java DMK enables you to make your JMXMP connections private by using a combination of the SASL/DIGEST-MD5 profile, for user authentication and encryption, and file access control based on the MBeanServerForwarder interface, for user access level authorization.

There is an example of an JMXMP connector implementing SASL privacy in the current/Security/jmxmp/sasl_privacy directory in the main examplesDir.


Example 11–9 Implementing SASL Privacy in a JMXMP Connector Server

[...]
import javax.security.sasl.Sasl;

public class Server {

    public static void main(String[] args) {
        try {
            MBeanServer mbs = MBeanServerFactory.createMBeanServer();
            HashMap env = new HashMap();

            Security.addProvider(new com.sun.security.sasl.Provider());
            env.put("jmx.remote.profiles", "SASL/DIGEST-MD5");
            env.put(Sasl.QOP, "auth-conf");
            env.put("jmx.remote.sasl.callback.handler",
		             new DigestMD5ServerCallbackHandler("config" +
						                                    File.separator +
						                                    "password.properties",
						                                    null,
						                                    null));

            env.put("jmx.remote.x.access.file",
           		     "config" + File.separator + "access.properties");

            JMXServiceURL url = new JMXServiceURL("jmxmp", null, 5555);
            JMXConnectorServer cs =
                JMXConnectorServerFactory.newJMXConnectorServer(url, 
                                                                env, 
                                                                mbs);
            cs.start();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

This example is similar to the simple secure JMXMP example shown in 11.1.2 JMXMP Connectors With Simple Security. It is structured in the same way, with an environment map env being populated with the required security configurations before being passed into a new instance of JMXConnectorServer.

However, as you can see, unlike in the simple secure JMXMP example, which used the PLAIN SASL mechanism, this example uses the DIGEST-MD5 SASL mechanism. The SASL DIGEST-MD5 profile defines the digital signature that must be exchanged between the client and server for a connection to be made, based on the HTTP Digest Authentication mechanism. The HTTP Digest Authentication mechanism was developed by the Internet Engineering Task Force as RFC 2831.

With the protocol profile set to DIGEST-MD5, the level of protection to be provided is defined by the quality of protection property, Sasl.QOP. In this example, the Sasl.QOP is set to auth-conf, denoting that the types of protection to be implemented are authentication and confidentiality, namely encryption. The encryption cypher used is dictated by SASL and can be controlled by properties specific to the DIGEST-MD5 SASL mechanism.

A instance of the callback handler provided with this example, DigestMD5ServerCallbackHandler, is created with the required password file, config/password.properities, passed to it. Similarly, the name of the user allowed to connect to the server, and the level of access granted to that user, are defined when the config/access.properties file is passed into the environment map. These properties must be matched by the client end of the connector, if the connection is to succeed. In this example, the level of access rights granted is readwrite, and the approved user is called username. The password.properties file provides the password for user username that is expected by the SASL/DIGEST-MD5 profile.

The DigestMD5ServerCallbackHandler class, being an implementation of the standard Java callback interface CallbackHandler, allows the server to retrieve the authentication information it needs from the properties files described above. This example does not implement canonical naming or proxy files, so the second and third parameters passed to DigestMD5ServerCallbackHandler when it is instantiated are null. The DIGEST-MD5 server mechanism will then be able to compare them with the user credentials supplied remotely by the client.

The environment map containing all of the above is then used to create the connector server instance, cs.


Example 11–10 Implementing SASL Privacy in a JMXMP Connector Client

public class Client {

    public static void main(String[] args) {
        try {
            HshMap env = new HashMap();

            Security.addProvider(new com.sun.security.sasl.Provider());
            env.put("jmx.remote.profiles", "SASL/DIGEST-MD5");
            env.put(Sasl.QOP, "auth-conf");
            env.put("jmx.remote.sasl.callback.handler",
                    new DigestMD5ClientCallbackHandler("username", 
                                                       "password"));

            JMXServiceURL url = new JMXServiceURL("jmxmp", null, 5555);
            JMXConnector jmxc = JMXConnectorFactory.connect(url, env);

            MBeanServerConnection mbsc = jmxc.getMBeanServerConnection();

            String domains[] = mbsc.getDomains();
            for (int i = 0; i < domains.length; i++) {
                System.out.println("\tDomain[" + i + "] = " + domains[i]);
            }

            ObjectName mbeanName = new ObjectName(
                                        "MBeans:type=SimpleStandard");
            System.out.println("\nCreate SimpleStandard MBean...");
            mbsc.createMBean("SimpleStandard", mbeanName, null, null);

            // Perform MBean operations, before unregistering MBean
            //  & closing connection
            [...]
            mbsc.unregisterMBean(mbeanName);

            jmxc.close();
            System.out.println("\nBye! Bye!");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

As shown in Example 11–10 above, the client end of the private connection is created using a very similar set of properties as the server end. However, the client end of the connector supplies the user and password information via a different callback handler, DigestMD5ClientCallbackHandler. Like DigestMD5ServerCallbackHandler, DigestMD5ClientCallbackHandler is an implementation of the CallbackHandler interface. DigestMD5ClientCallbackHandler is passed the username and password expected by DigestMD5ServerCallbackHandler when it is instantiated, which it then passes to the DIGEST-MD5 client mechanism. The client DIGEST-MD5 mechanism then makes them available for comparison with the properties held by the server, thus allowing the connection to the MBean server to be established. Operations can then be performed on the MBeans the MBean server contains.

To Run the Secure JMXMP Connector Example with SASL Privacy

Run this example from within the examplesDir/current/Security/jmxmp/sasl_privacy directory.

  1. Compile the Java classes.


    $ javac -classpath classpath \
          mbeans/SimpleStandard.java \
          mbeans/SimpleStandardMBean.java \
          server/Server.java \
          server/DigestMD5ServerCallbackHandler.java \
          client/Client.java \
          client/ClientListener.java \
          client/DigestMD5ClientCallbackHandler.java 
    
  2. Start the Server.


    $ java -classpath server:mbeans:classpath Server & 
    

    You will see confirmation of the creation of the MBean server, the initialization of the environment map and the launching of the JMXMP connector and its registration in the MBean server.

  3. Start the Client.


    $ java -classpath client:mbeans:classpath Client 
    

    You will see confirmation of the creation of the JMXMP connector client, the initialization of the environment map, the connection to the MBean server and the performance of the various MBean operations followed by the closure of the connection.

11.4.2 SASL Provider

In addition to the simple SASL security and the privacy-based security you can apply to JMXMP connectors, you can also define a custom SASL provider mechanism by defining your own SASL Provider.

The SASL mechanism used in this example is a straightforward customized mechanism called SAMPLE SASL. The example is found in the subdirectories of examplesDir/current/Security/jmxmp/sasl_provider. The SAMPLE SASL mechanism is defined in the SampleServer class, shown below.


Example 11–11 Custom SASL Server

import java.io.*;
import javax.security.sasl.*;

public class SampleServer implements SaslServer {

    private String username;
    private boolean completed = false;

    public String getMechanismName() {
        return "SAMPLE";
    }

    public byte[] evaluateResponse(byte[] responseData) 
            throws SaslException {
        if (completed) {
            throw new SaslException("Already completed");
        }
        completed = true;
        username = new String(responseData);
        return null;
    }

    public String getAuthorizationID() {
        return username;
    }

    public boolean isComplete() {
        return completed;
    }

    public byte[] unwrap(byte[] incoming, int offset, int len) 
        throws SaslException {
        throw new SaslException("No negotiated security layer");
    }

    public byte[] wrap(byte[] outgoing, int offset, int len)
        throws SaslException {
        throw new SaslException("No negotiated security layer");
    }

    public Object getNegotiatedProperty(String propName) {
        if (propName.equals(Sasl.QOP)) {
            return "auth";
        }
        return null;
    }

    public void dispose() throws SaslException {
    }
}

The custom SASL SAMPLE mechanism defined by the SampleServer class, shown above, extends the SASL interface SaslServer. It simply calls the standard SaslServer methods one by one, to perform the following security operations:


Example 11–12 Custom SASL Server

public class SampleClient implements SaslClient {

    private byte[] username;
    private boolean completed = false;

    public SampleClient(String authorizationID) throws SaslException {
        if (authorizationID != null) {
            try {
                username = ((String)authorizationID).getBytes("UTF8");
            } catch (UnsupportedEncodingException e) {
                throw new SaslException("Cannot convert " + authorizationID
                                        + " into UTF-8", e);
            }
        } else {
            username = new byte[0];
        }
    }

    public String getMechanismName() {
        return "SAMPLE";
    }

    public boolean hasInitialResponse() {
        return true;
    }

    public byte[] evaluateChallenge(byte[] challengeData) 
            throws SaslException {
        if (completed) {
            throw new SaslException("Already completed");
        }
        completed = true;
        return username;
    }

    public boolean isComplete() {
        return completed;
    }

    public byte[] unwrap(byte[] incoming, int offset, int len) 
        throws SaslException {
        throw new SaslException("No negotiated security layer");
    }

    public byte[] wrap(byte[] outgoing, int offset, int len)
        throws SaslException {
        throw new SaslException("No negotiated security layer");
    }

    public Object getNegotiatedProperty(String propName) {
        if (propName.equals(Sasl.QOP)) {
            return "auth";
        }
        return null;
    }

    public void dispose() throws SaslException {
    }
}

The SampleClient class is an extension of the SaslClient interface, and calls the standard SaslClient methods one by one, to perform the following operations that are expected by the SampleServer:

With these SASL server and client mechanisms defined, they can then be instantiated by their respective factories.


Example 11–13 A Custom SASL Server Factory

public class ServerFactory implements SaslServerFactory {
   
     public SaslServer createSaslServer(String mechs,
				       String protocol,
				       String serverName,
				       Map props,
				       CallbackHandler cbh)
          throws SaslException {
	         if (mechs.equals("SAMPLE")) {
	             return new SampleServer();
	         }
	         return null;
     }

    public String[] getMechanismNames(Map props) {
	        return new String[]{"SAMPLE"};
    }
}

This basic implementation of the SaslServerFactory interface creates SampleServer instances when it is called with the SASL mechanism parameter set to SAMPLE. None of the other parameters are used by this mechanism.

The SampleClientFactory creates SampleClient instances in exactly the same way as the SampleServerFactory creates SampleServer instances.


Example 11–14 SASL SAMPLE Provider Class

public final class Provider extends java.security.Provider {
    public Provider() {
	super("SampleSasl", 1.0, "SAMPLE SASL MECHANISM PROVIDER");
	put("SaslClientFactory.SAMPLE", "ClientFactory");
	put("SaslServerFactory.SAMPLE", "ServerFactory");
    }
}

The SASL SAMPLE Provider constructor shown above specifies the name of the provider (SampleSasl), the version of this provider (in this case, 1.0) and a description of the provider. It then defines the server and client factories that are used to create SASL servers and clients implementing this SASL mechanism.

With the mechanisms above thus defined, all the Server and Client classes used in this example require the correct Provider to be installed in the environment.


Example 11–15 Adding a Provider to a JMX Connector Server

public class Server {

    public static void main(String[] args) {
        try {
            MBeanServer mbs = MBeanServerFactory.createMBeanServer();
            HashMap env = new HashMap();

            Security.addProvider(new Provider());
            env.put("jmx.remote.profiles", "SASL/SAMPLE");
            env.put("jmx.remote.x.access.file",
      		    "config" + File.separator + "access.properties");

            JMXServiceURL url = new JMXServiceURL("jmxmp", null, 5555);
            JMXConnectorServer cs =
                JMXConnectorServerFactory.newJMXConnectorServer(url, 
                                                                env, 
                                                                mbs);

            cs.start();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

The SAMPLE SASL mechanism is passed into the Client in the same way.

To Run the Secure JMXMP Connector Example with a SASL Provider

Run this example from within the examplesDir/current/Security/jmxmp/sasl_provider directory.

  1. Compile the Java classes.


    $ javac -classpath classpath \
          mbeans/SimpleStandard.java \
          mbeans/SimpleStandardMBean.java \
          server/Server.java \
          client/Client.java \
          client/ClientListener.java \
          sample/Provider.java \
          sample/ClientFactory.java \
          sample/ServerFactory.java \
          sample/SampleClient.java \
          sample/SampleServer.java 
    
  2. Start the Server.


    $ java -classpath server:sample:mbeans:classpath Server & 
    

    You will see confirmation of the creation of the MBean server, the initialization of the environment map and the launching of the JMXMP connector and its registration in the MBean server.

  3. Start the Client.


    $ java -classpath client:sample:mbeans:classpath Client 
    

    You will see confirmation of the creation of the JMXMP connector client, the initialization of the environment map, the connection to the MBean server and the performance of the various MBean operations followed by the closure of the connection.

11.4.3 TLS Socket Factory

Your JMXMP connections can also be secured using an implementation of Transport Layer Security (TLS) sockets, as shown in the following example. This example is taken from the sub-directories of examplesDir/current/Security/jmxmp/tls_factory. The example shows how to provide a custom configured TLS factory for use by the client and the server.


Example 11–16 Securing a JMXMP Connector Server Using TLS Socket Factories

public class Server { 
 
    public static void main(String[] args) { 
      try { 
           MBeanServer mbs = MBeanServerFactory.createMBeanServer(); 
           HashMap env = new HashMap(); 
           String keystore = "config" + File.separator + "keystore"; 
           char keystorepass[] = "password".toCharArray(); 
           char keypassword[] = "password".toCharArray(); 
           KeyStore ks = KeyStore.getInstance("JKS"); 
           ks.load(new FileInputStream(keystore), keystorepass); 
           KeyManagerFactory kmf = 
              KeyManagerFactory.getInstance("SunX509"); 
           kmf.init(ks, keypassword); 
           SSLContext ctx = SSLContext.getInstance("TLSv1"); 
           ctx.init(kmf.getKeyManagers(), null, null); 
           SSLSocketFactory ssf = ctx.getSocketFactory(); 
           env.put("jmx.remote.profiles", "TLS"); 
           env.put("jmx.remote.tls.socket.factory", ssf); 
           env.put("jmx.remote.tls.enabled.protocols", "TLSv1"); 
           env.put("jmx.remote.tls.enabled.cipher.suites", 
                 "SSL_RSA_WITH_NULL_MD5"); 
 
           JMXServiceURL url = new JMXServiceURL("jmxmp", null, 5555); 
           JMXConnectorServer cs = 
              JMXConnectorServerFactory.newJMXConnectorServer(url, 
                                                              env, 
                                                              mbs); 
           cs.start(); 
 
         } catch (Exception e) { 
           e.printStackTrace(); 
         } 
      } 
  } 

Example 11–16 shows the creation of an MBean server mbs, an environment map env, and a key store object ks. The key store object ks is initialized by loading in the input stream keystore and the key store password, keystorepass, that grants access to the key store.

An instance of the SSL class KeyManagerFactory, named kmf, that implements the SunX509 key management algorithm, is then initialized. The kmf instance is passed the key information contained in the key store ks and the key password keypassword that grants access to the keys in the key store.

The SSL context, ctx, is set to use the protocol version TLSv1, and is initialized with kmf as its key manager. Finally, an instance of SSLSocketFactory, named ssf, is created by calling the getSocketFactory() method of the SSL context ctx.

The environment map env is populated with the TLS profiles, the SSL socket factory ssf, the TLSv1 protocol version, and the SSL_RSA_WITH_NULL_MD5 cipher suite.

Finally, with an instance of JMXConnectorServer, named cs, is created. The connector server cs is started by a call to the start() method of JMXConnectorServer.


Example 11–17 Securing a JMXMP Connector Client Using TLS Socket Factories

public class Client { 
 
   public static void main(String[] args) { 
     try { 
          HashMap env = new HashMap(); 
          String truststore = "config" + File.separator + "truststore"; 
          char truststorepass[] = "trustword".toCharArray(); 
          KeyStore ks = KeyStore.getInstance("JKS"); 
          ks.load(new FileInputStream(truststore), truststorepass); 
          TrustManagerFactory tmf =  
             TrustManagerFactory.getInstance("SunX509"); 
          tmf.init(ks); 
          SSLContext ctx = SSLContext.getInstance("TLSv1"); 
          ctx.init(null, tmf.getTrustManagers(), null); 
          SSLSocketFactory ssf = ctx.getSocketFactory(); 
          env.put("jmx.remote.profiles", "TLS"); 
          env.put("jmx.remote.tls.socket.factory", ssf); 
          env.put("jmx.remote.tls.enabled.protocols", "TLSv1"); 
          env.put("jmx.remote.tls.enabled.cipher.suites", 
             "SSL_RSA_WITH_NULL_MD5"); 
 
          JMXServiceURL url = new JMXServiceURL("jmxmp", null, 5555); 
          JMXConnector jmxc = JMXConnectorFactory.connect(url, env); 
          MBeanServerConnection mbsc = jmxc.getMBeanServerConnection(); 
          String domains[] = mbsc.getDomains(); 
          for (int i = 0; i < domains.length; i++) { 
          System.out.println("Domain[" + i + "] = " + domains[i]); 
          } 
 
          ObjectName mbeanName = 
              new ObjectName("MBeans:type=SimpleStandard"); 
          mbsc.createMBean("SimpleStandard", mbeanName, null, null); 
          // Perform MBean operations 
          // 
          [...] 
          mbsc.removeNotificationListener(mbeanName, listener); 
          mbsc.unregisterMBean(mbeanName); 
          jmxc.close(); 
        } catch (Exception e) { 
          e.printStackTrace(); 
        } 
     } 
} 

As for the Server, Example 11–17 shows the creation of an environment map env, and a key store object ks for the JMXMP connector Client. The key store object ks is initialized by loading in the input stream truststore and the password, truststorepass, that grants access to the trust store.

An instance of the SSL class TrustManagerFactory, named tmf, that implements the SunX509 key management algorithm, is then initialized. The tmf instance is passed the key information contained in the key store ks.

The SSL context, ctx, is set to use the protocol version TLSv1, and is initialized with tmf as its trust manager. Finally, an instance of SSLSocketFactory, named ssf, is created by calling the getSocketFactory() method of the SSL context ctx.

The environment map env is populated with the the SSL socket factory ssf, the TLSv1 protocol version, and the SSL_RSA_WITH_NULL_MD5 cipher suite.

An instance of JMXConnector, named jmxc is then created, and an MBean server connection, mbsc, is made by calling the getMBeanServerConnection method of the JMX Remote API connector jmxc.

In code that is not shown here, when the connection to the MBean server has been established, the Client creates an MBean called SimpleStandard and performs various operations on it. Once these MBean operations have completed, the Client unregisters the MBean, and closes down the connection jmxc.

To Run the Secure JMXMP Connector Example with TLS Socket Factories

Run this example from within the examplesDir/current/Security/jmxmp/tls_factory directory.

  1. Compile the Java classes.


    $ javac -classpath classpath \
          mbeans/SimpleStandard.java \
          mbeans/SimpleStandardMBean.java \
          server/Server.java \
          client/Client.java \
          client/ClientListener.java 
    
  2. Start the Server.


    $ java -classpath server:mbeans:classpath Server & 
    

    You will see confirmation of the creation of the MBean server, the initialization of the environment map and the launching of the JMXMP connector and its registration in the MBean server.

  3. Start the Client.


    $ java -classpath client:mbeans:classpath Client 
    

    You will see confirmation of the creation of the JMXMP connector client, the initialization of the environment map, the connection to the MBean server and the performance of the various MBean operations followed by the closure of the connection.