Exposing application statistics using JMX in spring

In one of my previous blogs ( Expose app. statistics by implementing JMX & view them in JConsole ) I wrote about , How we can expose application statistics by implementing JMX in a Plain Java application. Also I showed how we can view the JMX statistics in real time using JConsole. In Spring we have annotations to perform the tasks that we do in plain Java by implementing certain Interfaces. Today I am going to explain how we can use the spring annotations and expose application statistics through JMX. First I am going to explain the logic that explains what the code is supposed to do and how. And then the code that we are going to implement along with explaining the important parts of the code itself.


The Example

In spring we can create a Component or RestController and use a few annotations that convert them to JMX beans. Here I will write a simple bean, it has a JMX attribute called name, the type of the attribute is String. The second JMX attribute the simple bean has is called JavaApp of type boolean. There will be two JMX operations enableName and disableName. These two operations will control whether we will see the hardcoded name in the JMX console or not. We will use the isEnabled boolean variable internally to change the state. Then we will see how we can provide input values through JMX and add the input to the name attribute using the makeName JMX operation.


The Code

package com.myproj.cf.config;
 
import org.springframework.jmx.export.annotation.*
import org.springframework.stereotype.Component;
 
@Component
@ManagedResource(objectName="com.myproj.cf.config:name=AppDetails")
public class AppDetailsController {
  private String name = "JMX-Enabled-Java";
  private boolean isEnabled = false;
 
  @ManagedAttribute(description="get the name")
  public String getName() {
    return isEnabled == true ? name : "" ;
  }
  @ManagedAttribute(description="get the type")
  public boolean isJavaApp() {
    return true ;
  }
  @ManagedOperation(description="enable name")
  public void enableName() {
    isEnabled = true ;
  }
  @ManagedOperation(description="disable name")
  public void disableName() {
    isEnabled = false ;
  }
  @ManageOperation(description = "make name")
  @ManagedOperationParameters({
      @ManagedOperationParameter(name = "fName", description = "The First name"),
      @ManagedOperationParameter(name = "lName", description = "The Last name")
  })
  public void makeName(String fName, String lName) {
    name = name + "-" + fName + " " + lName;
  }
}

The above class contains all the JMX attributes, Operations and the java variables which are controlled by the JMX attributes. We use the @ManagedResource annotation to assign the qualified name of the JMX attributes and operations to the JAVA class. The @ManagedAttribute annotation is used on java methods to define the JMX attributes. Similarly, the @ManagedOperation annotation is used to define the JMX operations. We can optionally provide descriptions on the attributes and operations. When we want to provide inputs through JMX we need to add @ManagedOperationParameters and @ManagedOperationParameter annotation along with @ManageOperation. Each method variable will act as an input parameter for JMX. Note that the method naming should be as per Bean naming convention.

In the above class I have used the @ManagedResource annotation to define the class AppDetailsController that, it will be used as a container that will contain the JM attributes under the JMX object name AppDetails with qualifier com.myproj.cf.config. The @ManagedAttribute annotation on method getName defines it as JMX attribute and as the naming convention of the method is aligned with Bean naming convention the name of the attribute is automatically assigned as name. In the annotation we have also provided a description of the annotation. The return type of the method becomes the type of the JMX attribute automatically. Similarly, I have defined the second JMX attribute JavaApp of type boolean. I have annotated the enableName method with @ManagedOperation annotation, because of this annotation this method is treated as a JMX operation. We also need to point here that the return type of the method that works as an operation should be void. Similarly I have used the disableName method for the second JMX operation. Whenever the JMX operation is invoked the logic inside the method will execute. Here is this code, I have flipped the boolean variables state using these two JMX operations. Next, as I said I want to use the makeName JMX operation that can also supply two input values, I have annotated the makeName method with the @ManagedOperation annotation. Additionally, I have used the two input parameters fName and lName of the method to get input from JMX. I have declared these two parameters will be used as JMX input parameters for this JMX operation using the @ManagedOperationParameters and the @ManagedOperationParameter annotation. Similar to other JM annotations, these two annotations also have a description attribute that can be used to describe the fields with a text string. As makeName is also serving as JMX operation its return type is again void, like other two JMX operations. Inside this method I am just adding the two input parameters and saving them in the name variable. The current value of the name variable can be retrieved from the JMX console as JMX attributes because earlier I had defined it using the getName method.


Now I need to register this bean, I will create a spring Configuration for that. I will annotate the configuration class with @EnableMBeanExport annotation. This will automatically create a MBean server and register all the Mbeans from classes annotated with @ManagedResource. The MBean server name and default domain is also mentioned with the @EnableMBeanExport annotation as attributes.


package com.myproj.cf.config;
 
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.EnableMBeanExport;
 
import java.lang.management.ManagementFactory;
import javax.annotation.PostConstruct;
import javax.management.MBeanServer;
 
@Configuration
@EnableMBeanExport(server="myMBeanServer", defaultDomain="com.myproj.cf.config")
//@EnableMBeanExport
public class RegisterMBeans {
 
  private MBeanServer server;
  @PostConstruct
  public void init() throws Exception {
    initMBeans();
  }
 
  private initMBeans() {
    server = ManagementFactory.getPlatformMBeanServer();
  }
 
  @Bean
   public MBeanServer myMBeanServer() {
     return server;
   }
}


After we deploy it in the local machine we can open the jconsole and we will find the operation and attributes under MBean tab with namespace com.myproj.cf.config. You can refer to my blog (expose app. statistics by implementing JMX & view them in JConsole) where I explained how we can use Jconsole to view and interact with JMX.

Here, we learned how we can use spring annotations to create JMX attributes and operations, assign a namespace to the JMX attributes and namespaces and automatically register the JMX attributes with an MBean server. Spring makes it really easy and with less code compared to the traditional way using Java .


3 views0 comments

Recent Posts

See All