Register shutDownHook in web application

29,622

Solution 1

registerShutdownHook() in standalone (non-web) application:

The @PreDestroy annotation is used on bean method to be notified when the bean is being removed from the context or when the context is shutting down.

Shut down event is fired when context.close() or context.registerShutdownHook() is invoked.

@Component(value="someBean")
public class SomeBean {

    @PreDestroy
    public void destroy() {
        System.out.println("Im inside destroy...");
    }
}

I hope you already know this.


registerShutdownHook() in web application:

In a web application, DispatcherServlet/ContextListener creates the ApplicationContext and it will close the context when the server shutdown. You don't need to explicitly invoke context.close() or context.registerShutdownHook().

When the server shutdown, @PreDestory methods on your bean will be notified automatically.

Solution 2

In web applications, you can use a ServletContextListener which fires when your application is deployed and undeployed:

public class MyServletContextListener implements ServletContextListener {
    public void contextInitialized(ServletContextEvent sce) {
        //application is being deployed
    }
    public void contextDestroyed(ServletContextEvent sce) {
        //application is being undeployed
    }
}

You can access to your Spring beans by retrieving the current Spring context:

public void contextDestroyed(ServletContextEvent sce) {
    ServletContext ctx = sce.getServletContext();
    WebApplicationContext springContext = WebApplicationContextUtils.getWebApplicationContext(ctx);
    //retrieve your Spring beans here...
    SomeSpringBean bean = (SomeSpringBean)ctx.getBean("someSprinbgBean");
    //...
}

Solution 3

Using Spring 3+ you can add a ContextCleanupListener to the application context.

Register your listener at startup like so (you might prefer to use xml config but the same applies)

package com.myapp

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;

import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.ContextCleanupListener;
import org.springframework.web.context.ContextLoaderListener;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;

public class MyWebApplicationInitializer implements WebApplicationInitializer {

    @Override
    public void onStartup(ServletContext servletContext)
            throws ServletException {
        WebApplicationContext appContext = getContext();


        servletContext.addListener(new ContextLoaderListener(appContext));

        // line adding an implementation of ContextCleanupListener
        servletContext.addListener(new MyWebApplicationCleanupListener());

        ServletRegistration.Dynamic dispatcher = servletContext.addServlet("DispatcherServlet", new DispatcherServlet(appContext));
            dispatcher.setLoadOnStartup(1);
            dispatcher.addMapping("/");
    }

    private AnnotationConfigWebApplicationContext getContext() { 
        AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
        context.setConfigLocation("com.myapp");
        return context;
    }
} 

Implementation of ContextCleanupListener that runs your shutdown code:

package com.myapp;

import javax.servlet.ServletContextEvent;
import com.myapp.resources.requiring.clean.shutdown
import org.springframework.web.context.ContextCleanupListener;

public class MyWebApplicationCleanupListener extends ContextCleanupListener {

    @Override
    public void contextDestroyed(ServletContextEvent event) {
        // put your shutdown code in here

        MyResourceNeedingShutdown dataStore = MyResourceNeedingShutdown.getInstance();
        dataStore.shutdown();
    }

}

When you run up say tomcat for example, and press CTRL + C to shut it down, you'll immediately see the contextDestroyed method be hit in the debugger if you put a breakpoint there.

Share:
29,622

Related videos on Youtube

slisnychyi
Author by

slisnychyi

software engineer

Updated on May 26, 2020

Comments

  • slisnychyi
    slisnychyi almost 4 years

    How we can registerShutdown hook in web application?

    Is there any whays to register it in web.xml or in applicationContext.xml?

    I know that if we are using application with main class it's simple.

    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");
        context.registerShutdownHook();
    

    But what about web application? As it uses ContextListener

    • Kanagavelu Sugumar
      Kanagavelu Sugumar almost 7 years
      Have you got any solution ?
  • slisnychyi
    slisnychyi almost 10 years
    Thank you, for answer. Could you tell, how I can get object that stands for SPRING context. On which I can call context.registerShutdownHook();
  • Kalyan
    Kalyan almost 10 years
    Use can use @PreDestroy method in bean.
  • Luiggi Mendoza
    Luiggi Mendoza almost 10 years
    @Javakid that won't work if you want to only fire a process when the application is being undeployed.
  • Kalyan
    Kalyan almost 10 years
    @Luiggi Thanks. Why do you think it wouldn't work and when do you think @PreDestory will be invoked? I just tried to stop and undeploy the application, the method gets notified.
  • Luiggi Mendoza
    Luiggi Mendoza almost 10 years
    @Javakid try changing the scope of the bean to prototype and retrieve it two or more times.
  • Kalyan
    Kalyan almost 10 years
    :) Sorry, but you still didn't give an explanation. ServletContextListener is used for global cleanup task and @PreDestroy is used for bean specific cleanup task. You can choose either of them depending on your need. When using prototype, @PreDestroy will be invoked whenever the bean is removed from the context. Not many people I know use prototype correctly. So I can't say how you meant to use it. Anyway I'll leave it here.
  • Luiggi Mendoza
    Luiggi Mendoza almost 10 years
    @Javakid I don't need to give an explanation if you understand what will happen with beans as prototype: the process will be invoked per instance removed from the context. And people don't use prototype (correctly or not) mainly because the default scope for beans is singleton. Since OP haven't provided a way the process should behave nor any scenario for it, this can be the best option. Maybe the clean-up process needs several beans and an orchestration of these.
  • Kalyan
    Kalyan almost 10 years
    I kindly like to let you know that I don't agree with stmt Maybe the clean-up process needs several beans and an orchestration of these. That means you are not following Separation of concern law. Each bean should perform its own cleanup task. However, there may be an extreme scenario where global cleanup task might be required.
  • Luiggi Mendoza
    Luiggi Mendoza almost 10 years
    @Javakid when maintaining legacy applications, specially very old applications, there's no separation of concern law (just look at a monolithic architecture the application has), so this is the available option. The only thing I'm sure here is that this is the safest approach in case you want a single specific place to fire any clean up process in general web applications. Framework specific benefits (or backdoors to your own doom) are not covered in my answer, and it's good to know about @PreDestruct for this case. Just to note, @PreDestruct is not from Spring but from Java EE.
  • Kalyan
    Kalyan almost 10 years
    Thanks for your effort. Although @PreDestory is from Java EE, it is very much part of Spring annotation. There is a reason for not introducing new annotation for XML "destroy" attribute alternative and Spring community decided to use the existing annotation in Java EE.
  • Luiggi Mendoza
    Luiggi Mendoza almost 10 years
    @Javakid that wasn't the meaning of the last sentence of my comment. Anyway, looks like we both know what we're doing. Hopefully, OP will understand and decide the best option as well :)
  • Kalyan
    Kalyan almost 10 years
    +1 for your effort lol... Have a good night...:) The last message went away before I could finish.
  • slisnychyi
    slisnychyi almost 10 years
    Thanks for your suggestion. But this approach is not working. I added necessary function @Override public void contextInitialized(ServletContextEvent sce) { ServletContext context = sce.getServletContext(); WebApplicationContext springContext = WebApplicationContextUtils.getWebApplicationContext(context)‌​; ClassPathXmlApplicationContext applicationContext = (ClassPathXmlApplicationContext) springContext.getParent(); applicationContext.registerShutdownHook(); } But when I call applicationContext.registerShutDownHook().I got NullPointer excaption
  • Philippe
    Philippe over 8 years
    Excellent response. The @PreDestroy was the key! Thanks a lot :-)
  • Anand Kadhi
    Anand Kadhi over 8 years
    @Kalyan :: @ Predestroy is called for Bean destruction, that means if the bean is not singleton this would be invoked multiple times whenever the container removes the bean.
  • mre
    mre almost 7 years
  • Kanagavelu Sugumar
    Kanagavelu Sugumar almost 7 years
    I think i am missing to do this "In order to receive these notification events, the implementation class must be either declared in the deployment descriptor of the web application, annotated with WebListener, or registered via one of the addListener methods defined on ServletContext."
  • Kanagavelu Sugumar
    Kanagavelu Sugumar almost 7 years
    @LuiggiMendoza Your Solution is working, i have missed adding entry in the web.xml :( Thank you!! very much.
  • Kanagavelu Sugumar
    Kanagavelu Sugumar almost 7 years
    Is there a way only calling PreDestroy, because PostConstruct is called by ServletContextListener, but getting NPE because of; some of fields are not autowired at that time. Please help.
  • Luiggi Mendoza
    Luiggi Mendoza almost 7 years
    @KanagaveluSugumar Spring will wire everything for you. If something cannot be wired is because it was lazy wiring or the bean couldn't been created, and you have to check why wasn't created.
  • Kanagavelu Sugumar
    Kanagavelu Sugumar almost 7 years
    In your code sample given the variable springContext is null we are getting. This is same even @slisnychyi mentioned in this comment section. Also there is TYPO in the sample ctx.getBean() should springContext.getBean()