5.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-интерфейсом, тем самым помогая администратору системы.
-
Аннотацию
@JmxRunAsync
можно использовать для указания длительных операций. Если такая операция запускается через встроенную консоль JMX, платформа отображает диалог с неопределенным индикатором прогресса и кнопкой Cancel. Пользователь может прервать операцию и продолжить работу с приложением. Аннотация может также содержать параметрtimeout
, который устанавливает максимальное время выполнения в миллисекундах, например:@JmxRunAsync(timeout = 30000) String calculateTotals();
Если таймаут превышен, диалог закрывается с сообщением об ошибке.
WarningПожалуйста имейте в виду, что если операция прервана пользователем или по таймауту, она все равно продолжает работать в фоне, то есть данные действия не прерывают самого выполнения, а только возвращают управление пользователю.
-
Так как инструменты JMX поддерживают ограниченный набор типов данных, параметры и результат метода желательно задавать типа
String
, и при необходимости выполнять конвертацию внутри метода. ПомимоString
, поддерживаются следующие типы параметров:boolean
,double
,float
,int
,long
,Boolean
,Integer
.
-
-
Класс 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, значением - имя бина, заданное в аннотации@Component
. ObjectName начинается с имени веб-приложения, так как в одном экземпляре сервера приложения (т.е. в одной JVM) может быть развернуто несколько веб-приложений, экспортирующих одинаковые JMX-интерфейсы.