Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8044507

JMX Specific Annotations for Registration of Managed Resources

    XMLWordPrintable

Details

    • Jaroslav Bachorik
    • Feature
    • Open
    • SE
    • jmx dash dev at openjdk dot java dot net
    • S
    • M

    Description

      Summary

      Provide a set of annotations for registration and configuration of manageable service - also known as MBeans.

      Goals

      The primary goal is to make it easier for developers to register and configure MBeans. The secondary goal is improving the readability and coherency of the source code by keeping all parts of an MBean declaration in one place.

      Non-Goals

      Deprecation and replacement of the current ways to register and configure MBeans.

      Motivation

      Current mechanism of defining an MBean requires to provide an MBean interface and its implementation. The interface and the implementation must conform to the strict naming and visibility rules in order for the introspection to be able to bind them.

      At least the same level of verbosity is required when adding an MBeanInfo to generate MBean metadata.

      All this leads to a rather verbose code containing a lot of repeating boilerplate parts even for the most simple MBean registrations.

      There is a 3rd party implementation of MBean registration and configuration annotations in Spring and it became de-facto standard among developers - indicating that this approach should be a part of the JMX standard.

      Description

      The implementation will consist of a set of annotations for marking certain classes for publishing via JMX mechanisms and specifying their attributes and operations.

      There will be an annotation processor validating the placed annotations and their attributes.

      The managed resources specified by the annotations will be fully usable within the current JMX system and accessible to older clients without any changes necessary.

      All the annotations will be placed in javax.management.annotations package not to further clutter the javax.management one.

      @ManagedService

      Marking a managed service implementation. It must be applied to non-abstract classes only. By using this annotation each instance of the annotated class will become MXBean compatible.

      Arguments

      • objectName - the name the managed service will be registered in the MBean server if not specified otherwise in MBeanServer.registerMBean() method.(optional)
      • description - a conscious textual description for the managed service implementation (optional)
      • service - the service interface the managed servicecan be accessed through (optional)
        The annotated class need not to actually implement the service interface
        The service interface specified here will be exported as a part of Descriptor under interfaceClassName key.
      • tags - an array of @Tag annotations specifying tags (key-value pairs) to be added to the MBean Descriptor (optional)

      Definition

      @interface ManagedService {
          String objectName() default "";
          String description() default "";
          Class<?> service() default Object.class;
          Tag[] tags() default {}'
      }

      @ManagedAttribute

      Annotates a field or a method conforming to the getter/setter pattern within a class annotated by @ManagedService annotation to become accessible as a managed attribute.

      Arguments

      • name - the attribute name. May be different from the actual field name or the inferred getter/setter name. If not provided the field name will be used or the name will be inferred from the getter/setter method name. (optional)
      • access - the attribute access type - Read, Write, ReadWrite (optional). When not specified ReadWrite is assumed.
      • description - a conscious textual description for the attribute (optional)
      • getter - allows to specify a custom getter method from within the annotated class. The provided method must have appropriate access and its return type must be assignable to the annotated attribute. (optional)
        This might come handy when one attribute definition requires both the setter and getter. Instead of annotating both of those methods and copy-pasting the metadata it will be possible to annotate just the setter and refer to the getter using this attribute.
      • setter - allows to specify a custom setter method from within the annotated class. The provided method must have appropriate access and exactly one and argument type of which must be assignable from the annotated attribute(optional)
        This might come handy when one attribute definition requires both the setter and getter. Instead of annotating both of those methods and copy-pasting the metadata it will be possible to annotate just the getter and refer to the setter using this attribute.
      • units - a textual form of the units this attribute is measured in. It will be exported via the associated Descriptor (optional)
      • tags - an array of @Tag annotations specifying tags (key-value pairs) to be added to the associated Descriptor (optional)

      Definition

      @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

      Annotates a method within a class annotated by @ManagedService annotation to become accessible as a managed operation.

      Arguments

      • name - the operation name. If not provided the method name will be used. May be different from the actual method name - this could be used eg. to overcome the inability to properly resolve covariant methods in MBeans (optional)
      • description - a conscious textual description for the attribute (optional)
      • impact - the impact the operation can have. One of [INFO, ACTION, *ACTION_INFO*, UNKNOWN]. Corresponds to the impact codes used in javax.management.MBeanManagedOperation (optional)
      • units - a textual form of the units the result of this attribute is measured in. It will be exported via the associated Descriptor (optional)
      • tags - an array of @Tag annotations specifying tags (key-value pairs) to be added to the associated Descriptor (optional)

      Definition

      @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 {};
      }</code></pre> <h3>@ParameterInfo</h3> <p>Annotates a method parameter within a class annotated by <strong>@ManagedService</strong> annotation to provide more specific metadata.</p> <p><strong>Arguments</strong></p> <ul>
      <li><strong>name</strong> - the parameter name. May be different from the actual parameter name. If not provided the parameter name obtained via reflection will be used.
      (<em>optional</em>)</li>
      <li><strong>description</strong> - a conscious textual description for the attribute (<em>optional</em>)</li>
      <li><strong>units</strong> - a textual form of the units the value of this attribute is measured in. It will be exported via the associated <a href="http://docs.oracle.com/javase/8/docs/api/javax/management/Descriptor.html" class="external-link" target="_blank" rel="nofollow noopener" >Descriptor</a> (<em>optional</em>)</li>
      <li><strong>tags</strong> - an array of <em>@Tag</em> annotations specifying tags (key-value pairs) to be added to the associated <a href="http://docs.oracle.com/javase/8/docs/api/javax/management/Descriptor.html" class="external-link" target="_blank" rel="nofollow noopener" >Descriptor</a> (<em>optional</em>)</li>
      </ul> <p><strong>Definition</strong></p> <pre class="prettyprint" ><code>@Target(value = ElementType.PARAMETER)
      @Retention(value = RetentionPolicy.RUNTIME)
      @interface ParameterInfo {
      String name() default "";
      String description() default "";
      String units() default "";
      Tag[] tags() default {}; }

      @NotificationInfo

      When a field of NotificationSender is annotated by this annotation it will get injected by the actual implementation allowing to emit notifications.

      Arguments

      • implementation - the actual notification class - must be a subclass of javax.management.Notification. If not provided javax.management.Notification will be used.
      • description - the textual description (optional)
      • types - an array of strings representing the notification types (_see blank" rel="nofollow noopener" data-shared-secret="1713407718472-0.4300442693158708">javax.management.Notification#getType())
      • severity - an arbitrary number denoting the notification severity (default is 6) (optional)
      • tags - an array of @Tag annotations specifying tags (key-value pairs) to be added to the MBean Descriptor (optional)

      Definition

      @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

      When a field of NotificationSender is annotated by this annotation it will get injected by the actual implementation allowing to emit notifications. The enclosed NotificationInfo anotatations will be used for filling in the MBeanNotificationInfo array in the associated MBeanInfo instance.

      Arguments

      • value - an array of the contained @NotificationInfo declarations

      Definition

      @Target(ElementType.FIELD)
      @Retention(RetentionPolicy.RUNTIME)
      @interface NotificationInfos {
          NotificationInfo[] value();
      }

      @RegistrationHandler

      A convenient way to express that the MBean is interested in the registration lifecycle events. A method annotated by this annotation will be called for the lifecycle events. The annotated method must take exactly one parameter of type RegistrationEvent.

      If a managed service wishes to intercept the fine grained registration callbacks it just needs to implement MBeanRegistration interface.

      Definition

      @Target(ElementType.METHOD)
      @Retention(RetentionPolicy.RUNTIME)
      @interface RegistrationHandler {
      }

      Enumeration Impact

      Closely related to the MBeanManagedOperation#(INFO|ACTION|ACTION_INFO|UNKNOWN) constants.

      public static enum Impact {
          INFO, ACTION, ACTION_INFO, UNKNOWN
      }

      Enumeration RegistrationKind

      public enum RegistrationKind {
          REGISTER, DEREGISTER, FAIL
      }

      Enumeration AttributeAccess

      public enum AttributeAccess {
          READ, WRITE, READWRITE
      }

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

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

      Usage

      Define the Service

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

      Alternatives

      Adopting Spring annotations

      Spring JMX Annotations

      Instead of creating a similar but new set of annotations fitted for the in-JDK JMX implementation the already existing Spring annotations could be used.

      Pros

      • Existing, well-known solution
      • Wide user base
      • Better coverage of the standard Descriptor fields

      Cons

      • Different namespace
      • Possible dependencies on the Spring container
      • No method parameter annotations - worked around by @ManagedOperationAttributes
      • No support for automatic conversion of annotated fields to managed attributes

      Re-introducing JMX 2.0 annotations

      JMX 2.0 Annotations Proposal

      This proposal was the part of JSR 255. It served as an inspiration for this JEP while trying to improve the ease of use and reduce complexity by implementing just the most important features.

      The JMX 2.0 annotations implementation is cca. 90% complete with test cases.

      Pros

      • Almost readily available
      • Allows the usage of annotations like @Description or @DescriptorKey/Fields even in classes not annotated by @MBean or @MXBean
      • Standard @Resource annotation is used to inject resources

      Cons

      • No method parameter annotations
      • No support for automatic conversion of annotated fields to managed attributes
      • Dependency on other parts of JSR 255 (eg. MXBeanMappingFactory and MXBeanMapping being a public API)
      • @Resource annotation used for CDI has been moved to the jaxws project and is no longer part of the core Java APIs in JDK 9
      • Complexity
        • annotations may define a standard MBean as well as MXBean
        • overloaded meaning of @MXBean
        • @DescriptorFields takes just plain text and expects certain formatting

      Testing

      Unit tests with good code coverage will be provided. Current reg tests and JCK tests must still pass unchanged.

      Risks and Assumptions

      There are no major risks associated with this feature. Similar work has already been done by the others so this is no uncharted territory.

      Dependences

      No currently known dependencies.

      Impact

      • User experience: Significantly simplified development of custom MBeans
      • I18n/L10n: The annotations docs will need to be translated
      • Documentation: The annotations API will need to be documented

      Attachments

        Activity

          People

            Unassigned Unassigned
            jbachorik Jaroslav Bachorík
            Harsha Wardhana B Harsha Wardhana B (Inactive)
            Mikael Vidstedt, Staffan Larsen (Inactive)
            Mikael Vidstedt
            Votes:
            1 Vote for this issue
            Watchers:
            13 Start watching this issue

            Dates

              Created:
              Updated: