How to invoke code when JBoss EAP 7 is fully started

Solution Verified - Updated

Environment

  • Red Hat JBoss Enterprise Application Platform (EAP)
    • 8.0+
    • 7.1+

Issue

How to invoke code when JBoss EAP 7 is fully started

Resolution

Using an MBean Notification Listener as described in JBoss EAP Configuration Guide - Server Lifecycle Event Notifications , an application can register an MBean Notification listener and when it receives the notification that JBoss has finished starting, it can invoke a method that makes an external request back to the server it is running in, such as making an HTTP call, REST call, Remote EJB call over the network.

Below is a simple example of an EJB Singleton that registers a MBean listener and invokes code in its serverStarted method when EAP is started and in normal mode and starting to take external requests. This will work if EAP is started in normal mode or if it is started in suspended mode, when started in suspended mode, the serverStarted is called once it moved to normal mode.

@Startup
@LocalBean
@Singleton
public class ExampleSingletonBean {
    private static final String JBOSS_ROOT_STATE_MBEAN = "jboss.root:type=state";
    private static final String RUNNING_STATE = "RunningState";
    private static final String SUSPENDED = "suspended";
    private static final String NORMAL = "normal";

    @PostConstruct
    public void init() {
        try {
            ObjectName jbossStateObjectName = ObjectName.getInstance(JBOSS_ROOT_STATE_MBEAN);
            NotificationListener listener = new NotificationListener() {
                @Override
                public void handleNotification(Notification notification, Object handback) {
                    try {
                        AttributeChangeNotification attributeChangeNotification = (AttributeChangeNotification) notification;
                        if (RUNNING_STATE.equals(attributeChangeNotification.getAttributeName())) {
                            if (SUSPENDED.equals(attributeChangeNotification.getOldValue())
                                    && NORMAL.equals(attributeChangeNotification.getNewValue())) {
                                serverStarted();
                            }
                        }
                    } catch (Throwable e) {
                        e.printStackTrace();
                    }
                }
            };
            // register the listener
            ManagementFactory.getPlatformMBeanServer().addNotificationListener(jbossStateObjectName, listener, null, null);

            // register this as an mbean notification listeners
            if (NORMAL.equalsIgnoreCase(
                    ManagementFactory.getPlatformMBeanServer().getAttribute(jbossStateObjectName, RUNNING_STATE).toString())) {
                serverStarted();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void serverStarted() {
        logger.info("******* ExampleSingletonBean: - EAP fully started *******");
    }
}

Below is a similar example to the EJB Singleton except using a CDI Bean

@ApplicationScoped
public class ExampleCDIBean implements NotificationListener {

    private static final String JBOSS_ROOT_STATE_MBEAN = "jboss.root:type=state";
    private static final String RUNNING_STATE = "RunningState";
    private static final String SUSPENDED = "suspended";
    private static final String NORMAL = "normal";

    // when CDI is initialized , register the MBean listener
    public void initialized(@Observes @Initialized(ApplicationScoped.class) Object obj) {
        log.infof("@ApplicationScoped initialized_ at %d", System.currentTimeMillis());

        try {
            private ObjectName jbossStateObjectName = ObjectName.getInstance(JBOSS_ROOT_STATE_MBEAN);
            // register the listener
            ManagementFactory.getPlatformMBeanServer().addNotificationListener(jbossStateObjectName, this, null, null);

            // register this as an mbean notification listeners
            if (NORMAL.equalsIgnoreCase(
                    ManagementFactory.getPlatformMBeanServer().getAttribute(jbossStateObjectName, RUNNING_STATE).toString())) {
                log.info("JBoss Already started, sending events");
                serverStarted();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // when CDI is being shutdown , unregister the mbean listener
    public void beforeDestroyed(@Observes @BeforeDestroyed(ApplicationScoped.class) Object obj) {
        log.infof("@ApplicationScoped beforeDestroyed at %d", System.currentTimeMillis());

        // remove the listener
        try {
            ManagementFactory.getPlatformMBeanServer().removeNotificationListener(jbossStateObjectName, this);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void handleNotification(Notification notification, Object handback) {

        try {
            Thread.currentThread().setContextClassLoader(Example5ServerStartedCDIListener.class.getClassLoader());
            AttributeChangeNotification attributeChangeNotification = (AttributeChangeNotification) notification;
            if (RUNNING_STATE.equals(attributeChangeNotification.getAttributeName())) {

                if (SUSPENDED.equals(attributeChangeNotification.getOldValue())
                        && NORMAL.equals(attributeChangeNotification.getNewValue())) {

                    serverStarted();
                }
            }
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }
    private void serverStarted() {

        // put app init code here
        logger.info("********* ExampleCDIBean Server Started *********");
    }
}

See the attached examples eap-started-listener-examples.zip, which shows the above examples as well as using them as an abstract class where you can share the abstract class between multiple applications.

eap-started-listener-examples.zip:

  • Example 1 - EJB Singleton that just adds a mbean listener
  • Example 2 - The mbean listener is abstracted into its own class, where beans can register it and implement a callback that gets invoked when EAP is started
  • Example 3 - The mbean listener is registered and sends a custom CDI event that CDI beans can observe, this is useful if you have several beans that need to run code when EAP is fully started.
    This example also shows how you can use a CDI Bean to instead of an EJB Singleton to run code when the CDI bean is deployed.
  • Example 4 - Example of an abstract EJB Singleton, this class could be shared in a custom JBoss Module among multiple applications, then applications can just extend AbstractSingletonBean and implement serverStarted to run code when EAP is started
  • Example 5 - Example of an abstract CDI Bean, this class could be shared in a custom JBoss Module among multiple applications, then application can just extend Example5ServerStartedCDIListener and implement serverStarted to run code when EAP is started
Components
Category

This solution is part of Red Hat’s fast-track publication program, providing a huge library of solutions that Red Hat engineers have created while supporting our customers. To give you the knowledge you need the instant it becomes available, these articles may be presented in a raw and unedited form.