Design notes for "Ability to register a listener (integrated with the management layer) that will be notified of the lifecycle server events" (NotificationRegistrars)

Version 2

    Requirements summary

     

    We do not support remote management notifications yet, so all registered notification handlers need to be in-vm. The current/obvious way to register a notification handler is to implement a subsystem to register these notifications, however that is quite complicated to do for end users. The aim is to make it simpler for users to register their own implementations of notification listeners by having people implement a NotificationRegistrar interface, and register that class in a central place in the management model.

     

    This is quite a straightforward requirement. Users specify the NotificationRegistrar implementation, and it gets called when the resource defining the NotificationRegistrar implementation is added.

     

    Since this feature is already implemented, the detail is in the following design notes.

     

    Design notes

     

    In the standalone case the notification registrars are registered under:

         /core-service=management/service=notification-registrat/registrar=*

     

    In the host controller case the notification registrars are registered under:

         /host=<host-name>/core-service=management/service=notification-registrar/registrar=*

     

    These resources are used to register implementations of the NotificationRegistrar interface, which is explained later. In the HC case, the NotificationRegistrar implementation is instantiated and started on both the HC itself, and on the servers it controls.

     

    In both cases the resource takes the following parameters:

    • code - the class name of the NotificationRegistrar implementation. It is a required string.
    • module - the module containing the NotificationRegistrar implementation. It is a required string.
    • properties - a map of key/value pairs which can be used to configure the NotificationRegistrar.

     

    Interfaces

     

    There are three new, central interfaces involved. The user implements the NotificatioRegistrar interface, and they then make use of the NotificationRegistrarContext and NotificationRegistry references that they get passed to register notification handlers.

     

    NotificationRegistrar

    The NotificationRegistrar interface to be implemented by users looks like:

     

    /**

     

    * Interface for users to implement in order to register notification handlers from the

     

    * {@code /core-service=management/service=notifications} resources.

    *

    * @author Kabir Khan

    */

    public interface NotificationRegistrar {

        /**

        * Registers notification handlers. The notification handlers will be automatically removed when this registrar

        * is removed.

        *

        * @param context the context

        */

        void registerNotificationListeners(NotificationRegistrarContext context);


        /**

        * Callback to do any cleanup required when removing this registrar.

        */

        default void cleanup() {

        }

    }

     

    When the resource is added, a service is installed in the add handler's RUNTIME stage. When that service starts, NotificationRegistrar.registerNotificationListeners() gets called. Note that the add handler's RUNTIME stage gets called regardless of if we are running in admin-only mode, or normal mode.

     

    NotificationRegistrar.registerNotificationListeners() then instantiates and registers notification handlers using the NotificationRegistrarContext. Any notification handlers that get registers during this call are automatically removed when the handler is removed. The cleanup() method can be used to perform additional cleanup of the registered handlers (such as closing a connection), and it is called after the handlers are unregistered.

     

    NotificationRegistrarContext

    The NotificationRegistrarContext contains operations to register the notification handlers, and to get information about the process we are running in:

    org.jboss.as.controller.notification;

     

    import java.util.Map;

     

    import org.jboss.as.controller.ProcessType;

    import org.jboss.as.controller.RunningMode;

    import org.jboss.as.controller.client.ModelControllerClient;

    /**

    * Encapsulation of data to register notifications via the {@link NotificationRegistrar}

    *

    * @author Kabir Khan

    */

    public interface NotificationRegistrarContext {

        /**

        * Get the name of this registrar

        *

        * @return the name

        */

        String getName();


        /**

        * Get the notification registry into which notification handlers will be installed

        *

        * @return the notification registry

        */

        NotificationRegistry getNotificationRegistry();


        /**

        * Get the process type of the process

        *

        * @return the process type

        */

        ProcessType getProcessType();


        /**

        * Get the running mode of the process

        *

        * @return the running mode

        */

        RunningMode getRunningMode();


        /**

        * Get the properties for configuring the notification handler(s)

        *

        * @return the properties

        */

        Map<String, String> getProperties();

     

        /**

     

        * Get the model controller client for the process

        *

        * @return the client

        */

        ModelControllerClient getModelControllerClient();

    }

     

    The getName() method is the name of the registrar=* resource installing this NotificationRegistrar.

     

    getNotificationRegistry() gives us access to the NotificationRegistry which will be mentioned below.


    When running on a host controller the NotificationRegistrar gets installed on both the host controller and its managed servers. The getProcessType() method allows us to determine which process we are running in. We can then make choices such as whether to install the notification handlers or not, or whether to configure the handlers differently (perhaps writing to different file locations).

     

    getRunningMode() tells us whether we are running in admin-only or normal mode.

     

    getProperties() gives us access to the key/value pairs defined in the properties attribute of the resource defining the NotificationRegistrar implementation. This allows us to configure the notification handlers installed by the NotificationRegistrar implementation.

     

    getModelControllerClient() gives us access to the model controller client. This should never be called during the NotificationRegistrar.registerNotificationListeners() call but can be stored for later use by the installed notification handlers.

     

    NotificationRegistry

    The NotificationRegistry is used to register the notifications:

    package org.jboss.as.controller.notification;

     

    import org.jboss.as.controller.PathAddress;

     

    /**

    * Interface exposed to the users to register notifications via the {@link NotificationRegistrar}

    *

    * @author Kabir Khan

    */

    public interface NotificationRegistry {

        /**

        * Register the given NotificationHandler to receive notifications emitted by the resource at the given source address.

        * The {@link org.jboss.as.controller.notification.NotificationHandler#handleNotification(org.jboss.as.controller.notification.Notification)} method will only be called on the registered handler if the filter's {@link org.jboss.as.controller.notification.NotificationFilter#isNotificationEnabled(org.jboss.as.controller.notification.Notification)}

        * returns {@code true} for the given notification.

        *

        * @param source  the path address of the resource that emit notifications.

        * @param handler the notification handler

        * @param filter  the notification filter. Use {@link org.jboss.as.controller.notification.NotificationFilter#ALL} to let the handler always handle notifications

        */

        void registerNotificationHandler(PathAddress source, NotificationHandler handler, NotificationFilter filter);

    }

     

    The NotificationRegistry is used to perform the registration of the notification handlers. During the NotificationRegistrar.registerNotificationListeners() call, the NotificationRegistrar implementation instantiates the notification handlers and calls this method to register them. The parameters of this method are:

    • source - the address to register the notification handlers against. Multiple calls can be made to register handlers for different addresses.
    • handler - the NotificationFilter (exists in EAP 7.0 already) implementation to filter the notifications we are interested in.
    • filter - a NotificationFilter (exists in EAP 7.0 already) implementation to filter the notifications we are interested in.

     

    Issue Metadata

    Downstream: https://issues.jboss.org/browse/EAP7-472

    Upstream: https://issues.jboss.org/browse/WFCORE-1405

    Upstream PR: https://github.com/wildfly/wildfly-core/pull/1553

    Development contact: Kabir Khan

    QA contact: ???

    Related to: https://issues.jboss.org/browse/EAP7-471/https://issues.jboss.org/browse/WFCORE-1157

    Polarion Test Plan: TBD