4.2.5.1. Создание JMX-бина

Рассмотрим процесс создания JMX-бина на примере.

  • Интерфейс JMX-бина:

    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);
    }

    Интерфейс и его методы могут содержать аннотации для задания описания JMX-бина и его операций. Это описание будет отображаться во всех инструментах, работающих с данным JMX-интерфейсом, тем самым помогая администратору системы.

    Так как инструменты JMX поддерживают ограниченный набор типов данных, параметры и результат метода желательно задавать типа String, и при необходимости выполнять конвертацию внутри метода.

  • Класс JMX-бина:

    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);
          }
      }
    }

    Аннотация @ManagedBean определяет, что данный класс является управляемым бином с именем sales_OrdersMBean. Имя указано напрямую в аннотации, а не в константе, так как доступ к JMX-бину из кода Java не требуется.

    Рассмотрим реализацию метода calculateTotals().

    • Метод имеет аннотацию @Authenticated, т.е. при входе в метод и при отсутствии в потоке выполнения пользовательской сессии выполняется системная аутентификация.

    • Тело метода обернуто в блок try/catch, так что метод в случае успешного выполнения возвращает строку "Done", а в случае ошибки - stacktrace исключения в виде строки.

      Следует иметь в виду, что в данном случае все исключения обрабатываются, а значит, не попадают в MBeanInterceptor и не выводятся в журнал автоматически. Поэтому при необходимости логгировать исключения здесь нужно добавить вызов логгера в секции catch.

    • Логика метода заключается в том, что он стартует транзакцию, загружает экземпляр сущности Order по идентификатору, и передает управление бину OrderWorker для обработки.

  • Регистрация JMX-бина в 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>

Все JMX-бины проекта объявляются в одном экземпляре MBeanExporter в элементах map/entry свойства beans. Ключом элемента здесь является JMX ObjectName, значением - имя бина, заданное в аннотации @ManagedBean. ObjectName начинается с имени веб-приложения, так как в одном экземпляре Tomcat (т.е. в одной JVM) может быть развернуто несколько веб-приложений, экспортирующих одинаковые JMX-интерфейсы.