Monitoring and Management Using JMX Technology
The Java virtual machine (Java VM ) has built-in instrumentation that enables you to monitor and manage it using the Java Management Extensions (JMX) technology. These built-in management utilities are often referred to as out-of-the-box management tools for the Java VM. You can also monitor any appropriately instrumented applications using the JMX API.
Setting System Properties
To enable and configure the out-of-the-box JMX agent so that it can monitor and manage the Java VM, you must set certain system properties when you start the Java VM. You set a system property on the command-line as follows.
java -Dproperty=value ...
You can set any number of system properties in this way. If you do not specify a value for a management property, then the property is set with its default value. The full set of out-of-the-box management properties is described in Table 2-1 at the end of this chapter. You can also set system properties in a configuration file, as described in Out-of-the-Box Monitoring and Management Properties.
Note - To run the Java VM from the command line, you must add JRE_HOME/bin to your path, where JRE_HOME is the directory containing the Java Runtime Environment (JRE) implementation. Alternatively, you can enter the full path when you type the command.
The following documents describe the syntax and the full set of command-line options supported by the Java HotSpot VMs.
Enabling the Out-of-the-Box Management
To monitor a Java platform using the JMX API, you must do the following.
-
Enable the JMX agent (another name for the platform MBean server) when you start the Java VM. You can enable the JMX agent for:
-
Local monitoring, for a client management application running on the local system.
-
Remote monitoring, for a client management application running on a remote system.
-
-
Monitor the Java VM with a tool that complies to the JMX specification, such as JConsole. See Chapter 3, Using JConsole for more information about Console.
These steps are described in the next sections.
Local Monitoring and Management
Under previous releases of the Java SE platform, to allow the JMX client access to a local Java VM, you had to set the following system property when you started the Java VM or Java application.
com.sun.management.jmxremote
Setting this property registered the Java VM platform's MBeans and published the Remote Method Invocation (RMI) connector via a private interface to allow JMX client applications to monitor a local Java platform, that is, a Java VM running on the same machine as the JMX client.
In the Java SE 6 platform, it is no longer necessary to set this system property. Any application that is started on the Java SE 6 platform will support the Attach API, and so will automatically be made available for local monitoring and management when needed.
For example, previously, to enable the JMX agent for the Java SE sample application Notepad, you would have to run the following commands.
% cd JDK_HOME/demo/jfc/Notepad % java -Dcom.sun.management.jmxremote -jar Notepad.jar
In the above command, JDK_HOME is the directory in which the Java Development Kit (JDK) is installed. In the Java SE 6 platform, you would simply have to run the following command to start Notepad.
% java -jar Notepad.jar
Once Notepad has been started, a JMX client using the Attach API can then enable the out-of-the-box management agent to monitor and manage the Notepad application.
Note - On Windows platforms, for security reasons, local monitoring and management is only supported if your default temporary directory is on a file system that allows the setting of permissions on files and directories (for example, on a New Technology File System (NTFS) file system). It is not supported on a File Allocation Table (FAT) file system, which provides insufficient access controls.
Local Monitoring and Management Using JConsole
Local monitoring with JConsole is useful for development and creating prototypes. Using JConsole locally is not recommended for production environments, because JConsole itself consumes significant system resources. Rather, you should use JConsole on a remote system to isolate it from the platform being monitored.
However, if you do wish to perform local monitoring using JConsole, you start the tool by typing jconsole in a command shell. When you start jconsole without any arguments, it will automatically detect all local Java applications, and display a dialog box that enables you to select the application you want to monitor. Both JConsole and the application must by executed by the same user, since the monitoring and management system uses the operating system's file permissions.
Note - To run JConsole from the command line, you must add JDK_HOME/bin to your path. Alternatively, you can enter the full path when you type the command.
For more information, see Chapter 3, Using JConsole.
Remote Monitoring and Management
To enable monitoring and management from remote systems, you must set the following system property when you start the Java VM.
com.sun.management.jmxremote.port=portNum
In the property above, portNum is the port number through which you want to enable JMX RMI connections. Be sure to specify an unused port number. In addition to publishing an RMI connector for local access, setting this property publishes an additional RMI connector in a private read-only registry at the specified port using a well known name, "jmxrmi".
Note - You must set the above system property in addition to any properties you might set for security.
Remote monitoring and management requires security, to ensure that unauthorized persons cannot control or monitor your application. Password authentication over the Secure Sockets Layer (SSL) and Transport Layer Security (TLS) is enabled by default. You can disable password authentication and SSL separately, as described in the next sections.
For production systems, use SSL client certificates for authentication instead of passwords, or ensure that the client obtains the remote connector object securely, for example through a secure LDAP server or a file in a shared secure file system. See the topics Using SSL and Using LDAP Authentication for more information.
The Java platform supports pluggable login modules for authentication. You can plug in any login module depending on the authentication infrastructure in your organization. The section titled Using LDAP Authentication describes how to plug in the com.sun.security.auth.module.LdapLoginModule for Lightweight Directory Access Protocol (LDAP) based authentication.
After you have enabled the JMX agent for remote use, you can monitor your application using JConsole, as described in Remote Monitoring with JConsole. How to connect to the management agent programmatically is described in Connecting to the JMX Agent Programmatically.
Using Password Authentication
Using LDAP Authentication
The JMXAuthenticator
implementation in the JMX agent is based on Java Authentication and
Authorization Service (JAAS) technology. Authentication is
performed by passing the user credentials to a JAAS javax.security.auth.spi.LoginModule
object. The
com.sun.security.auth.module.LdapLoginModule class enables
authentication using LDAP. You can replace the default
LoginModule
class with the
LdapLoginModule
as described next.
Create a JAAS configuration file that would work in the required
business organization. An example of a configuration file
(ldap.config
) is shown next:
ExampleCompanyConfig { com.sun.security.auth.module.LdapLoginModule REQUIRED userProvider="ldap://example-ds/ou=people,dc=examplecompany,dc=com" userFilter="(&(uid={USERNAME})(objectClass=inetOrgPerson))" authzIdentity=monitorRole; };
The options in the configuration file are briefly explained next. See documentation for the com.sun.security.auth.module.LdapLoginModule class for information about the configuration options shown in the code snippet.
- The
com.sun.security.auth.module.LdapLoginModule REQUIRED
option means that authentication usingLdapLoginModule
is required in order for overall authentication to be successful - The
userProvider
option identifies the LDAP server and the position in the directory tree where user entries are located - The
userFilter
option specifies the search filter to use to locate a user entry in the LDAP directory. The token{USERNAME}
is replaced with the username before the filter is used to search the directory. - The
authzIdentity
option specifies the access role for autheticated users. In the example, authenticated users will have themonitorRole
. See the Access Files section for more information.
Start your application with the following properties set on the command line:
com.sun.management.jmxremote.login.config
- This property configures the JMX agent to use the specified JAAS configuration entry.java.security.auth.login.config
- specifies the path to the JAAS configuration file.
A sample command line is shown next.
java -Dcom.sun.management.jmxremote.port=5000 -Dcom.sun.management.jmxremote.login.config=ExampleCompanyConfig -Djava.security.auth.login.config=ldap.config -jar MyApplication.jar
Using File-Based Password Authentication
The file-based password authentication mechanism supported by the JMX agent stores the password in clear-text and is intended only for development use. For production use, it is strongly recommended that you use SSL client certificates for authentication or plug in a secure login configuration.
Caution - A potential security issue has been identified with password authentication for remote connectors when the client obtains the remote connector from an insecure RMI registry (the default). If an attacker starts a bogus RMI registry on the target server before the legitimate registry is started, then the attacker can steal clients' passwords. This scenario includes the case where you launch a Java VM with remote management enabled, using the system property com.sun.management.jmxremote.port=portNum, even when SSL is enabled. Although such attacks are likely to be noticed, it is nevertheless a vulnerability.
By default, when you enable the JMX agent for remote monitoring, it uses password authentication. However, the way you set it up depends on whether you are in a single-user environment or a multiple-user environment.
Since passwords are stored in clear-text in the password file, it is not advisable to use your regular user name and password for monitoring. Instead, use the user names specified in the password file such as monitorRole and controlRole. For more information, see Using Password and Access Files.
To Set up a Single-User Environment
You set up the password file in the JRE_HOME/lib/management directory as follows.
- Copy the password template file, jmxremote.password.template, to jmxremote.password.
- Set file permissions so that only the owner can read and write the password file.
- Add passwords for roles such as monitorRole and controlRole.
To Set up a Multiple-User Environment
You set up the password file in the JRE_HOME/lib/management directory as follows.
- Copy the password template file, jmxremote.password.template, to your home directory and rename it to jmxremote.password.
- Set file permissions so that only you can read and write the password file.
- Add passwords for the roles such as monitorRole and controlRole.
- Set the following system property when you start the Java
VM.
com.sun.management.jmxremote.password.file=pwFilePath
In the above property, pwFilePath is the path to the password file.
Disabling Password Authentication
Password authentication for remote monitoring is enabled by default. To disable it, set the following system property when you start the Java VM.
com.sun.management.jmxremote.authenticate=false
Caution - This configuration is insecure. Any remote user who knows (or guesses) your JMX port number and host name will be able to monitor and control your Java application and platform. While it may be acceptable for development, it is not recommended for production systems.
When you disable password authentication, you can also disable SSL, as described in Disabling Security. You may also want to disable passwords, but use SSL client authentication, as described in Enabling SSL Client Authentication.
Using SSL
SSL is enabled by default when you enable remote monitoring and management. To use SSL, you need to set up a digital certificate on the system where the JMX agent (the MBean server) is running and then configure SSL properly. You use the command-line utility keytool to work with certificates. The general procedure is as follows.
To Set up SSL
- If you do not already have a key pair and certificate set up
on the server:
-
Generate a key pair with the keytool -genkey command.
-
Request a signed certificate from a certificate authority (CA) with the keytool -certreq command.
-
Import the certificate into your keystore with the keytool -import command. See Importing Certificates in the keytool documentation.
For more information and examples, see keytool - Key and Certificate Management Tool (Solaris and Linux) or (Windows platforms).
-
- Configure SSL on the server system.
A full explanation of configuring and customizing SSL is beyond the scope of this document, but you generally need to set the system properties described in the list below.
- System Property
-
Description
- javax.net.ssl.keyStore
-
Keystore location.
- javax.net.ssl.keyStoreType
-
Default keystore type.
- javax.net.ssl.keyStorePassword
-
Default keystore password.
- javax.net.ssl.trustStore
-
Truststore location.
- javax.net.ssl.trustStoreType
-
Default truststore type.
- javax.net.ssl.trustStorePassword
-
Default truststore password.
For more information about setting system properties, see Setting System Properties above, or consult the following documents.
Enabling RMI Registry Authentication
When setting up connections for monitoring remote applications, you can optionally bind the RMI connector stub to an RMI registry that is protected by SSL. This allows clients with the appropriate SSL certificates to get the connector stub that is registered in the RMI registry. To protect the RMI registry using SSL, you must set the following system property.
com.sun.management.jmxremote.registry.ssl=true
When this property is set to true, an RMI registry protected by SSL will be created and configured by the out-of-the-box management agent when the Java VM is started. The default value of this property is false. If this property is set to true, in order to have full security then SSL client authentication must also be enabled, as described in the next section.
Enabling SSL Client Authentication
To enable SSL client authentication, set the following system property when you start the Java VM.
com.sun.management.jmxremote.ssl.need.client.auth=true
SSL must be enabled (the default), to use client SSL authentication. This configuration requires the client system to have a valid digital certificate. You must install a certificate and configure SSL on the client system, as described in Using SSL. As stated in the previous section, if RMI registry SSL protection is enabled, then client SSL authentication must be set to true.
Disabling SSL
To disable SSL when monitoring remotely, you must set the following system property when you start the Java VM.
com.sun.management.jmxremote.ssl=false
Password authentication will still be required unless you disable it, as specified in Disabling Password Authentication.
Disabling Security
To disable both password authentication and SSL (namely to disable all security), you should set the following system properties when you start the Java VM.
com.sun.management.jmxremote.authenticate=false com.sun.management.jmxremote.ssl=false
Caution - This configuration is insecure: any remote user who knows (or guesses) your port number and host name will be able to monitor and control your Java applications and platform. Furthermore, possible harm is not limited to the operations you define in your MBeans. A remote client could create a javax.management.loading.MLet MBean and use it to create new MBeans from arbitrary URLs, at least if there is no security manager. In other words, a rogue remote client could make your Java application execute arbitrary code.
Consequently, while disabling security might be acceptable for development, it is strongly recommended that you do not disable security for production systems.
Remote Monitoring with JConsole
You can remotely monitor an application using JConsole, with or without security enabled.
Remote Monitoring with JConsole with SSL Disabled
To monitor a remote application with SSL disabled, you would start JConsole with the following command.
% jconsole hostName:portNum
You can also omit the host name and port number, and enter them in the dialog box that JConsole provides.
Remote Monitoring with JConsole with SSL Enabled
To monitor a remote application with SSL enabled, you need to set up the truststore on the system where JConsole is running and configure SSL properly. For example, you can create a keystore as described in the JSSE Guide and start your application (called Server in this example) with the following commands.
% java -Djavax.net.ssl.keyStore=keystore \ -Djavax.net.ssl.keyStorePassword=password Server
If you created the keystore and started Server as shown above, then you would have to start JConsole as follows.
% jconsole -J-Djavax.net.ssl.trustStore=truststore \ -J-Djavax.net.ssl.trustStorePassword=trustword
The above configuration authenticates the server only. If SSL client authentication is set up, you will need to provide a similar keystore for JConsole's keys, and an appropriate truststore for the application.
See Customizing the Default Key and Trust Stores, Store Types, and Store Passwords in the JSSE Guide for information.
For more information on using JConsole, see Chapter 3, Using JConsole.
Using Password and Access Files
The password and access files control security for remote monitoring and management. These files are located by default in JRE_HOME/lib/management and are in the standard Java properties file format. For more information on the format, see the API reference for the java.util.Properties package.
Password Files
The password file defines the different roles and their passwords. The access control file (jmxremote.access by default) defines the permitted access for each role. To be functional, a role must have an entry in both the password and the access files.
The JRE implementation contains a password file template named jmxremote.password.template. Copy this file to JRE_HOME/lib/management/jmxremote.password or to your home directory, and add the passwords for the roles defined in the access file.
You must ensure that only the owner has read and write permissions on this file, since it contains the passwords in clear text. For security reasons, the system checks that the file is only readable by the owner and exits with an error if it is not. Thus in a multiple-user environment, you should store the password file in private location such as your home directory.
Property names are roles, and the associated value is the role's password. For example, the following are sample entries in the password file.
Example 2-1 An Example Password File# specify actual password instead of the text password monitorRole password controlRole password
On Solaris and Linux systems, you can set the file permissions for the password file by running the following command.
chmod 600 jmxremote.password
For instructions on how to set file permissions on Windows platforms, see Appendix A, Additional Security Information For Microsoft Windows.
Access Files
By default, the access file is named jmxremote.access. Property names are identities from the same space as the password file. The associated value must be either readonly or readwrite.
The access file defines roles and their access levels. By default, the access file defines the two following primary roles.
-
monitorRole, which grants read-only access for monitoring.
-
controlRole, which grants read-write access for monitoring and management.
An access control entry consists of a role name and an associated access level. The role name cannot contain spaces or tabs and must correspond to an entry in the password file. The access level can be either one of the following.
-
readonly, which grants access to read an MBean's attributes. For monitoring, this means that a remote client in this role can read measurements but cannot perform any action that changes the environment of the running program. The remote client can also listen to MBean notifications.
-
readwrite, which grants access to read and write an MBean's attributes, to invoke operations on them, and to create or remove them. This access should be granted to only trusted clients, since they can potentially interfere with the operation of an application.
A role should have only one entry in the access file. If a role has no entry, it has no access. If a role has multiple entries, then the last entry takes precedence. Typical predefined roles in the access file resemble the following.
Example 2-2 An Example Access File# The "monitorRole" role has readonly access. # The "controlRole" role has readwrite access. monitorRole readonly controlRole readwrite
Out-of-the-Box Monitoring and Management Properties
You can set out-of-the-box monitoring and management properties in a configuration file or on the command line. Properties specified on the command line override properties in a configuration file. The default location for the configuration file is JRE_HOME/lib/management/management.properties. The Java VM reads this file if either of the command-line properties com.sun.management.jmxremote or com.sun.management.jmxremote.port are set. Management via the Simple Network Management Protocol (SNMP) uses the same configuration file. For more information about SNMP monitoring, see Chapter 5, SNMP Monitoring and Management.
You can specify a different location for the configuration file with the following command-line option.
com.sun.management.config.file=ConfigFilePath
In the property above, ConfigFilePath is the path to the configuration file.
Table 2-1 describes all the out-of-the-box monitoring and management properties.
Property |
Description |
Values |
---|---|---|
com.sun.management.jmxremote |
Enables the JMX remote agent and local monitoring via a JMX connector published on a private interface used by JConsole and any other local JMX clients that use the Attach API. JConsole can use this connector if it is started by the same user as the user that started the agent. No password or access files are checked for requests coming via this connector. |
true / false. Default is true. |
com.sun.management.jmxremote. port |
Enables the JMX remote agent and creates a remote JMX connector to listen through the specified port. By default, the SSL, password, and access file properties are used for this connector. It also enables local monitoring as described for the com.sun.management.jmxremote property. |
Port number. No default. |
com.sun.management.jmxremote. registry.ssl |
Binds the RMI connector stub to an RMI registry protected by SSL. |
true / false. Default is false. |
com.sun.management.jmxremote. ssl |
Enables secure monitoring via SSL. If false, then SSL is not used. |
true / false. Default is true. |
com.sun.management.jmxremote. ssl.enabled.protocols |
A comma-delimited list of SSL/TLS protocol versions to enable. Used in conjunction with com.sun.management.jmxremote.ssl. |
Default SSL/TLS protocol version. |
com.sun.management.jmxremote. ssl.enabled.cipher.suites |
A comma-delimited list of SSL/TLS cipher suites to enable. Used in conjunction with com.sun.management.jmxremote.ssl. |
Default SSL/TLS cipher suites. |
com.sun.management.jmxremote. ssl.need.client.auth |
If this property is true and the property com.sun.management.jmxremote.ssl is also true, then client authentication will be performed. |
true / false. Default is true. |
com.sun.management.jmxremote. authenticate |
If this property is false then JMX does not use passwords or access files: all users are allowed all access. |
true / false. Default is true. |
com.sun.management.jmxremote. password.file |
Specifies location for password file. If com.sun.management.jmxremote.authenticate is false, then this property and the password and access files are ignored. Otherwise, the password file must exist and be in the valid format. If the password file is empty or nonexistent, then no access is allowed. |
JRE_HOME/lib/management/ jmxremote.password |
com.sun.management.jmxremote. access.file |
Specifies location for the access file. If com.sun.management.jmxremote.authenticate is false, then this property and the password and access files are ignored. Otherwise, the access file must exist and be in the valid format. If the access file is empty or nonexistent, then no access is allowed. |
JRE_HOME/lib/management/ jmxremote.access |
Specifies the name of a Java Authentication and Authorization Service (JAAS) login configuration entry to use when the JMX agent authenticates users. When using this property to override the default login configuration, the named configuration entry must be in a file that is loaded by JAAS. In addition, the login modules specified in the configuration should use the name and password callbacks to acquire the user's credentials. For more information, see the API documentation for javax.security.auth.callback.NameCallback and javax.security.auth.callback.PasswordCallback. |
Default login configuration is a file-based password authentication. |
Configuration Errors
If any errors occur during start up of the MBean server, the RMI registry, or the connector, the Java VM will throw an exception and exit. Configuration errors include the following.
-
Failure to bind to the port number.
-
Invalid password file.
-
Invalid access file.
-
Password file is readable by users other than the owner.
If your application runs a security manager, then additional permissions are required in the security permissions file.
Connecting to the JMX Agent Programmatically
Once you have enabled the JMX agent, a client can use the following URL to access the monitoring service.
service:jmx:rmi:///jndi/rmi://hostName:portNum/jmxrmi
A client can create a connector for the agent by instantiating a javax.management.remote.JMXServiceURL object using the URL, and then creating a connection using the JMXConnectorFactory.connect method, shown in Example 2-3.
Example 2-3 Creating a Connection Using JMXConnectorFactory.connectMXServiceURL u = new JMXServiceURL( "service:jmx:rmi:///jndi/rmi://" + hostName + ":" + portNum + "/jmxrmi"); JMXConnector c = JMXConnectorFactory.connect(u);
Setting up Monitoring and Management Programmatically
As stated previously, in the Java SE platform version 6, you can create a JMX client that uses the Attach API to enable out-of-the-box monitoring and management of any applications that are started on the Java SE 6 platform, without having to configure the applications for monitoring when you launch them. The Attach API provides a way for tools to attach to and start agents in the target application. Once an agent is running, JMX clients (and other tools) are able to obtain the JMX connector address for that agent via a property list that is maintained by the Java VM on behalf of the agents. The properties in the list are accessible from tools that use the Attach API. So, if an agent is started in an application, and if the agent creates a property to represent a piece of configuration information, then that configuration information is available to tools that attach to the application.
The JMX agent creates a property with the address of the local JMX connector server. This allows JMX tools to attach to and get the connector address of an agent, if it is running.
Example 2-4 shows code that could be used in a JMX tool to attach to a target VM, get the connector address of the JMX agent and connect to it.
Example 2-4 Attaching a JMX tool to a connector and getting the agent's addressstatic final String CONNECTOR_ADDRESS = "com.sun.management.jmxremote.localConnectorAddress"; // attach to the target application VirtualMachine vm = VirtualMachine.attach(id); // get the connector address String connectorAddress = vm.getAgentProperties().getProperty(CONNECTOR_ADDRESS); // no connector address, so we start the JMX agent if (connectorAddress == null) { String agent = vm.getSystemProperties().getProperty("java.home") + File.separator + "lib" + File.separator + "management-agent.jar"; vm.loadAgent(agent); // agent is started, get the connector address connectorAddress = vm.getAgentProperties().getProperty(CONNECTOR_ADDRESS); } // establish connection to connector server JMXServiceURL url = new JMXServiceURL(connectorAddress); JMXConnector = JMXConnectorFactory.connect(url);
Example 2-4 uses the com.sun.tools.attach.VirtualMachine class's attach() method to attach to a given Java VM so that it can read the properties that the target Java VM maintains on behalf of any agents running in it. If an agent is already running, then the VirtualMachine class's getAgentProperties() method is called to obtain the agent's address. The getAgentProperties() method returns a string property for the local connector address com.sun.management.jmxremote.localConnectorAddress, that you can use to connect to the local JMX agent.
If no agent is running already, then one is loaded by the VirtualMachine from JRE_HOME/lib/management-agent.jar, and its connector address is obtained by getAgentProperties().
A connection to the agent is then established by calling JMXConnectorFactory.connect on a JMX service URL that has been constructed from this connector address.
Mimicking Out-of-the-Box Management Using the JMX Remote API
As explained above, remote access to the out-of-the-box management agent is protected by authentication and authorization, and by SSL encryption, and all configuration is performed by setting system properties or by defining a management.properties file. In most cases, using the out-of-the-box management agent and configuring it through the management.properties file is more than sufficient to provide secure management of remote Java VMs. However, in some cases greater levels of security are required and in other cases certain system configurations do not allow the use of a management.properties file. Such cases might involve exporting the RMI server's remote objects over a certain port to allow passage through a firewall, or exporting the RMI server's remote objects using a specific network interface in multi-homed systems. For such cases, the behavior of the out-of-the-box management agent can be mimicked by using the JMX Remote API directly to create, configure and deploy the management agent programmatically.
Example of Mimicking Out-of-the-Box Management
This section provides an example of how to implement a JMX agent that identically mimics an out-of-the-box management agent. In exactly the same way as the out-of-the-box management agent, the agent created in Example 2-5 will run on port 3000, will have a password file named password.properties, an access file named access.properties and it will implement the default configuration for SSL/TLS-based RMI Socket Factories, requiring server authentication only. This example assumes a keystore has already been created, as described in Using SSL. Information about how to set up the SSL configuration can be found in the JSSE Reference Guide.
To enable monitoring and management on an application named com.example.MyApp using the out-of-the-box JMX agent with the configuration described above, you would run com.example.MyApp with the following command.
% java -Dcom.sun.management.jmxremote.port=3000 \ -Dcom.sun.management.jmxremote.password.file=password.properties \ -Dcom.sun.management.jmxremote.access.file=access.properties \ -Djavax.net.ssl.keyStore=keystore \ -Djavax.net.ssl.keyStorePassword=password \ com.example.MyApp
Note - The com.sun.management.jmxremote.* properties could have been specified in a management.properties file instead of passing them at the command line. In that case, the system property -Dcom.sun.management.config.file=management.properties would be required to specify the location of the management.properties file.
Example 2-5 shows the code you would need to write to create programmatically a JMX agent that will allow exactly the same monitoring and management on com.example.MyApp as would be possible using the command above.
Example 2-5 Mimicking an Out-of-the-Box JMX Agent Programmaticallypackage com.example; import java.lang.management.*; import java.rmi.registry.*; import java.util.*; import javax.management.*; import javax.management.remote.*; import javax.management.remote.rmi.*; import javax.rmi.ssl.*; public class MyApp { public static void main(String[] args) throws Exception { // Ensure cryptographically strong random number generator used // to choose the object number - see java.rmi.server.ObjID // System.setProperty("java.rmi.server.randomIDs", "true"); // Start an RMI registry on port 3000. // System.out.println("Create RMI registry on port 3000"); LocateRegistry.createRegistry(3000); // Retrieve the PlatformMBeanServer. // System.out.println("Get the platform's MBean server"); MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); // Environment map. // System.out.println("Initialize the environment map"); HashMap<String,Object> env = new HashMap<String,Object>(); // Provide SSL-based RMI socket factories. // // The protocol and cipher suites to be enabled will be the ones // defined by the default JSSE implementation and only server // authentication will be required. // 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); // Provide the password file used by the connector server to // perform user authentication. The password file is a properties // based text file specifying username/password pairs. // env.put("jmx.remote.x.password.file", "password.properties"); // Provide the access level file used by the connector server to // perform user authorization. The access level file is a properties // based text file specifying username/access level pairs where // access level is either "readonly" or "readwrite" access to the // MBeanServer operations. // env.put("jmx.remote.x.access.file", "access.properties"); // Create an RMI connector server. // // As specified in the JMXServiceURL the RMIServer stub will be // registered in the RMI registry running in the local host on // port 3000 with the name "jmxrmi". This is the same name the // out-of-the-box management agent uses to register the RMIServer // stub too. // System.out.println("Create an RMI connector server"); JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://:3000/jmxrmi"); JMXConnectorServer cs = JMXConnectorServerFactory.newJMXConnectorServer(url, env, mbs); // Start the RMI connector server. // System.out.println("Start the RMI connector server"); cs.start(); } }
Start this application with the following command.
java -Djavax.net.ssl.keyStore=keystore \ -Djavax.net.ssl.keyStorePassword=password \ com.example.MyApp
The com.example.MyApp application will enable the JMX agent and will be able to be monitored and managed in exactly the same way as if the Java platform's out-of-the-box management agent had been used. However, there is one slight but important difference between the RMI registry used by the out-of-the-box management agent and the one used by a management agent that mimics it. The RMI registry used by the out-of-the-box management agent is read-only, namely a single entry can be bound to it and once bound this entry cannot be unbound. This is not true of the RMI registry created in Example 2-5.
Furthermore, both RMI registries are insecure as they do not use SSL/TLS. The RMI registries should be created using SSL/TLS-based RMI socket factories which require client authentication. This will prevent a client from sending its credentials to a rogue RMI server and will also prevent the RMI registry from giving access to the RMI server stub to a non-trusted client.
RMI registries which implement SSL/TLS RMI socket factories can be created by adding the following properties to your management.properties file.
com.sun.management.jmxremote.registry.ssl=true com.sun.management.jmxremote.ssl.need.client.auth=true
Example 2-5 mimics the main behavior of the out-of-the-box JMX agent, but does not replicate all the existing properties in the management.properties file. However, you could add other properties by modifying com.example.MyApp appropriately.
Monitoring Applications through a Firewall
As stated above, the code in Example 2-5 can be used to monitor applications through a firewall, which might not be possible if you use the out-of-the-box monitoring solution. The com.sun.management.jmxremote.port management property specifies the port where the RMI Registry can be reached but the ports where the RMIServer and RMIConnection remote objects are exported is chosen by the RMI stack. To export the remote objects (RMIServer and RMIConnection) to a given port you need to create your own RMI connector server programmatically, as described in Example 2-5. However, you must specify the JMXServiceURL as follows:
JMXServiceURL url = new JMXServiceURL("service:jmx:rmi://localhost:" + port1 + "/jndi/rmi://localhost:" + port2 + "/jmxrmi");
In the URL above, port1 is the port number on which the RMIServer and RMIConnection remote objects are exported and port2 is the port number of the RMI Registry.
Using an Agent Class to Instrument an Application
The Java SE platform provides services that allow Java programming language agents to instrument programs running on the Java VM. Creating an instrumentation agent means you do not have to add any new code to your application in order to allow it to be monitored. Instead of implementing monitoring and management in your application's static main method you implement it in a separate agent class, and start your application with the -javaagent option specified. See the API reference documentation for the java.lang.instrument package for full details about how to create an agent class to instrument your applications.
The following procedure shows how you can adapt the code of com.example.MyApp to make an agent to instrument any other application for monitoring and management.
Creating an Agent Class to Instrument an Application
- Create a com.example.MyAgent class.
Create a class called com.example.MyAgent, declaring a premain method rather than a main method.
package com.example; [...] public class MyAgent { public static void premain(String args) throws Exception { [...]
The rest of the code for the com.example.MyAgent class can be exactly the same as the com.example.MyApp class shown in Example 2-5.
- Compile the com.example.MyAgent class.
- Create a manifest file, MANIFEST.MF, with a
Premain-Class entry.
An agent is deployed as a Java archive (JAR) file. An attribute in the JAR file manifest specifies the agent class which will be loaded to start the agent. Create a file called MANIFEST.MF, containing the following line.
Premain-Class: com.example.MyAgent
- Create a JAR file, MyAgent.jar.
The JAR file should contain the following files.
-
META-INF/MANIFEST.MF
-
com/example/MyAgent.class
-
- Start an application, specifying the agent to provide
monitoring and management services.
You can use com.example.MyAgent to instrument any application for monitoring and management. This example uses the Notepad application.
% java -javaagent:MyAgent.jar -Djavax.net.ssl.keyStore=keystore \ -Djavax.net.ssl.keyStorePassword=password -jar Notepad.jar
The com.example.MyAgent agent is specified using the -javaagent option when you start Notepad. Also, if your com.example.MyAgent application replicates the same code as the com.example.MyApp application shown in Example 2-5, then you will need to provide the keystore and password because the RMI connector server is protected by SSL.