0

I'm trying to perform some checks at the startup of a spring web application (e.g. check that the DB version is as excepted). If the checks fail, the servlet should be killed (or better yet, never started) so as to prevent it from serving any pages. Ideally, the containing Tomcat/Netty/whatever service should also be killed (although this looks more tricky).

I can't call System.exit because my startup check depends on a lot of services that should be safely shut down (e.g. DB connections, etc...).

I found this thread, which suggests calling close on the spring context. However, except for reporting exceptions, spring merrily continues to start up the servlet (see below).

I've looked into the Java Servlet documentation -- it says not to call destroy on the servlet - and I've no idea whether I'd be calling Servlet.destroy from methods where the Servlet object appears further up the stack (don't want to eat my own tail). In fact, I'd rather the servlet was never created in the first place. Better to run my at-startup checks first, before starting any web-serving.

Here's what I have...

@Service
class StartupCheckService extends InitializingBean {

  @Autowired a:OtherServiceToCheckA = null
  @Autowired b:OtherServiceToCheckB = null

  override def afterPropertiesSet = {
    try{

       checkSomeEssentialStuff();   

    } catch {
      case e: Any => {

        // DON'T LET THE SERVICE START!
        ctx = getTheContext();
        ctx.close();

        throw e;
   }
}

The close call causes the error:

BeanFactory not initialized or already closed - call 'refresh' before accessing beans via the ApplicationContext.

presumably because you shouldn't call close while bean initialization is happening (and refresh is likely to put us into an infinite loop). Here's my startup code...

class WebAppInitializer extends WebApplicationInitializer {

  def onStartup(servletContext: ServletContext): Unit = {

    val ctx = new AnnotationConfigWebApplicationContext()

    // Includes StartupCheckService
    ctx.register(classOf[MyAppConfig])  
    ctx.registerShutdownHook() // add a shutdown hook for the above context...


    // Can't access StartupCheckService bean here.  


    val loaderListener = new ContextLoaderListener(ctx)
    // Make context listens for servlet events
    servletContext.addListener(loaderListener)

    // Make context know about the servletContext
    ctx.setServletContext(servletContext)

    val servlet: Dynamic = servletContext.addServlet(DISPATCHER_SERVLET_NAME, new DispatcherServlet(ctx))
    servlet.addMapping("/")
    servlet.setLoadOnStartup(1)
}

I've tried doing this kind of thing in onStartup

ctx.refresh()
val ss:StartupService = ctx.getBean(classOf[StartupCheckService])
ss.runStarupRountines()

but apparently I'm not allowed to call refresh until onStartup exits.

Sadly, Spring's infinite onion of abstraction layers is making it very hard for me to grapple with this simple problem. All of the important details about the order of how things get initialize are hidden.

Before the "should have Googled it" Nazis arrive... A B C D E F

Community
  • 1
  • 1
user48956
  • 11,390
  • 14
  • 67
  • 125

2 Answers2

1

I'm not sure why you need to do this in a WebApplicationInitializer. If you want to configure a @Bean that does the health check for you then do it in an ApplicationListener<ContextRefreshedEvent>. You can access the ConfigurableApplicationContext from there (the source of the event) and close it. That will shut down the Spring context. Throw an exception if you want the Servlet and the webapp to die.

You can't kill a container (Tomcat etc.) unless you started it. You could try using an embedded container (e.g. Spring Boot will do that for you easily).

Dave Syer
  • 52,217
  • 10
  • 149
  • 137
1

As far as I understand, you don't have to explicitly call close(). Just let the exception escape the afterPropertiesSet(), Spring should automatically stop instantiating remaining beans and shutdown the whole context.

You can use @PreDestroy if you have to make some cleanup on beans which have been initialized so far.

Guillaume Darmont
  • 4,702
  • 1
  • 19
  • 34