4.2.5.1. Creating a JMX Bean

The following example shows how to create a JMX bean.

  • JMX bean interface:

    package com.sample.sales.core;
    
    import org.springframework.jmx.export.annotation.*;
    
    @ManagedResource(description = "Performs operations on Orders")
    public interface OrdersMBean {
    
        @ManagedOperation(description = "Recalculates an order amount")
        @ManagedOperationParameters({@ManagedOperationParameter(name = "orderId", description = "")})
        String calculateTotals(String orderId);
    }

    The interface and its methods may contain annotations to specify the description of the JMX bean and its operations. This description will be displayed in all tools that work with this JMX interface, thereby helping the system administrator.

    Since the JMX tools support a limited set of data types, it is desirable to use String as the type for the parameters and result of the method and perform the conversion inside the method, if necessary.

  • The JMX bean class:

    package com.sample.sales.core;
    
    import com.haulmont.cuba.core.*;
    import com.haulmont.cuba.core.app.*;
    import com.sample.sales.entity.Order;
    import org.apache.commons.lang.exception.ExceptionUtils;
    import javax.annotation.ManagedBean;
    import javax.inject.Inject;
    import java.util.UUID;
    
    @ManagedBean("sales_OrdersMBean")
    public class Orders implements OrdersMBean {
    
        @Inject
        protected OrderWorker orderWorker;
    
        @Inject
        protected Persistence persistence;
    
        @Authenticated
        @Override
        public String calculateTotals(final String orderId) {
            try {
                persistence.createTransaction().execute(new Transaction.Runnable() {
                    @Override
                    public void run(EntityManager em) {
                        Order entity = em.find(Order.class, UUID.fromString(orderId));
                        orderWorker.calculateTotals(entity);
                    }
                });
                return "Done";
            } catch (Throwable e) {
                return ExceptionUtils.getStackTrace(e);
            }
        }
    }

    The @ManagedBean annotation defines class as a managed bean with the sales_OrdersMBean name. The name is specified directly in the annotation and not in the constant, since access to the JMX bean from Java code is not required.

    Lets overview the implementation of the calculateTotals() method.

    • The method has the @Authenticated annotation, i.e., system authentication is performed on method entry in the absence of the user session.

    • The method’s body is wrapped in the try/catch block, so that, if successful, the method returns "Done", and in case of error – the stack trace of the exception as string.

      It should be kept in mind that, in this case, all exceptions are handled and therefore do not get logged automatically, because they never fall through to MBeanInterceptor. If logging of exceptions is required, the call of the logger should be added in the catch section.

    • The method starts the transaction, loads the Order entity instance by identifier, and passes control to the OrderWorker bean for processing.

  • The registration of the JMX bean in spring.xml:

    <bean id="sales_MBeanExporter" lazy-init="false"
          class="com.haulmont.cuba.core.sys.jmx.MBeanExporter">
        <property name="beans">
            <map>
                <entry key="${cuba.webContextName}.sales:type=Orders"
                       value-ref="sales_OrdersMBean"/>
            </map>
        </property>
    </bean>

All JMX beans of a project are declared in one MBeanExporter instance in the map/entry elements of the beans property. The key is JMX ObjectName, the value – the bean’s name specified in the @ManagedBean annotation. ObjectName begins with the name of the web application, because several web applications, which export the same JMX interfaces, can be deployed in one Tomcat instance (i.e., in one JVM).