How to intercept JVM shutdown in a Container

Java applications can require some cleanup / checks before the JVM is stopped. In this tutorial we will learn how to intercept a JVM shutdown in a container such as WildFly / JBoss EAP or Quarkus. When the shutdown is intercepted, we will execute some cleanup work before the JVM finishes shutting down.

#1 Add a shutdown hook to your application server.

A shut down hook is simply a Thread that is registered with the JVM and run just before the JVM shuts down. You can add it to any Class that is instantiated by your container. In this example we will add it to the init() method of a Servlet which is registered as Startup Servlet.

package sample;

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

 
public class ServletShutdownHook extends HttpServlet {
    private static final long serialVersionUID = 1L;
       
     
    public ServletShutdownHook() {
        super();
        
    }

    public void init() {
    MyShutdown sh = new MyShutdown(this);
        Runtime.getRuntime().addShutdownHook(sh);
        System.out.println("Added shutdown hook");        
    }
     
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }

     
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
         
    }

    class MyShutdown extends Thread {
        public MyShutdown(ServletShutdownHook managedClass) {
            super();
            this.managedClass = managedClass;
        }
        private ServletShutdownHook managedClass;
        public void run() {
            System.out.println("MyShutDown Thread started");
            try {
                managedClass.freeResources();
            } catch (Exception ee) {
                ee.printStackTrace();
            }
        }
    }

    public void freeResources() {
        System.out.println("Freeing resources here!");

    }

}

And here is its mapping:

<servlet>
    <servlet-name>ServletShutdownHook</servlet-name>
    <servlet-class>sample.ServletShutdownHook</servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>ServletShutdownHook</servlet-name>
    <url-pattern>/hook</url-pattern>
  </servlet-mapping>

Here, the critical piece of code is in the init() method which registers the Shutdown hook on the class MyShutDown.

MyShutdown sh = new MyShutdown(this);
Runtime.getRuntime().addShutdownHook(sh);

The run() method in the Class MyShutdown will be fired as soon as shutdown kicks in and recalls the method freeResources from the Servlet.

#2 Add an MBean to your deployments and override the stopService method

This approach is less portable since it requires extending the ServiceMBeanSupport Class which exposes JBoss MBeans lifecycle methods.

package com.sample;

import org.jboss.system.ServiceMBeanSupport;


public class StartupService extends ServiceMBeanSupport 
implements 
StartupServiceMBean {

    public StartupService() { }


    @Override
    protected void startService() { }

    @Override
    protected void stopService() throws Exception
    {
        log.info("[StartupService ] Stopping Startup Mbean");
        freeResources();
    }
 
    public void freeResources() {
           // Do housework here
    }

}

package com.sample

import org.jboss.system.ServiceMBean;

public interface StartupServiceMBean extends ServiceMBean {
    public void clearSessions();
}

Here, when the shutdown sequence gets started, and before the MBean deployed is evicted from memory, the stopService method is invoked, which cares to free resources. 

#3 Use @PreDestroy annotation to clean the house

If you application uses EJB to handle resources which need to be evicted at shutdown, then consider using the @PreDestroy annotation as a “finalizator”.

@Stateless
public class SampleEJB {

    @PreDestroy
    public void destroy( ) {
      // Deallocate resources acquired here
    }

}

The destroy method will kick in as soon as the bean is being de-allocated, including JBoss shutdown.

Java 11 Update: The PreDestroy annotation is part of Java EE. And since Java EE has been deprecated in Java 9 and removed in Java 11. To make it running in Java 11 you have to add an additional dependency to use these annotations:

<dependency>
    <groupId>javax.annotation</groupId>
    <artifactId>javax.annotation-api</artifactId>
    <version>1.3.2</version>
</dependency>

#4 Use Quarkus Lifecycle events

If you are coding Quarkus applications in JVM mode, then you have an easy solution: you can add a Class which implements lifecycle events. Example:

import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.event.Observes;

import io.quarkus.runtime.ShutdownEvent;
import io.quarkus.runtime.StartupEvent;
import org.jboss.logging.Logger;

@ApplicationScoped
public class AppLifecycleBean {

    private static final Logger LOGGER = Logger.getLogger("ListenerBean");

    void onStart(@Observes StartupEvent ev) {               
        LOGGER.info("The application is starting...");
    }

    void onStop(@Observes ShutdownEvent ev) {               
        LOGGER.info("The application is stopping...");
    }

}

In this example, you can control the application (and thus the JVM) lifecycle with the following events:

  • onStart: which is capturing the application startup
  • onStop: which is capturing the application shutdown

Read more: How to manage the lifecycle of a Quarkus application

Found the article helpful? if so please follow us on Socials