Sunday, 8 March 2015

Getting notified when Java EE Application is ready

Introduction

Within the Java EE ecosystem, you have various options when you want to perform some kind of action when the application is just deployed or server is up and running again.

This text gives a review of them. Or how to get ready when Java EE is up and running.

EJB

One of the most known options, probably because it is the easiest option, is the @Startup annotation for the @Singleton EJB bean (since EJB 3.1; December 2009 included in Java EE 6)

Singleton beans can have the indication that they need to be created and initialised when the container is booting up.  This gives us the option to perform some initialisation for the application.

@Singleton
@Startup
public class StartupEJB {

    @PostConstruct
    public void init() {
        // EJB Ready    }

}

Servlet

The oldest option is using the servlet infrastructure.  The ServletContextListener, available since Servlet 2.3; September 2001, allows you to perform the initialisation steps you want.

public class MyServletContextListener implements ServletContextListener {

    @Override
    public void contextInitialized(ServletContextEvent sce) {
        // Servlet ready    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {

    }
}

The downside of this approach is that you need to register the class in the web.xml configuration file.

What are your options if you want to have a solution with annotations only where you don’t need any configuration in an XML file?

You can annotate a HttpServlet with the @WebServlet annotation where you indicate the loadOnStartup member (Servlet 3.0; december 2009 - Java EE 6)

@WebServlet(loadOnStartup = 2, urlPatterns = "/test")
public class ServletStartup extends HttpServlet {

    @Override
    public void init(ServletConfig config) throws ServletException {
        // Servlet Ready    }
}

But why should we create a servlet which we only use for the init method and not for some real functionality.  This is not a real option.

Another options is including DeltaSpike in your application. Or use it because there is a good chance that you can use some other goodies from the CDI framework. They have created a ‘bridge’ between the ServletContextListener and a CDI event.  They register a listener, with the help a a web.xml fragment which can be located within a jar file, and fire a CDI event. (compatible with Servlet 3.0 containers; works with any Java EE 6 application server)

public class DSServletStartup {
    
    public void onCreate(@Observes @Initialized ServletContext context) {
        // Servlet Ready (DS version)    }
}

PS. The @Initialised is not the CDI one because it is not available in Java EE 6.

This way, you can have a configuration less way to get notified when the Servlet system is ready.

CDI

The CDI specification was the last one of the major 4, who has defined the possibility to get notified of the availability of the system.
Only recently, with the CDI 1.1 version; may 2013 (Java EE 7); you have the possibility to receive a CDI event when the container is ready.

public class CDIStartup {

    public void postConstruct(@Observes @Initialized(ApplicationScoped.class) Object o) {
        // CDI Ready    }
}

JSF

The last framework that I will include in this overview, has again since quite some time the possibility to have some feedback on startup.
You have the system event PostConstructApplicationEvent (together with SystemEventListener JSF 2.0; July 2009)

This listener, with the correct event, must be defined within the faces configuration file (faces-config.xml) to have it executed when JSF system is ready.

<application>
    <!-- Application is started -->    <system-event-listener>
        <system-event-listener-class>be.rubus.squad.startup.JsfStartup</system-event-listener-class>
        <system-event-class>javax.faces.event.PostConstructApplicationEvent</system-event-class>
    </system-event-listener>
</application>

public class JsfStartup implements SystemEventListener {
    @Override
    public void processEvent(SystemEvent systemEvent) throws AbortProcessingException {
        // JSF Ready    }

    @Override
    public boolean isListenerForSource(Object o) {
        return true;    }
}

Since it requires some configuration in an XML file, I also created some kind of bridge so that a CDI event is emitted instead.  (see Jerry SystemStartup)

Order?

Just out of curiosity, I have created a web application where I coded the 4 feedback mechanism.  And I compared the order in which they occurred  on WildFlay 8.2 and GlassFish 4.1.  The 2 main Java EE 7 application servers available today.


WildFlyGlassFish
EJB
CDI
Servlet
JSF
@WebServlet(loadOnStartup)
EJB
JSF
CDI
Servlet
@WebServlet(loadOnStartup)

It is no surprise that the order is different on the 2 servers, but only JSF is ready at a different moment. Because there is, to my knowledge, never described in any specification which is the order of initialisation of the different subsystem in the Java EE server.
But it really doesn’t matter, I don’t think there is any useless where you need to rely on the order of the startup.

What initialisation to choose

Whenever the initialisation needs some database access, the EJB is the natural choose because we have transaction support available. In those situations, @Startup is easy to use.

The other way that I use quit often, is the JSF initialisation since a lot of the applications I’m involved in are JSF based.  That is the reason why I created some small utility class to convert the PostConstructApplicationEvent of JSF to a CDI event.

Have fun.



6 comments:

  1. Excellent post!!!

    Just a question: is there any way to have database access without EJB? I'm using Weld CDI and I'm getting and exception: JBAS011469: Transaction is required to perform this operation.

    Thanks

    ReplyDelete
    Replies
    1. Yes there is. But you always need a transaction, as the exception indicates. You can define your own CDI interceptor. You can look at the DeltaSpike project for inspiration (http://deltaspike.apache.org/documentation/jpa.html#__transactional) or add the JPA data module of DS.

      Delete
    2. I've set the application to use transaction, and it's working perfectly. It has lots of beans annotated with @RequestScope/@ViewScope/@ApplicationScope that use transactions. The problem is just happening with the JSF PostConstructApplicationEvent class, that I created to ensure that the DB is correctly populated when the application starts. In this case I annotated the class with @Transaction, @ApplicationScope, and so on. But it didn't work.

      Delete
  2. Thanks for this. What I am looking for, is a possibility to get a notification AFTER the deployment has been finished. All these methods here are triggered just before the deployment process has been finished. Even though we could think the application is ready, the endpoints (e.g. REST) are not yet available (the application server responds to all with 404 at time these triggers are executed) and if we throw an exception from one of these, the whole deployment process is rolled back and the application is not available. Is there no way to get a trigger, after the deployment has been finished?

    ReplyDelete