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 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. Имя указано напрямую в аннотации, а не в константе, так как доступ к 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 начинается с имени веб-приложения, так как в одном экземпляре сервера приложения (т.е. в одной JVM) может быть развернуто несколько веб-приложений, экспортирующих одинаковые JMX-интерфейсы.