Connecting to the JMX Server
JBoss includes adaptors that allow access to the JMX MBeanServer from outside the JBoss server VM. The current adaptors include HTML, an RMI interface, and an EJB.
Inspecting the Server: The JMX Console Web Application
JBoss comes with its own implementation of a JMX HTML adaptor that allows you to view the server's MBeans, using a standard web browser. The default URL for the console web application is http://localhost:8080/jmx-console/. If you browse to this location, you will see something similar to what is presented in Figure 2.12.
The top view is called the agent view, and it provides a listing of all MBeans registered with the MBeanServer, sorted by the domain portion of the MBean's ObjectName. Under each domain you can see the MBeans that are in that domain. When you select one of the MBeans, you are taken to the MBean view, where you can view and edit an MBean's attributes as well as invoke operations. As an example, Figure 2.13 shows the MBean view for the jboss.system:type=Server MBean.
The source code for the JMX Console web application is located in the varia module, under the src/ main/org/jboss/jmx directory. Its web pages are located under varia/src/resources/jmx. The application is a simple MVC servlet with JSP views that utilize the MBeanServer.
Securing the JMX Console
Because the JMX Console web application is just a standard servlet, it can be secured using standard J2EE role-based security. The jmx-console.war is deployed as an unpacked WAR that includes template settings for quickly enabling simple username- and password-based access restrictions. If you look at the jmx-console.war in the server/default/deploy directory, you will find the web.xml and jboss-web.xml descriptors in the WEB-INF directory and jmx-console-roles.properties and jmx-console-users.properties files under WEB-INF/classes.
By uncommenting the security sections of the web.xml and jboss-web.xml descriptors, as shown in Listing 2.10 and Listing 2.11, you enable HTTP basic authentication that restricts access to the JMX Console application to the user admin with password admin. The username and password are determined by the admin=admin line in the jmx-console-users.properties file.
Listing 2.10. The jmx-console.war web.xml Descriptors, with the Security Elements Uncommented
<?xml version="1.0"?>
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<!-- ... -->
<!-- A security constraint that restricts access to the HTML JMX console
to users with the role JBossAdmin. Edit the roles to what you want and
uncomment the WEB-INF/jboss-web.xml/security-domain element to enable
secured access to the HTML JMX console.
-->
<security-constraint>
<web-resource-collection>
<web-resource-name>HtmlAdaptor</web-resource-name>
<description> An example security config that only allows users with
the role JBossAdmin to access the HTML JMX console web
application </description>
<url-pattern>/*</url-pattern>
<http-method>GET</http-method>
<http-method>POST</http-method>
</web-resource-collection>
<auth-constraint>
<role-name>JBossAdmin</role-name>
</auth-constraint>
</security-constraint>
<login-config>
<auth-method>BASIC</auth-method>
<realm-name>JBoss JMX Console</realm-name>
</login-config>
<security-role>
<role-name>JBossAdmin</role-name>
</security-role>
</web-app>
Listing 2.11. The jmx-console.war jboss-web.xml Descriptors, with the Security Elements Uncommented
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE jboss-web
PUBLIC "-//JBoss//DTD Web Application 2.3//EN"
"http://www.jboss.org/j2ee/dtd/jboss-web_3_0.dtd">
<jboss-web>
<!--
Uncomment the security-domain to enable security. You will
need to edit the htmladaptor login configuration to set up the
login modules used to authenticate users.
-->
<security-domain>java:/jaas/jmx-console</security-domain>
</jboss-web>
Make these changes to uncomment the security code, and then when you try to access the JMX Console URL, you will see a dialog similar to the one shown in Figure 2.14.
By default JBoss uses properties files for securing access to the JMX Console application. To see how to better configure the security settings of web applications, see Chapter 8, "Security on JBoss."
Connecting to JMX Using RMI
JBoss supplies an RMI interface for connecting to the JMX MBeanServer. This interface is org.jboss.jmx.adaptor.rmi.RMIAdaptor. The RMIAdaptor interface is bound into JNDI in the default location jmx/invoker/RMIAdaptor, as well as jmx/rmi/RMIAdaptor for backward compatibility with older clients.
Listing 2.12 shows a client that makes use of the RMIAdaptor interface to query the MBeanInfo for the JNDIView MBean. It also invokes the MBean's list(boolean) method and displays the result.
Listing 2.12. A JMX Client That Uses the RMIAdaptor Interface
public class JMXBrowser
{
/**
* @param args the command line arguments
*/
public static void main(String[] args) throws Exception
{
InitialContext ic = new InitialContext();
RMIAdaptor server = (RMIAdaptor) ic.lookup("jmx/invoker/RMIAdaptor");
// Get the MBeanInfo for the JNDIView MBean
ObjectName name = new ObjectName("jboss:service=JNDIView");
MBeanInfo info = server.getMBeanInfo(name);
System.out.println("JNDIView Class: " + info.getClassName());
MBeanOperationInfo[] opInfo = info.getOperations();
System.out.println("JNDIView Operations: ");
for(int o = 0; o < opInfo.length; o ++) {
MBeanOperationInfo op = opInfo[o];
String returnType = op.getReturnType();
String opName = op.getName();
System.out.print(" + " + returnType + " " + opName + "(");
MBeanParameterInfo[] params = op.getSignature();
for(int p = 0; p < params.length; p++) {
MBeanParameterInfo paramInfo = params[p];
String pname = paramInfo.getName();
String type = paramInfo.getType();
if (pname.equals(type)) {
System.out.print(type);
} else {
System.out.print(type + " " + name);
}
if (p < params.length-1) {
System.out.print(',');
}
}
System.out.println(")");
}
// Invoke the list(boolean) op
String[] sig = {"boolean"};
Object[] opArgs = {Boolean.TRUE};
Object result = server.invoke(name, "list", opArgs, sig);
System.out.println("JNDIView.list(true) output:\n"+result);
}
}
To test the client access using the RMIAdaptor, you should run the following:
[examples]$ ant -Dchap=chap2 -Dex=4 run-example
...
run-example4:
[java] JNDIView Class: org.jboss.mx.modelmbean.XMBean
[java] JNDIView Operations:
[java] + java.lang.String list(boolean jboss:service=JNDIView)
[java] + java.lang.String listXML()
[java] + void create()
[java] + void start()
[java] + void stop()
[java] + void destroy()
[java] + void jbossInternalLifecycle(java.lang.String jboss:service=JNDIView)
[java] + java.lang.String getName()
[java] + int getState()
[java] + java.lang.String getStateString()
[java] JNDIView.list(true) output:
[java] <h1>java: Namespace</h1>
[java] <pre>
[java] +- XAConnectionFactory (class: org.jboss.mq.SpyXAConnectionFactory)
[java] +- DefaultDS (class: javax.sql.DataSource)
[java] +- SecurityProxyFactory (class: org.jboss.security.SubjectSecurityProxyFactory)
[java] +- DefaultJMSProvider (class: org.jboss.jms.jndi.JNDIProviderAdapter)
[java] +- comp (class: javax.naming.Context)
[java] +- JmsXA (class: org.jboss.resource.adapter.jms.JmsConnectionFactoryImpl)
[java] +- ConnectionFactory (class: org.jboss.mq.SpyConnectionFactory)
[java] +- jaas (class: javax.naming.Context)
[java] | +- JmsXARealm (class: org.jboss.security.plugins.SecurityDomainContext)
[java] | +- jbossmq (class: org.jboss.security.plugins.SecurityDomainContext)
[java] | +- HsqlDbRealm (class: org.jboss.security.plugins.SecurityDomainContext)
[java] +- timedCacheFactory (class: javax.naming.Context)
[java] Failed to lookup: timedCacheFactory, errmsg=null
[java] +- TransactionPropagationContextExporter (class: org.jboss.tm
.TransactionPropagationContextFactory)
[java] +- StdJMSPool (class: org.jboss.jms.asf.StdServerSessionPoolFactory)
[java] +- Mail (class: javax.mail.Session)
[java] +- TransactionPropagationContextImporter (class: org.jboss.tm
.TransactionPropagationContextImporter)
[java] +- TransactionManager (class: org.jboss.tm.TxManager)
[java] </pre>
[java] <h1>Global JNDI Namespace</h1>
[java] <pre>
[java] +- XAConnectionFactory (class: org.jboss.mq.SpyXAConnectionFactory)
[java] +- UIL2ConnectionFactory[link -> ConnectionFactory] (class: javax.naming
.LinkRef)
[java] +- UserTransactionSessionFactory (proxy: $Proxy11 implements interface org
.jboss.tm.usertx.interfaces.UserTransactionSessionFactory)
[java] +- HTTPConnectionFactory (class: org.jboss.mq.SpyConnectionFactory)
[java] +- console (class: org.jnp.interfaces.NamingContext)
[java] | +- PluginManager (proxy: $Proxy36 implements interface org.jboss.console
.manager.PluginManagerMBean)
[java] +- UIL2XAConnectionFactory[link -> XAConnectionFactory] (class: javax.naming
.LinkRef)
[java] +- UUIDKeyGeneratorFactory (class: org.jboss.ejb.plugins.keygenerator.uuid
.UUIDKeyGeneratorFactory)
[java] +- HTTPXAConnectionFactory (class: org.j boss.mq.SpyXAConnectionFactory)
[java] +- topic (class: org.jnp.interfaces.NamingContext)
[java] | +- testDurableTopic (class: org.jboss.mq.SpyTopic)
[java] | +- testTopic (class: org.jboss.mq.SpyTopic)
[java] | +- securedTopic (class: org.jboss.mq.SpyTopic)
[java] +- queue (class: org.jnp.interfaces.NamingContext)
[java] | +- A (class: org.jboss.mq.SpyQueue)
[java] | +- testQueue (class: org.jboss.mq.SpyQueue)
[java] | +- ex (class: org.jboss.mq.SpyQueue)
[java] | +- DLQ (class: org.jboss.mq.SpyQueue)
[java] | +- D (class: org.jboss.mq.SpyQueue)
[java] | +- C (class: org.jboss.mq.SpyQueue)
[java] | +- B (class: org.jboss.mq.SpyQueue)
[java] +- ConnectionFactory (class: org.jboss.mq.SpyConnectionFactory)
[java] +- UserTransaction (class: org.jboss.tm.usertx.client.ClientUserTransaction)
[java] +- jmx (class: org.jnp.interfaces.NamingContext)
[java] | +- invoker (class: org.jnp.interfaces.NamingContext)
[java] | | +- RMIAdaptor (proxy: $Proxy35 implements interface org.jboss.jmx
.adaptor.rmi.RMIAdaptor, interface org.jboss.jmx.adaptor.rmi.RMIAdaptorExt)
[java] | +- rmi (class: org.jnp.interfaces.NamingContext)
[java] | | +- RMIAdaptor[link -> jmx/invoker/RMIAdaptor] (class: javax.naming
.LinkRef)
[java] +- HiLoKeyGeneratorFactory (class: org.jboss.ejb.plugins.keygenerator.hilo
.HiLoKeyGeneratorFactory)
[java] +- UILXAConnectionFactory[link -> XAConnectionFactory] (class: javax.naming
.LinkRef)
[java] +- UILConnectionFactory[link -> ConnectionFactory] (class: javax.naming.LinkRef)
[java] </pre>
Command-Line Access to JMX
JBoss provides a simple command-line tool that allows for interaction with a remote JMX server instance. This tool is called Twiddle (for twiddling bits via JMX) and is located in the bin directory of the distribution. Twiddle is a command execution tool, not a general command shell. It is run using either the twiddle.sh script or the twiddle.bat script; and passing in a -h(--help) argument provides the basic syntax, and --help-commands shows what you can do with the tool:
[bin]$ ./twiddle.sh -h
A JMX client to 'twiddle' with a remote JBoss server.
usage: twiddle.sh [options] <command> [command_arguments]
options:
-h, --help Show this help message
--help-commands Show a list of commands
-H=<command> Show command specific help
-c=command.properties Specify the command.properties file to use
-D<name>[=<value>] Set a system property
-- Stop processing options
-s, --server=<url> The JNDI URL of the remote server
-a, --adapter=<name> The JNDI name of the RMI adapter to use
-q, --quiet Be somewhat more quiet
Connecting Twiddle to a Remote Server
By default the twiddle command connects to the local host at port 1099 to look up the default jmx/rmi/RMIAdaptor binding of the RMIAdaptor service as the connector for communicating with the JMX server. To connect to a different server/port combination, you can use the -s (--server) option:
[bin]$ ./twiddle.sh -s toki serverinfo -d jboss
[bin]$ ./twiddle.sh -s toki:1099 serverinfo -d jboss
To connect using a different RMIAdaptor binding, you use the -a (--adapter) option:
[bin]$ ./twiddle.sh -s toki -a jmx/rmi/RMIAdaptor serverinfo -d jboss
Sample Twiddle Command Usage
To access basic information about a server, you use the serverinfo command:
[bin]$ ./twiddle.sh -H serverinfo
Get information about the MBean server
usage: serverinfo [options]
options:
-d, --domain Get the default domain
-c, --count Get the MBean count
-l, --list List the MBeans
-- Stop processing options
[bin]$ ./twiddle.sh --server=toki serverinfo --count
460
[bin]$ ./twiddle.sh --server=toki serverinfo --domain
jboss
To query the server for the name of MBeans matching a pattern, you use the query command:
[bin]$ ./twiddle.sh -H query
Query the server for a list of matching MBeans
usage: query [options] <query>
options:
-c, --count Display the matching MBean count
-- Stop processing options
Examples:
query all mbeans: query '*:*'
query all mbeans in the jboss.j2ee domain: query 'jboss.j2ee:*'
[bin]$ ./twiddle.sh -s toki query 'jboss:service=invoker,*'
jboss:readonly=true,service=invoker,target=Naming,type=http
jboss:service=invoker,type=jrmp
jboss:service=invoker,type=local
jboss:service=invoker,type=pooled
jboss:service=invoker,type=http
jboss:service=invoker,target=Naming,type=http
To get the attributes of an MBean, you use the get command:
[bin]$ ./twiddle.sh -H get
Get the values of one or more MBean attributes
usage: get [options] <name> [<attr>+]
If no attribute names are given all readable attributes are retrieved
options:
--noprefix Do not display attribute name prefixes
-- Stop processing options
[bin]$ ./twiddle.sh get jboss:service=invoker,type=jrmp RMIObjectPort StateString
RMIObjectPort=4444
StateString=Started
[bin]$ ./twiddle.sh get jboss:service=invoker,type=jrmp
ServerAddress=0.0.0.0
RMIClientSocketFactoryBean=null
StateString=Started
State=3
RMIServerSocketFactoryBean=org.jboss.net.sockets.DefaultSocketFactory@ad093076
EnableClassCaching=false
SecurityDomain=null
RMIServerSocketFactory=null
Backlog=200
RMIObjectPort=4444
Name=JRMPInvoker
RMIClientSocketFactory=null
To query the MBeanInfo for an MBean, you use the info command:
[bin]$ ./twiddle.sh -H info
Get the metadata for an MBean
usage: info <mbean-name>
Use '*' to query for all attributes
[bin]$ Description: Management Bean.
+++ Attributes:
Name: ServerAddress
Type: java.lang.String
Access: rw
Name: RMIClientSocketFactoryBean
Type: java.rmi.server.RMIClientSocketFactory
Access: rw
Name: StateString
Type: java.lang.String
Access: r-
Name: State
Type: int
Access: r-
Name: RMIServerSocketFactoryBean
Type: java.rmi.server.RMIServerSocketFactory
Access: rw
Name: EnableClassCaching
Type: boolean
Access: rw
Name: SecurityDomain
Type: java.lang.String
Access: rw
Name: RMIServerSocketFactory
Type: java.lang.String
Access: rw
Name: Backlog
Type: int
Access: rw
Name: RMIObjectPort
Type: int
Access: rw
Name: Name
Type: java.lang.String
Access: r-
Name: RMIClientSocketFactory
Type: java.lang.String
Access: rw
+++ Operations:
void start()
void jbossInternalLifecycle(java.lang.String java.lang.String)
void create()
void stop()
void destroy()
To invoke an operation on an MBean, you use the invoker command:
[bin]$ ./twiddle.sh -H invoke
Invoke an operation on an MBean
usage: invoke [options] <query> <operation> (<arg>)*
options:
-q, --query-type[=<type>] Treat object name as a query
-- Stop processing options
query type:
f[irst] Only invoke on the first matching name [default]
a[ll] Invoke on all matching names
[bin]$ ./twiddle.sh invoke jboss:service=JNDIView list true
<h1>java: Namespace</h1>
<pre>
+- XAConnectionFactory (class: org.jboss.mq.SpyXAConnectionFactory)
+- DefaultDS (class: javax.sql.DataSource)
+- SecurityProxyFactory (class: org.jboss.security.SubjectSecurityProxyFactory)
+- DefaultJMSProvider (class: org.jboss.jms.jndi.JNDIProviderAdapter)
+- comp (class: javax.naming.Context)
+- JmsXA (class: org.jboss.resource.adapter.jms.JmsConnectionFactoryImpl)
+- ConnectionFactory (class: org.jboss.mq.SpyConnectionFactory)
+- jaas (class: javax.naming.Context)
| +- JmsXARealm (class: org.jboss.security.plugins.SecurityDomainContext)
| +- jbossmq (class: org.jboss.security.plugins.SecurityDomainContext)
| +- HsqlDbRealm (class: org.jboss.security.plugins.SecurityDomainContext)
+- timedCacheFactory (class: javax.naming.Context)
Failed to lookup: timedCacheFactory, errmsg=null
+- TransactionPropagationContextExporter (class: org.jboss.tm
.TransactionPropagationContextFactory)
+- StdJMSPool (class: org.jboss.jms.asf.StdServerSessionPoolFactory)
+- Mail (class: javax.mail.Session)
+- TransactionPropagationContextImporter (class: org.jboss.tm
.TransactionPropagationContextImporter)
+- TransactionManager (class: org.jboss.tm.TxManager)
</pre>
<h1>Global JNDI Namespace</h1>
<pre>
+- XAConnectionFactory (class: org.jboss.mq.SpyXAConnectionFactory)
+- UIL2ConnectionFactory[link -> ConnectionFactory] (class: javax.naming.LinkRef)
+- UserTransactionSessionFactory (proxy: $Proxy11 implements interface org.jboss.tm
.usertx.interfaces.UserTransactionSessionFactory)
+- HTTPConnectionFactory (class: org.jboss.mq.SpyConnectionFactory)
+- console (class: org.jnp.interfaces.NamingContext)
| +- PluginManager (proxy: $Proxy36 implements interface org.jboss.console.manager
.PluginManagerMBean)
+- UIL2XAConnectionFactory[link -> XAConnectionFactory] (class: javax.naming.LinkRef)
+- UUIDKeyGeneratorFactory (class: org.jboss.ejb.plugins.keygenerator.uuid
.UUIDKeyGeneratorFactory)
+- HTTPXAConnectionFactory (class: org.jboss.mq.SpyXAConnectionFactory)
+- topic (class: org.jnp.interfaces.NamingContext)
| +- testDurableTopic (class: org.jboss.mq.SpyTopic)
| +- testTopic (class: org.jboss.mq.SpyTopic)
| +- securedTopic (class: org.jboss.mq.SpyTopic)
+- queue (class: org.jnp.interfaces.NamingContext)
| +- A (class: org.jboss.mq.SpyQueue)
| +- testQueue (class: org.jboss.mq.SpyQueue)
| +- ex (class: org.jboss.mq.SpyQueue)
| +- DLQ (class: org.jboss.mq.SpyQueue)
| +- D (class: org.jboss.mq.SpyQueue)
| +- C (class: org.jboss.mq.SpyQueue)
| +- B (class: org.jboss.mq.SpyQueue)
+- ConnectionFactory (class: org.jboss.mq.SpyConnectionFactory)
+- UserTransaction (class: org.jboss.tm.usertx.client.ClientUserTransaction)
+- jmx (class: org.jnp.interfaces.NamingContext)
| +- invoker (class: org.jnp.interfaces.NamingContext)
| | +- RMIAdaptor (proxy: $Proxy35 implements interface org.jboss.jmx.adaptor.rmi
.RMIAdaptor, interface org.jboss.jmx.adaptor.rmi.RMIAdaptorExt)
| +- rmi (class: org.jnp.interfaces.NamingContext)
| | +- RMIAdaptor[link -> jmx/invoker/RMIAdaptor] (class: javax.naming.LinkRef)
+- HiLoKeyGeneratorFactory (class: org.jboss.ejb.plugins.keygenerator.hilo
.HiLoKeyGeneratorFactory)
+- UILXAConnectionFactory[link -> XAConnectionFactory] (class: javax.naming.LinkRef)
+- UILConnectionFactory[link -> ConnectionFactory] (class: javax.naming.LinkRef)
</pre>
Connecting to JMX Using Any Protocol
With the detached invokers and a somewhat generalized proxy factory capability, you can really talk to the JMX server by using the InvokerAdaptorService and a proxy factory service to expose an RMIAdaptor or similar interface over your protocol of choice. You'll learn about the detached invoker notion, along with proxy factories, later in this chapter, in the section "Remote Access to Services, Detached Invokers." See the section "A Detached Invoker Example, the MBeanServer Invoker Adaptor Service," later in this chapter, for an example of an invoker service that allows you to access the MBean server by using to the RMIAdaptor interface over any protocol for which a proxy factory service exists.
|