2 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 ready-to-use 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. See Table 2-1 for the full set of ready-to-use management properties. You can also set system properties in a configuration file, as described in the Ready-to-Use Monitoring and Management Properties section.

Note:

To run the Java VM from the command line, you must add JAVA_HOME/bin to your path, where JAVA_HOME is the directory where the JDK is installed . Alternatively, you can enter the full path when you enter the command.

The syntax and the full set of command-line options supported by the Java HotSpot VMs are described in the java section of Java Development Kit Tool Specifications.

Ready-to-Use Management

With the current Java SE platform, any application can be monitored and managed locally when required through the Attach API (no need to specify -Dcom.sun.management.jmxremote). However, you need to enable and configure remote monitoring.

Note:

On Windows platforms, for security reasons, local monitoring and management is supported only 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. In production environments, be cautious that JConsole itself may affect the platform being monitored.

To perform local monitoring using JConsole, start the tool by entering 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 that you want to monitor. Both JConsole and the application must by executed by the same user, because 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 enter the command. See Using JConsole.

Remote Monitoring and Management

RMI is the transport for remote connection. By default, the remote stubs for locally created remote objects that are sent to clients contain the IP address of the local host in dotted-quad format. For remote stubs to be associated with a specific interface address, the java.rmi.server.hostname system property must be set to IP address of that interface.

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
Where, portNum is the port number to enable JMX RMI connections. Ensure that you 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 the name, jmxrmi. The port number to which the RMI connector will be bound using the system property:
com.sun.management.jmxremote.rmi.port

Ensure to use an unused port number.

Note:

You must set the prior system property in addition to any properties that you might set for security.
You can also configure the third port that accepts local JMX connections by using the system property:
com.sun.management.jmxremote.local.port

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.

Note:

For production systems, use both SSL client certificates to authenticate the client host and password authentication for user management. See Using SSL and Using LDAP Authentication.

The Java platform supports pluggable login modules for authentication. You can plug in any login module depending on the authentication infrastructure in your organization. Using LDAP Authentication describes how to plug in the com.sun.security.auth.module.LdapLoginModule module 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

This section details different password authentication methods.

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 recommended that you use SSL client certificates for authentication or plug in a secure login configuration.

Note:

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 start 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. As 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. See Using Password and Access Files.

Setting Up the Password File

You set up the password file in the JAVA_HOME/conf/management directory as follows:

  1. Copy the password template file jmxremote.password.template to jmxremote.password.
  2. Set file permissions so that only the owner can read and write the password file.
  3. Add passwords for roles such as monitorRole and controlRole.

If the JDK is used for a single purpose or all invocations need to use the same roles and passwords for monitoring, editing jmxremote.password in the conf/management directory is appropriate.

If an instance of the JDK is to run with a specific jmxremote.password file:

  1. Copy the template to jmxremote.password in some other location (not within the JDK directory).
  2. Set file permissions so that only the user starting the Java application can read and write the password file.
  3. Set the following system property when you start the Java VM:
    com.sun.management.jmxremote.password.file=pwFilePath

    Where 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

Note:

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 can also disable passwords, but use SSL client authentication, as described in Enabling SSL Client 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 class.

Create a JAAS configuration file that works in the required business organization. Here is an example of a configuration file (ldap.config) :

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;
    };

Here is an overview of the options mentioned in the configuration file:

  • The com.sun.security.auth.module.LdapLoginModule REQUIRED option means that authentication using LdapLoginModule is required for the 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 user name before the filter is used to search the directory.
  • The authzIdentity option specifies the access role for authenticated users. In the example, authenticated users will have the monitorRole option. See Access Files.

The details of the configuration options mentioned in the code example is explained in the com.sun.security.auth.module.LdapLoginModule class.

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: This property specifies the path to the JAAS configuration file.

Here is a sample command line:

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 SSL

SSL is enabled by default when you enable remote monitoring and management, but it needs to be configured.

A keystore is a secure repository of cryptographic keys or trusted certificates. A truststore is where we place the certificates that we trust. The keytool command can create and manipulate both keystores and truststores. It can work on a default store for the user, or a store specified with a command-line option.

Complete explanation of configuring and customizing SSL is beyond the scope of this document, but you can refer to: in Java Platform, Standard Edition Security Developer's Guide.

Example Scenarios of Configuring SSL

Here are few example scenarios that will help you to configure SSL.

Use a self-signed certificate pair at the JMX agent (server)

  1. Create a self-signed certificate pair on the JMX server:
    keytool -genkeypair -dname "CN=My Name, OU=Department, O=Company, L=City, S=State,
            C=Country" -alias jmxservercert -keyalg rsa –keystore /path/to/keystore -storepass
            mystorepass

    Specifying the location of the keystore is optional. The keytool uses a default keystore file .keystore in the home directory. However, it is recommended to specify the keystore location as there could be different keystores for different purposes.

  2. Export the certificate as a file. This exports the public key from the generated certificate pair:
    keytool -export -alias jmxservercert –keystore /path/to/keystore -storepass mystorepass
            -rfc -file exported_server_cert
  3. Copy the exported certificate to the client (where the attaching tool executes) and import into a truststore:
    keytool -importcert -alias jmxservercert -file exported_server_cert -storepass mystorepass
            -keystore myTruststore 
  4. Run the server, specifying the keystore and the specific port where JMX is enabled:
    java -Djavax.net.ssl.keyStore=/path/to/keystore
            -Djavax.net.ssl.keyStorePassword=mystorepass -Dcom.sun.management.jmxremote.port=<<PORTNUMBER>>
            MyJavaApp
  5. Run the client tool, for example JConsole:
    jconsole -J-Djavax.net.ssl.trustStore=myTruststore
            -J-Djavax.net.ssl.trustStorePassword=mystorepass
  6. In the New Connection dialog, specify host:port and the configured role name and password. See Using Password and Access Files.
Another option for importing the server certificate at the client is to import into the default truststore. This is within the JDK directory, so may not be appropriate if the JDK is shared, but could be easier as there is no need to specify a truststore location or password when running JConsole. For example:
keytool -importcert -file exported_server_cert

Enabling SSL client authentication

In the Use a self-signed certificate pair at the JMX agent (server) section, the server public certificate proves the identity of the server. In this section, the following procedure permits two-way SSL authentication, where additionally the server has a public certificate from the client as proof of its identity.

Before you begin, create a self-signed certificate pair at the JMX agent (server) and import that to the truststore of the client, as in the steps above. Then:

  1. At the client, generate a key pair:
    keytool -genkeypair -dname "CN=My Name, OU=Department, O=Company, L=City, S=State,
            C=Country" -alias jmxservercert -keyalg rsa -storepass mystorepass
  2. Export the key pair:
    keytool -export -alias jmxclientcert -storepass mystorepass -rfc -file exported_client_cert
  3. At the JMX server, import
    keytool -importcert -alias jmxclientcert -file exported_client_cert -keystore myTruststore
            -storepass mystorepass 
  4. Run the JMX server specifying the truststore, and the com.sun.management.jmxremote.ssl.need.client.auth property:
    java -Djavax.net.ssl.trustStore=myTruststore 
    -Djavax.net.ssl.trustStorePassword=mystorepass
    -Djavax.net.ssl.keyStore=/path/to/keystore 
    -Djavax.net.ssl.keyStorePassword=mystorepass
    -Dcom.sun.management.jmxremote.port=PORTNUMBER  
    -Dcom.sun.management.jmxremote.ssl.need.client.auth=true MyJavaApp
  5. Run JConsole (the client):
    jconsole -J-Djavax.net.ssl.trustStore=myTruststore
            -J-Djavax.net.ssl.trustStorePassword=mystorepass

SSL with CA-signed certificates

When a certificate is signed by a Certificate Authority, it can be verified without being exported and imported to the other party.

The general procedure to set up SSL is as follows:

  1. If you do not have a key pair and certificate set up on the server, then perform the following tasks:
    • Generate a key pair with the keytool -genkey command.

    • Request a signed certificate from a certificate authority (CA) with the keytool -certreq command. This creates a Certificate Signing Request (CSR).

    • Import the certificate into your keystore with the keytool -import command. See the Importing Certificates in keytool documentation.

  2. Configure SSL on the server system. Complete explanation of configuring and customizing SSL is beyond the scope of this document, but you generally need to set the system properties as described in the following list:
  3. Setting system properties is detailed in the Setting System Properties section.
  4. With this certificate on the server, the client can trust the server. Similarly, the client can create a key pair and then a CSR to have it signed by a CA. Then, the server can trust the client and the com.sun.management.jmxremote.ssl.need.client.auth property can be set to true.

See:

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 ready-to-use management agent when the Java VM is started. The default value of this property is false. However, it is recommended that you set this property to true. If this property is set to true, then to have full security, you must also enable SSL client authentication.

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 (default is set to false) to use client SSL authentication. It is recommended that you set this property to true. This configuration requires that the client system 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

Note:

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 that 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 remote client can 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 Enabled

To monitor a remote application with SSL enabled, you need to set up the truststore file on the system where JConsole is running and configure SSL properly. For example, you can create a keystore file 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

See Customizing the Default Keystores and Truststores, Store Types, and Store Passwords in the Java Platform, Standard Edition Security Developer's Guide.

If you create the keystore file and start the Server applicaton, then start JConsole as follows:

% jconsole -J-Djavax.net.ssl.trustStore=truststore \
  -J-Djavax.net.ssl.trustStorePassword=trustword

See Using JConsole.

The configuration authenticates the server only. If SSL client authentication is set up, then you need to provide a similar keystore file for JConsole's keys and an appropriate truststore file for the application.

Using Password and Access Files

The password and access files control security for remote monitoring and management. These files are located by default in JAVA_HOME/conf/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 JDK contains a password file template named jmxremote.password.template. Copy this file to JAVA_HOME/conf/management/jmxremote.password in 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, because it contains the passwords in clear-text. For security reasons, the system checks that the file is readable only 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 a private location such as your home directory.

Property names are roles, and the associated value is the role's password. Example 2–1 shows 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 Linux or macOS operating systems, you can set the file permissions for the password file by running the following command:

chmod 600 jmxremote.password
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 either be readonly or readwrite.

The access file defines roles and their access levels. By default, the access file defines the 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: Grants access to read the 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: Grants access to read and write the MBean's attributes, and to call operations on them. This access should be granted only to trusted clients, because they can potentially interfere with the operation of an application. The readwrite access level can optionally be followed by the create or unregister keywords. The unregister keyword grants access to unregister (delete) MBeans. The create keyword grants access to create MBeans of a particular class or of any class matching a particular pattern. Access should only be granted to create MBeans of known and trusted classes.

A role should have only one entry in the access file. If a role has no entry, then it has no access. If a role has multiple entries, then the last entry takes precedence. Typical predefined roles in the access file resemble what is shown in the Example 2-2.

Example 2-2 Example Access File

# The "monitorRole" role has readonly access.
# The "controlRole" role has readwrite access.
monitorRole readonly
controlRole readwrite

In Example 2-3, the entry grants readwrite access to controlRole. It also provides access to create MBeans of the class javax.management.monitor.CounterMonitor and to unregister any MBean.

Example 2-3 Example using create and unregister

controlRole readwrite \
             create javax.management.monitor.CounterMonitorMBean \
             unregister

Remote Monitoring with JConsole with SSL Disabled

To monitor a remote application with SSL disabled, start the 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.

Ready-to-Use Monitoring and Management Properties

You can set ready-to-use 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 JAVA_HOME/conf/management/management.properties. The Java VM reads this file if any of the command-line properties is set:

  • com.sun.management.jmxremote

    or
  • com.sun.management.jmxremote.port

    or
  • com.sun.management.jmxremote.local.port

You can specify a different location for the configuration file with the following command-line option:

com.sun.management.config.file=ConfigFilePath

ConfigFilePath is the path to the configuration file.

Table 2-1 describes the ready-to-use monitoring and management properties.

Table 2-1 Ready-to-Use Monitoring and Management Properties

Property Description Values

com.sun.management.jmxremote

Enables the JMX remote agent and local monitoring using a JMX connector. This agent is published on a private interface that is used by JConsole and any other local JMX clients, which use the Attach API. JConsole can use this connector if it is started by the same user who started the agent. No password or access files are checked for requests coming from 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 that is protected by SSL.

true / false. Default is false.

com.sun.management.jmxremote.ssl

Enables secure monitoring using SSL. If the value is false, then SSL is not used.

true / false. Default is true.

com.sun.management.jmxremote.ssl.enabled.protocols

Shows 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

Shows 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

Performs client authentication if this property is true and the property com.sun.management.jmxremote.ssl is also true.

It is recommended that you set this property to true.

true / false. Default is false.

com.sun.management.jmxremote.authenticate

Prevents JMX from using password or access files if this property is false. All users are provided complete access.

true / false. Default is true.

com.sun.management.jmxremote. password.file

Specifies the location for the 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.

JAVA_HOME/conf/management/ jmxremote.password

com.sun.management.jmxremote.access.file

Specifies the 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.

JAVA_HOME/conf/management/jmxremote.access

com.sun.management.jmxremote.login.config

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.

com.sun.management.jmxremote.rmi.port

Specifies the port number to which the RMI connector will be bound.

Port number. Ensure to use an unused port number.

com.sun.management.jmxremote.local.port

Specifies the local port number that accepts local JMX connections.

Port number. Ensure to use an unused port number.

Configuration Errors

If any errors occur during the start up of the MBean server, the RMI registry, or the connector, then 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

After 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, as shown in the Example 2-3.

Example 2-3 Creating a Connection Using JMXConnectorFactory.connect

JMXServiceURL u = new JMXServiceURL(
  "service:jmx:rmi:///jndi/rmi://" + hostName + ":" + portNum +  "/jmxrmi");
  JMXConnector c = JMXConnectorFactory.connect(u); 

Setting Up Monitoring and Management Programmatically

You can create a JMX client that uses the Attach API to enable ready-to-use monitoring and management of any applications that are started on the Java SE platform, without having to configure the applications for monitoring when you start them. The Attach API provides a way for tools to attach to and start agents in the target application. After an agent is running, JMX clients (and other tools) are able to obtain the JMX connector address for that agent using 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 Address

static 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) {
     vm.startLocalManagementAgent();

   // agent is started, get the connector address
   connectorAddress =
       vm.getAgentProperties().getProperty(CONNECTOR_ADDRESS);
}
// establish connection to connector server
JMXServiceURL url = new JMXServiceURL(connectorAddress);
JMXConnector 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, which you can use to connect to the local JMX agent.

If no agent is running, then one is loaded by the VirtualMachine class and its connector address is obtained by the getAgentProperties() method.

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.

Note:

Previous to JDK 11, the Attach API had issues locating JVMs running in docker containers. This is now fixed, and jcmd and jps work as expected. However, jmc will not list java processes running in separate docker containers. There is no known way to explicitly provide the PID of the java process to this tool.

Mimicking Ready-to-Use Management Using the JMX Remote API

The remote access to the ready-to-use management agent is protected by authentication and authorization, and by SSL encryption. The configuration is performed by setting system properties or by defining a management.properties file. In most cases, using the ready-to-use management agent and configuring it through the management.properties file is 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 multihomed systems. For such cases, the behavior of the ready-to-use management agent can be mimicked by using the JMX Remote API directly to create, configure, and deploy the management agent programmatically.

Example of Mimicking Ready-to-Use Management

This section provides an example of how to implement a JMX agent that identically mimics an ready-to-use management agent. In exactly the same way as the ready-to-use management agent, the agent created in Example 2-5 will run on port 3000. It 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 is explained in Creating a Keystore to Use with JSSE section of Java Platform, Standard Edition Security Developer's Guide.

To enable monitoring and management on an application named com.example.MyApp, using the ready-to-use JMX agent with the configuration, run the 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 can be 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 is required to specify the location of the management.properties file.

Example 2-5 shows the code that you need to write to programmatically create a JMX agent, which will allow exactly the same monitoring and management on com.example.MyApp as using the prior command.

Example 2-5 Mimicking a Ready-to-Use JMX Agent Programmatically

package 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 that the
        // ready-to-use management agent uses to register the RMIServer
        // stub.
        //
        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 monitored and managed in exactly the same way as if the Java platform's ready-to-use management agent has been used. However, there is one slight but important difference between the RMI registry used by the ready-to-use management agent and the one used by a management agent that mimics it. The RMI registry used by the ready-to-use management agent is read-only, namely a single entry can be bound to it and upon being bound, this entry cannot be unbound. This is not true with 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 that 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 nontrusted client.

RMI registries that 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 ready-to-use JMX agent, but does not replicate all the existing properties in the management.properties file. However, you can add other properties by modifying com.example.MyApp appropriately.

Monitoring Applications Through a Firewall

The code in Example 2-5 can be used to monitor applications through a firewall, which might not be possible if you use the ready-to-use 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 JMXServiceURL as follows:

JMXServiceURL url = new JMXServiceURL("service:jmx:rmi://localhost:" + 
      port1  + "/jndi/rmi://localhost:" + port2 + "/jmxrmi");

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 that 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.

Creating an Agent Class to Instrument an Application

The following procedure shows how you can adapt the code of com.example.MyApp to create an agent to instrument any other application for monitoring and management.

  1. 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 is same as the com.example.MyApp class as shown in Example 2-5.

  2. Compile the com.example.MyAgent class.
  3. 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 that will be loaded to start the agent. Create a file called MANIFEST.MF, containing the following line:

    Premain-Class: com.example.MyAgent
    
  4. Create a JAR file, MyAgent.jar.

    The JAR file should contain the following files:

    • META-INF/MANIFEST.MF

    • com/example/MyAgent.class

  5. 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 provide the keystore and password information because the RMI connector server is protected by SSL.