Using Spring JMX

JMX is quite a popular part of Java; this popularity has caused it to become part of the standard Java distribution as of Java 5.0. If you are unfamiliar with JMX, we recommend that you read the documentation available at http://java.sun.com/products/JavaManagement/.

Using Spring JMX you can expose your Spring beans as JMX resources without having to couple your beans to the JMX API. In addition to this, you can access JMX-managed resources through proxies, thus removing the need for your application to interact with the complex and loosely typed JMX client API.

Until recently, there was no standard mechanism for exposing JMX resources to remote clients; as a result, a variety of different adaptors emerged from a variety of vendors. With the advent of JSR-160, there is now a standard way for JMX resources to be exposed and accessed remotely. Spring JMX provides full support for exposing JSR-160 services and for accessing remote services using proxy classes.

In this section, we take a quick look at some of the basic functionality provided by Spring JMX.

Exposing Beans to JMX

The central class in Spring JMX is JmxMBeanAdapter, which orchestrates the entire process of creating JMX management interfaces for your beans, obtaining ObjectNames for your beans, and registering your beans with MBeanServer. The JmxMBeanAdapter class supports full declarative configuration, allowing you to expose your beans via JMX without writing a single line of Java code.

As an example of this, consider the JmxBean class shown in Listing D-9.

Listing D-9: The JmxBean Class

image from book
package com.apress.prospring.apd.jmx;      public class JmxBean {          private String name;          private int age;          private String favoriteColor;          public int getAge() {         return age;     }          public void setAge(int age) {         this.age = age;     }          public String getFavoriteColor() {         return favoriteColor;     }          public void setFavoriteColor(String favoriteColor) {         this.favoriteColor = favoriteColor;     }          public String getName() {         return name;     }          public void setName(String name) {         this.name = name;     } }
image from book

As you can see, this is just a plain JavaBean containing no JMX-specific code at all. To register this bean with MbeanServer, simply pass it into an instance of JmxMBeanAdapter using the beans property, as shown in Listing D-10.

Listing D-10: Exposing a Bean via JMX

image from book
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"  "http://www.springframework.org/dtd/spring-beans.dtd"> <beans>     <bean  >         <property name="beans">             <map>                 <entry key="spring:name=someBean">                     <ref local="someBean"/>                 </entry>             </map>         </property>         <property name="server">             <ref local="mbeanServer"/>             </property>     </bean>          <bean             />          <bean  >          <property name="name"><value>Rob Harrop</value></property>          <property name="age"><value>100</value></property>          <property name="favoriteColor"><value>Blue</value></property>      </bean> </beans> 
image from book

There are two interesting parts to this configuration. First, notice that we define a bean, mbeanServer, of type MBeanServerFactoryBean and use this to set the value of the JmxMBeanAdapter.server property. Internally, JmxMBeanAdapter attempts to locate a running instance of MBeanServer, which is useful when your application is running in an application server such as JBoss, which provides a default MBeanServer. However, when MBeanServer is not available, you should use MBeanServerFactoryBean to create one and pass it to JmxMBeanAdapter.

Second, notice that when passing in someBean to JmxMBeanAdapter via the beans property, the key of the Map entry is a JMX ObjectName. By default, JmxMBeanAdapter uses the key of the beans Map as the ObjectName when registering the bean with MBeanServer. As you will see in subsequent sections, this behavior can be modified.

In Listing D-11, you can see a simple example that loads the configuration shown in Listing D-10 and accesses someBean via JMX.

Listing D-11: Accessing someBean via JMX

image from book
package com.apress.prospring.apd.jmx;      import javax.management.Attribute; import javax.management.MBeanServer; import javax.management.ObjectName;      import org.springframework.context.ApplicationContext; import org.springframework.context.support.FileSystemXmlApplicationContext;      public class SimpleExample {          public static void main(String[] args) throws Exception {              ApplicationContext ctx = new FileSystemXmlApplicationContext(                 "./ap4/src/conf/jmx/simple.xml");              MBeanServer server = (MBeanServer) ctx.getBean("mbeanServer");         JmxBean bean = (JmxBean) ctx.getBean("someBean");              System.out.println(bean.getName());              // invoke JMX operation         server.setAttribute(ObjectName.getInstance("spring:name=someBean"),                 new Attribute("name", "Jan Machacek"));              System.out.println(bean.getName());     } } 
image from book

Here you can see that after loading ApplicationContext, we retrieve both the someBean and mbeanServer beans. Then we write out the value of the name property of someBean twice, in between which we change the value of this property using JMX. Running this code results in the following output:

Rob Harrop Jan Machacek

As you can see, the call to MBeanServer.setAttribute() changed the value of someBean, indicating that someBean was registered as a JMX resource under the spring:name=someBean ObjectName.

Accessing a Managed Resource Using a Proxy

As you saw from the example in Listing D-11, accessing JMX-managed resources can be messy and may cause you to lose all the compile-time benefits of the strongly typed nature of Java. Using Spring JMX, you can overcome this by creating a proxy to a JMX-managed resource using the JmxProxyFactoryBean class. Listing D-12 shows a bean declaration that you can add to the configuration shown in Listing D-10 to create a proxy to the spring:name=someBean MBean.

Listing D-12: Configuring a JMX Proxy

image from book
<bean  >     <property name="server">          <ref local="mbeanServer"/>     </property>     <property name="objectName">          <value>spring:name=someBean</value>     </property> </bean>
image from book

Here you can see that we pass in both the MBeanServer and the ObjectName of the resource we wish to proxy to JmxProxyFactoryBean. By default, JmxProxyFactoryBean attempts to determine the class of the managed resource and then creates a dynamic subclass of this. If you are using JmxProxyFactoryBean to access a remote resource and the managed resource class

is not accessible locally, then you can create an interface for the proxy and pass this to the JmxProxyFactoryBean class using the proxyInterfaces property.

In Listing D-13, you can see a simplified version of the code in Listing D-11 that uses the proxy defined in Listing D-12 to access the spring:name=someBean MBean.

Listing D-13: Using a JMX Proxy

image from book
package com.apress.prospring.apd.jmx;      import org.springframework.context.ApplicationContext; import org.springframework.context.support.FileSystemXmlApplicationContext;      public class WithProxyExample {          public static void main(String[] args) throws Exception {              ApplicationContext ctx = new FileSystemXmlApplicationContext(                 "./ap4/src/conf/jmx/simple.xml");              JmxBean bean = (JmxBean) ctx.getBean("someBean");         JmxBean proxy = (JmxBean) ctx.getBean("proxy");                  System.out.println(bean.getName());                  // invoke JMX operation         proxy.setName("Jan Machacek");                  System.out.println(bean.getName());     } }
image from book

The output of this example is the same as the output of the example shown in Listing D-11.

Defining the Management Interface Using Metadata

Up to now, all the JMX examples shown have exposed all methods and properties on JmxBean via JMX; this is the default behavior of JmxMBeanAdapter. Thankfully, you can customize this behavior using an implementation of the ModelMBeanInfoAssembler interface to which the JmxMBeanAdapter delegates. By default, JmxMBeanAdapter uses the ReflectiveModelMBeanInfoAssembler interface, which uses reflection to access all public methods and properties of a class and exposes them via JMX. Spring JMX comes complete with a second implementation of the ModelMBeanInfoAssembler interface, MetadataModelMBeanInfoAssembler, which uses source level metadata to determine which methods and properties should be exposed via JMX. At the time of writing, only Commons Attributes metadata is supported by Spring JMX, although JSR-175 metadata will be included with the final release.

Listing D-14 shows a modification of the JmxBean class complete with source level meta- data defining which properties should be exposed via JMX.

Listing D-14: Using Source Level Metadata to Define a JMX-Managed Interface

image from book
package com.apress.prospring.apd.jmx;      /**  * @@org.springframework.jmx.metadata.support.ManagedResource *   (description="My Managed Resource", objectName="spring:name=jmxBean") */ public class JmxBean {          private String name;          private int age;          private String favoriteColor;          /**      * @@org.springframework.jmx.metadata.support.ManagedAttribute      * (description="The Age Attribute")      */     public int getAge() {         return age;     }          /**      * @@org.springframework.jmx.metadata.support.ManagedAttribute      * (description="The Age Attribute")      */     public void setAge(int age) {         this.age = age;     }          /**      * @@org.springframework.jmx.metadata.support.ManagedAttribute      * (description="The Favourite Color Attribute")      */     public String getFavoriteColor() {         return favoriteColor;     }          public void setFavoriteColor(String favoriteColor) {         this.favoriteColor = favoriteColor;     }          /**      * @@org.springframework.jmx.metadata.support.ManagedAttribute      * (description="The Name Attribute")      */     public String getName() {         return name;     }          public void setName(String name) {         this.name = name;     } } 
image from book

Here you can see that the JmxBean class and various methods of the class are marked with metadata that defines them as part of the JMX management interface. The ManagedResource attribute used on the class itself defines the description of the managed resource that graphical JMX clients will display in addition to the ObjectName. The ManagedAttribute attribute used on some of the getXXX()/setXXX() methods is used to mark a method as part of the interface to a JMX attribute. Notice that you can also provide a description for the attributes as well. You can see that in some cases, we have not placed a ManagedAttribute on the setXXX() method of a property, making that property read-only when accessed via JMX. The ManagedAttribute attribute can only be used on getXXX()/setXXX() methods; to mark a normal method for exposure as a JMX operation, use the ManagedOperation attribute.

Using Commons Attributes requires an initial phase in the build process prior to compilation to generate additional Java source for the attributes. Listing D-15 shows a sample Ant build script that does just this.

Listing D-15: Compiling Commons Attributes

image from book
<project name="ap4" default="compile" basedir="../../../../../../../../">          <property name="spring.dir" value="${basedir}/../spring" />     <property name="src.dir" value="${basedir}/ap4/src/java" />     <property name="dest.dir" value="${basedir}/bin" />          <path >         <fileset dir="${spring.dir}/lib">             <include name="**/*.jar" />         </fileset>         <path location="${spring.dir}/target/sandbox/classes" />     </path>          <taskdef classpathref="project.classpath"               resource="org/apache/commons/attributes/anttasks.properties" />          <target name="compile">         <attribute-compiler destdir="${src.dir}">             <fileset dir="${src.dir}" includes="**/*.java" />         </attribute-compiler>         <javac source="1.4" srcdir="${src.dir}" destdir="${dest.dir}"                 classpathref="project.classpath" />     </target> </project>
image from book

In the sample code, this build file sits in the same directory as the JmxBean source file. Listing D-16 shows how to configure JmxMBeanAdapter to use the MetadataModelMBeanInfoAssembler class to assemble the management interface for your beans.

Listing D-16: Configuring MetadataModelMBeanInfoAssembler

image from book
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"  "http://www.springframework.org/dtd/spring-beans.dtd"> <beans>     <bean  >         <property name="namingStrategy">             <ref local="metadataNamingStrategy"/>         </property>         <property name="assembler">             <ref local="metadataAssembler"/>         </property>         <property name="server">             <ref local="mbeanServer"/>         </property>     </bean>          <bean             />          <bean             />          <bean   />          <bean  >          <property name="name"><value>Rob Harrop</value></property>          <property name="age"><value>100</value></property>          <property name="favoriteColor"><value>Blue</value></property>      </bean>           <bean  >         <property name="server">             <ref local="mbeanServer"/>         </property>         <property name="objectName">             <value>spring:name=jmxBean</value>         </property>     </bean> </beans>
image from book

Here you can see that we set the assembler property of the JmxMBeanAdapter class to an instance of MetadataModelMBeanInfoAssembler, which means that the metadata of each bean is read by Spring when it creates the management interface.

The second thing to notice about this example is that we set the JmxMBeanAdapter.namingStrategy property to an instance of MetadataNamingStrategy. When determining what ObjectName to use when registering a bean, JmxMBeanAdapter consults its configured implementation of the NamingStrategy interface. The NamingStrategy interface has a single method, getObjectName(), to which Spring passes the Map key associated with the bean and the bean instance itself. MetadataNamingStrategy uses the ObjectName configured via the ManagedResource attribute that you saw in Listing D-14.

The third and final point of interest in this example is that no beans are passed to JmxMBeanAdapter via the beans property. The reason for this is that the MetadataModelMBeanInfoAssembler class implements the AutodetectCapableModelMBeanInfoAssembler interface and JmxMBeanAdapter gives it a chance to identify beans that should be exposed to JMX. The MetadataModelMBeanInfoAssembler class adds any bean whose class is marked with the ManagedResource attribute to the set of beans to be exposed. When using this approach, the bean name is used in place of the Map key when obtaining the ObjectName from the configured NamingStrategy implementation.

Listing D-17 demonstrates the role of metadata in the construction of the management interface by attempting to write to the name attribute that is marked as read-only in the code (i.e., setName() has no ManagedAttribute).

Listing D-17: Testing MetadataModelMBeanInfoAssembler

image from book
package com.apress.prospring.apd.jmx;      import javax.management.MBeanServer;      import org.springframework.context.ApplicationContext; import org.springframework.context.support.FileSystemXmlApplicationContext;      public class MetadataExample {          public static void main(String[] args) throws Exception {              ApplicationContext ctx = new FileSystemXmlApplicationContext(                 "./ap4/src/conf/jmx/attributes.xml");              MBeanServer server = (MBeanServer) ctx.getBean("mbeanServer");         JmxBean proxy = (JmxBean) ctx.getBean("proxy");              System.out.println(proxy.getName());         try {             proxy.setName("Jan Machacek");         } catch(Exception ex) {             System.out.println(ex.getMessage());         }     } } 
image from book

Running this example results in the following output:

Rob Harrop Operation/Attribute setName is not exposed on the management interface

As you can see, invoking the getName() method is acceptable because it is marked with a ManagedAttribute attribute, but invoking setName() causes an exception because it does not have a ManagedAttribute.

Using JSR-160 Support

With the advent of JSR-160, you can now expose an MBeanServer to remote clients in a standard way; clients can also access remote MBeanServers in a standard way. Spring JMX provides JSR-160 support classes to allow MBeanServer instances to be exposed to remote clients in an entirely declarative manner and to allow remote JMX resources to be accessed via proxy.

In Listing D-18 you can see an example configuration that exposes an MBeanServer, and thus all its managed resources, to remote clients using ConnectorServiceBean.

Listing D-18: Exposing an MBeanServer Using JSR-160

image from book
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"  "http://www.springframework.org/dtd/spring-beans.dtd"> <beans>     <bean  >         <property name="beans">             <map>                 <entry key="spring:name=someBean">                     <ref local="someBean"/>                 </entry>             </map>         </property>         <property name="server">             <ref local="mbeanServer"/>         </property>     </bean>          <bean   />          <bean  >         <property name="name">             <value>Rob Harrop</value>         </property>         <property name="age">             <value>100</value>         </property>         <property name="favoriteColor">             <value>Blue</value>         </property>     </bean>          <bean   >         <property name="server">             <ref local="mbeanServer"/>         </property>     </bean> </beans>
image from book

You will recognize much of this code from Listing D-10, although the connectorService bean is new. Using the ConnectorServiceBean class, you can expose MBeanServer via JSR-160 to remote clients. By default, ConnectorServiceBean uses the service URL service:jmx:jmxmp:// localhost:9876, although you can change this by setting the serviceUrl property of ConnectorServiceBean.

ConnectorServiceBean automatically starts listening for remote connections as soon as your ApplicationContext initializes it, so hosting a remote MBeanServer requires little Java code, as shown in Listing D-19.

Listing D-19: Hosting MBeanServer

image from book
package com.apress.prospring.apd.jmx;      import org.springframework.context.ApplicationContext; import org.springframework.context.support.FileSystemXmlApplicationContext;      public class Jsr160Server {          public static void main(String[] args) throws Exception {         ApplicationContext ctx = new FileSystemXmlApplicationContext(         "./ap4/src/conf/jmx/jsr160.xml");         System.out.println("Waiting...");         System.in.read();     } }
image from book

To access a managed resource in a remote MbeanServer, you can use the JmxProxyFactoryBean as you would with a local resource, but instead of passing in an MBeanServer instance, you pass in an MBeanServerConnection instance, which you can configure using MBeanServerConnectionFactoryBean, as shown in Listing D-20.

Listing D-20: Configuring a Remote JMX Proxy

image from book
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"  "http://www.springframework.org/dtd/spring-beans.dtd"> <beans>     <bean   >         <property name="serviceUrl">             <value>service:jmx:jmxmp://localhost:9876</value>         </property>     </bean>     <bean  >         <property name="server">             <ref local="mbeanServerConnection"/>         </property>         <property name="objectName">             <value>spring:name=someBean</value>         </property>     </bean> </beans>
image from book

Here you can see that we configure JmxProxyFactoryBean with an instance of MBeanServerConnection using MBeanServerConnectionFactoryBean, which itself is configured with the service URL of the remote MBeanServer. The resulting proxy can be used just like any other, as shown in Listing D-21.

Listing D-21: Using a Remote JMX Proxy

image from book
package com.apress.prospring.apd.jmx;      import org.springframework.context.ApplicationContext; import org.springframework.context.support.FileSystemXmlApplicationContext;      public class Jsr160Client {          public static void main(String[] args) {         ApplicationContext ctx = new FileSystemXmlApplicationContext(         "./ap4/src/conf/jmx/jsr160-client.xml");                  JmxBean bean = (JmxBean)ctx.getBean("proxy");         System.out.println(bean.getName());              } }
image from book



Pro Spring
Pro Spring
ISBN: 1590594614
EAN: 2147483647
Year: 2006
Pages: 189

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net