3.2.5.1. 创建 JMX Bean

以下示例展示如何创建 JMX Bean。

  • JMX bean 接口:

    package com.sample.sales.core;
    
    import org.springframework.jmx.export.annotation.*;
    import com.haulmont.cuba.core.sys.jmx.JmxBean;
    
    @JmxBean(module = "sales", alias = "OrdersMBean")
    @ManagedResource(description = "Performs operations on Orders")
    public interface OrdersMBean {
        @ManagedOperation(description = "Recalculates an order amount")
        @ManagedOperationParameters({@ManagedOperationParameter(name = "orderId", description = "")})
        String calculateTotals(String orderId);
    }
    • 接口和其方法上可定义注解,通过这些注解来对 JMX Bean 和它提供的操作进行描述。这些描述会显示在使用 JMX 接口的所有工具中,这样可以帮助系统管理员了解接口。

    • 可选的 @JmxBean 注解用来自动注册类实例至 JMX 服务,按照 modulealias 属性来注册。可以使用这个注解来注册 JMX bean,代替在 spring.xml 中配置。

    • 可选的 @JmxRunAsync 注解用来标识出一个需要长时间执行的操作。当使用内置的 JMX console 运行此操作时,平台将会显示一个带有无限进度条和 Cancel 按钮的对话框。用户可以中断操作并继续使用应用程序。该注解还可以包含 timeout 参数,该参数用来设置操作的最长执行时间(以毫秒为单位),例如:

      @JmxRunAsync(timeout = 30000)
      String calculateTotals();

      如果执行超时,对话框将关闭并显示错误消息。

      请注意,如果在用户界面上取消操作,或者观察到操作超时,但操作实际上继续在后台运行,也就是说这些操作不能实际终止,只是将控制权交给用户。

    • 由于 JMX 工具只支持有限的数据类型,因此最好使用 String 作为方法的参数和结果类型,必要时,需要在方法内进行类型转换。除了 String 外,还支持以下参数类型:booleandoublefloatintlongBooleanInteger

  • JMX bean 类:

    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 org.springframework.stereotype.Component;
    import javax.inject.Inject;
    import java.util.UUID;
    
    @Component("sales_OrdersMBean")
    public class Orders implements OrdersMBean {
    
        @Inject
        protected OrderWorker orderWorker;
    
        @Inject
        protected Persistence persistence;
    
        @Authenticated
        @Override
        public String calculateTotals(String orderId) {
            try {
                try (Transaction tx = persistence.createTransaction()) {
                    Order entity = persistence.getEntityManager().find(Order.class, UUID.fromString(orderId));
                    orderWorker.calculateTotals(entity);
                    tx.commit();
                };
                return "Done";
            } catch (Throwable e) {
                return ExceptionUtils.getStackTrace(e);
            }
        }
    }

    @Component 注解将类定义为具有 sales_OrdersMBean 名称的 Spring bean,该名称直接在注解中指定而不是在常量中,因为不需要从 Java 代码访问 JMX bean。

    下面概述一下 calculateTotals() 方法的实现。

    • 该方法有 @Authenticated 注解,也就是说在没有用户会话的情况下在方法入口处进行了系统身份验证

    • 方法主体包含在 try/catch 块中,因此,如果成功,则方法返回 "Done",在出现错误时,则会以字符串形式返回异常堆栈的跟踪信息。

      在这种情况下,全部异常都会被妥善处理,所以异常不会被自动记录到日志中,因为异常根本到不了 MBeanInterceptor。如果需要记录异常日志信息,应该在 catch 部分调用 Logger。

    • 该方法开启事务、通过标识符加载 Order 实体实例、传递控制权给 OrderWorker bean 进行处理。

  • spring.xml 中注册 JMX bean:

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

    项目的所有 JMX beans 都是在 beans 属性的 map/entry 元素中的一个 MBeanExporter 实例中声明的。键是 JMX ObjectName,值是在 @Component 注解中指定的 bean 名称。ObjectName 以 web 应用程序的名称开始,因为可以将输出相同 JMX 接口的多个 web 应用程序部署到一个应用程序服务实例中(部署到 JVM 中)。