JEP draft: JMX Specific Annotations for Registration of Managed Resources
Специфичные для JMX аннотации для регистрации управляемых ресурсов
| Автор | Jaroslav Bachorik |
| Ответственный | Harsha Wardhana B |
| Тип | Feature |
| Область | SE |
| Статус | Draft |
| Выпуск | tbd |
| Компонент | core-svc / javax.management |
| Обсуждение | jmx dash dev at openjdk dot java dot net |
| Трудоёмкость | S |
| Длительность | M |
| Рецензенты | Mikael Vidstedt, Staffan Larsen |
| Одобрен | Mikael Vidstedt |
| Создан | 2014/06/02 10:38 |
| Обновлён | 2025/06/10 14:52 |
| Задача | 8044507 |
Аннотация
Предоставить набор аннотаций для регистрации и настройки управляемых сервисов, также известных как MBeans.
Цели
Основная цель — упростить разработчикам регистрацию и настройку MBeans. Второстепенная цель — сделать исходный код более читаемым и согласованным: все части объявления MBean будут находиться в одном месте.
Что не является целью
Объявление устаревшими и замена существующих способов регистрации и настройки MBeans.
Мотивация
Существующий механизм определения MBean требует предоставить интерфейс MBean и его реализацию. Интерфейс и реализация должны соответствовать строгим правилам именования и видимости, чтобы интроспекция могла связать их между собой.
Не меньше многословности требуется при добавлении MBeanInfo для генерации метаданных MBean.
Всё это приводит к довольно многословному коду с большим количеством повторяющихся шаблонных частей даже для самых простых регистраций MBean.
В Spring есть реализация аннотаций для регистрации и настройки MBean от 3-й стороны, и она стала среди разработчиков стандартом де-факто — это указывает на то, что такой подход должен стать частью стандарта JMX.
Описание
Реализация будет состоять из набора аннотаций, которые помечают определённые классы для публикации через механизмы JMX и задают их атрибуты и операции.
Будет предусмотрен обработчик аннотаций, проверяющий расставленные аннотации и их атрибуты.
Управляемые ресурсы, заданные аннотациями, будут полностью пригодны для использования в существующей системе JMX и доступны более старым клиентам без каких-либо изменений.
Все аннотации будут размещены в пакете javax.management.annotations, чтобы не загромождать ещё больше пакет javax.management.
@ManagedService
Помечает реализацию управляемого сервиса. Должна применяться только к неабстрактным классам. При использовании этой аннотации каждый экземпляр аннотированного класса станет совместимым с MXBean.
Аргументы
- objectName — имя, под которым управляемый сервис будет зарегистрирован в сервере MBean, если иное не указано в методе MBeanServer.registerMBean().(необязательный)
- description — осмысленное текстовое описание реализации управляемого сервиса (необязательный)
- service — интерфейс сервиса, через который можно обращаться к управляемому сервису (необязательный)
Аннотированный класс не обязан на самом деле реализовывать интерфейс сервиса
Указанный здесь интерфейс сервиса будет экспортирован как часть Descriptor под ключом interfaceClassName. - tags — массив аннотаций @Tag, задающих теги (пары ключ-значение), которые будут добавлены в Descriptor MBean (необязательный)
Определение
@interface ManagedService {
String objectName() default "";
String description() default "";
Class<?> service() default Object.class;
Tag[] tags() default {}'
}
@ManagedAttribute
Аннотирует поле или метод, соответствующий шаблону getter/setter, в классе, аннотированном @ManagedService, чтобы сделать его доступным как управляемый атрибут.
Аргументы
- name — имя атрибута. Может отличаться от фактического имени поля или имени, выведенного из getter/setter. Если не указано, будет использовано имя поля или имя, выведенное из имени метода getter/setter. (необязательный)
- access — тип доступа к атрибуту: Read, Write, ReadWrite (необязательный). Если не указан, предполагается ReadWrite.
- description — осмысленное текстовое описание атрибута (необязательный)
- getter — позволяет указать собственный метод getter из аннотированного класса. Указанный метод должен иметь соответствующий доступ, а его возвращаемый тип должен быть присваиваемым аннотированному атрибуту. (необязательный)
Это может пригодиться, когда определению одного атрибута нужны и setter, и getter. Вместо того чтобы аннотировать оба метода и копировать метаданные, можно будет аннотировать только setter и сослаться на getter с помощью этого атрибута. - setter — позволяет указать собственный метод setter из аннотированного класса. Указанный метод должен иметь соответствующий доступ и ровно один аргумент, тип которого должен допускать присваивание из аннотированного атрибута(необязательный)
Это может пригодиться, когда определению одного атрибута нужны и setter, и getter. Вместо того чтобы аннотировать оба метода и копировать метаданные, можно будет аннотировать только getter и сослаться на setter с помощью этого атрибута. - units — текстовая форма единиц, в которых измеряется этот атрибут. Будет экспортирована через связанный Descriptor (необязательный)
- tags — массив аннотаций @Tag, задающих теги (пары ключ-значение), которые будут добавлены в связанный Descriptor (необязательный)
Определение
@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface ManagedAttribute {
String name() default "";
String description() default "";
Access access() default AttributeAccess.READWRITE;
String getter() default "";
String setter() default "";
String units() default "";
Tag[] tags() default {};
}
@ManagedOperation
Аннотирует метод в классе, аннотированном @ManagedService, чтобы сделать его доступным как управляемую операцию.
Аргументы
- name — имя операции. Если не указано, будет использовано имя метода. Может отличаться от фактического имени метода — это можно использовать, например, чтобы обойти невозможность корректно разрешать ковариантные методы в MBeans (необязательный)
- description — осмысленное текстовое описание атрибута (необязательный)
- impact — воздействие, которое может оказать операция. Одно из [INFO, ACTION, ACTION_INFO, UNKNOWN]. Соответствует кодам воздействия, используемым в javax.management.MBeanManagedOperation (необязательный)
- units — текстовая форма единиц, в которых измеряется результат этого атрибута. Будет экспортирована через связанный Descriptor (необязательный)
- tags — массив аннотаций @Tag, задающих теги (пары ключ-значение), которые будут добавлены в связанный Descriptor (необязательный)
Определение
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface ManagedOperation {
String name() default "";
String description() default "";
Impact impact() default Impact.UNKNOWN;
String units() default "";
Tag[] tags() default {};
}
@ParameterInfo
Аннотирует параметр метода в классе, аннотированном @ManagedService, чтобы предоставить более конкретные метаданные.
Аргументы
- name — имя параметра. Может отличаться от фактического имени параметра. Если не указано, будет использовано имя параметра, полученное через рефлексию. (необязательный)
- description — осмысленное текстовое описание атрибута (необязательный)
- units — текстовая форма единиц, в которых измеряется значение этого атрибута. Будет экспортирована через связанный Descriptor (необязательный)
- tags — массив аннотаций @Tag, задающих теги (пары ключ-значение), которые будут добавлены в связанный Descriptor (необязательный)
Определение
@Target(value = ElementType.PARAMETER)
@Retention(value = RetentionPolicy.RUNTIME)
@interface ParameterInfo {
String name() default "";
String description() default "";
String units() default "";
Tag[] tags() default {};
}
@NotificationInfo
Если поле типа NotificationSender аннотировано этой аннотацией, в него будет внедрена фактическая реализация, позволяющая отправлять уведомления.
Аргументы
- implementation — фактический класс уведомления, должен быть подклассом javax.management.Notification. Если не указан, будет использован javax.management.Notification.
- description — текстовое описание (необязательный)
- types — массив строк, представляющих типы уведомлений (см. javax.management.Notification#getType())
- severity — произвольное число, обозначающее серьёзность уведомления (по умолчанию 6) (необязательный)
- tags — массив аннотаций @Tag, задающих теги (пары ключ-значение), которые будут добавлены в Descriptor MBean (необязательный)
Определение
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(NotificationInfos.class)
public @interface NotificationInfo {
Class<? extends javax.management.Notification> implementation() default javax.management.Notification.class;
String description() default "";
String[] types();
int severity() default "";
Tag[] tags() default {};
}
@NotificationInfos
Если поле типа NotificationSender аннотировано этой аннотацией, в него будет внедрена фактическая реализация, позволяющая отправлять уведомления. Вложенные аннотации NotificationInfo будут использованы для заполнения массива MBeanNotificationInfo в связанном экземпляре MBeanInfo.
Аргументы
- value — массив содержащихся объявлений @NotificationInfo
Определение
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface NotificationInfos {
NotificationInfo[] value();
}
@RegistrationHandler
Удобный способ указать, что MBean заинтересован в событиях жизненного цикла регистрации. Метод, аннотированный этой аннотацией, будет вызываться для событий жизненного цикла. Аннотированный метод должен принимать ровно один параметр типа RegistrationEvent.
Если управляемому сервису нужно перехватывать детализированные обратные вызовы регистрации, ему достаточно реализовать интерфейс MBeanRegistration.
Определение
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface RegistrationHandler {
}
Перечисление Impact
Тесно связано с константами MBeanManagedOperation#(INFO|ACTION|ACTION_INFO|UNKNOWN).
public static enum Impact {
INFO, ACTION, ACTION_INFO, UNKNOWN
}
Перечисление RegistrationKind
public enum RegistrationKind {
REGISTER, DEREGISTER, FAIL
}
Перечисление AttributeAccess
public enum AttributeAccess {
READ, WRITE, READWRITE
}
Класс RegistrationEvent
/**
* Registration event type. May be obtained in a method annotated by
* {@linkplain RegistrationHandler} annotation.
*/
final public class RegistrationEvent {
private final RegistrationKind kind;
private final MBeanServer mbs;
private final ObjectName on;
public RegistrationEvent(RegistrationKind kind, MBeanServer mbs, ObjectName on) {
this.kind = kind;
this.mbs = mbs;
this.on = on;
}
/**
* Registration event kind.
* @return A {@linkplain RegistrationKind} value
*/
public RegistrationKind getKind() {
return kind;
}
/**
* The {@linkplain MBeanServer} this event was generated by.
* @return The associated {@linkplain MBeanServer} instance
*/
public MBeanServer getMBeanServer() {
return mbs;
}
/**
* The name of the MBean for which the event was generated.
* @return The MBean's {@linkplain ObjectName}
*/
public ObjectName getObjectName() {
return on;
}
}
Интерфейс NotificationSender
/**
* <p>Interface for marking a class as being able to send notifications</p>
*
* @since 1.9
*/
public interface NotificationSender {
/**
* <p>Sends a standard notification.</p>
*
* @param type The notification type.
* @param message The notification message.
* @param userData The user data. It is used for whatever data
* the notification source wishes to communicate to its consumers.
*/
void sendNotification(String type, String message, Object userData);
/**
* Sends a custom notification.
*
* @param notification The notification to send.
*/
void sendNotification(javax.management.Notification notification);
}
Использование
Определение сервиса
// The following class is a managed service implementation
// It will be registered under the provided objectName if not overridden
// The "service" parameter is optional; it serves as a hint for creating the service proxy and the managed service does not necessarily need to implement the interface
@ManagedService(
objectName="net.java.jmx:type=StandardService",
description="A simple service exposed as an MXBean",
service=Service.class,
tags = {
@Tag(name = "tag1", value = "val1"),
@Tag(name = "tag2", value = "val2")
}
)
public class SimpleService {
@NotificationInfo(types = "test.mbean.label", description = "Label was set")
@NotificationInfo(types = "test.mbean.threshold", description = "Counter threshold reached")
private NotificationSender ns;
// A read-only attribute measured in "ticks"
@ManagedAttribute(access = AttributeAccess.READ, units = "ticks")
int counter = 1;
// A read-only attribute being an array of strings
@ManagedAttribute(access = AttributeAccess.READ)
String[] arr = new String[]{"sa", "ba", "ca"};
// Declare a read-only attribute ...
@ManagedAttribute(access = AttributeAccess.READ)
private String label = "the label";
// ... and augment it with a complex setter later on
@ManagedAttribute
public void setLabel(String l) {
ns.sendNotification("test.mbean.label", "Label set", l);
label = l;
}
// an operation modifying the 'counter' attribute and sending a custom notification
@ManagedOperation(impact = Impact.ACTION, description = "Increases the associated counter by 1")
public int count() {
if (counter >= 5) {
ns.sendNotification("test.mbean.threshold", "Threshold reached", counter);
}
return ++counter;
}
// an operation declaring custom 'units' metadata for one of its parameters
@ManagedOperation
public void checkTime(@Parameter(units = "ms") long ts) {
System.err.println(new Date(ts));
}
// handle the registration/unregsitration of the MBean
@RegistrationHandler
public void onRegistration(RegistrationEvent re) {
switch(re.getKind()) {
case REGISTER: {
System.err.println("Registered " + re.getObjectName().getCanonicalName());
break;
}
case UNREGISTER: {
System.err.println("Unregistered " + re.getObjectName().getCanonicalName());
break;
}
}
}
}
Альтернативы
Использование аннотаций Spring
Вместо создания похожего, но нового набора аннотаций, приспособленного для реализации JMX в JDK, можно было бы использовать уже существующие аннотации Spring.
Плюсы
- Существующее, хорошо известное решение
- Широкая база пользователей
- Лучший охват стандартных полей Descriptor
Минусы
- Другое пространство имён
- Возможные зависимости от контейнера Spring
- Нет аннотаций для параметров методов — обходится с помощью @ManagedOperationAttributes
- Нет поддержки автоматического преобразования аннотированных полей в управляемые атрибуты
Повторное введение аннотаций JMX 2.0
Предложение по аннотациям JMX 2.0
Это предложение было частью JSR 255. Оно послужило источником идей для этого JEP, в котором мы стремимся упростить использование и снизить сложность, реализуя только самые важные возможности.
Реализация аннотаций JMX 2.0 готова примерно на 90 % вместе с тестами.
Плюсы
- Почти готово к использованию
- Позволяет использовать аннотации вроде @Description или @DescriptorKey/Fields даже в классах, не помеченных аннотациями @MBean или @MXBean
- Для внедрения ресурсов используется стандартная аннотация @Resource
Минусы
- Нет аннотаций для параметров методов
- Нет поддержки автоматического преобразования аннотированных полей в управляемые атрибуты
- Зависимость от других частей JSR 255 (например, MXBeanMappingFactory и MXBeanMapping становятся публичным API)
- Аннотация @Resource, используемая для CDI, перенесена в проект jaxws и в JDK 9 больше не входит в основные API Java
- Сложность
- аннотации могут определять как стандартный MBean, так и MXBean
- перегруженное значение @MXBean
- @DescriptorFields принимает обычный текст и ожидает определённого форматирования
Тестирование
Будут предоставлены модульные тесты с хорошим покрытием кода. Существующие регрессионные тесты и тесты JCK должны по-прежнему проходить без изменений.
Риски и допущения
Серьёзных рисков, связанных с этой возможностью, нет. Похожая работа уже была проделана другими, так что это не неизведанная территория.
Зависимости
На данный момент зависимости неизвестны.
Влияние
- Удобство для пользователей: значительно упрощается разработка собственных MBean
- I18n/L10n: документацию по аннотациям потребуется перевести
- Документация: API аннотаций потребуется задокументировать