What is difference between ContextRefreshedEvent, ContextStartedEvent, ContextStoppedEvent and ContextClosedEvent - spring

In Spring 5.x, what is the difference between following events?
ContextRefreshedEvent
ContextStartedEvent
ContextStoppedEvent
ContextClosedEvent
Which event correlate with the servlet context events (as per https://docs.oracle.com/javaee/6/api/javax/servlet/ServletContextListener.html):
ServletContextListener.contextInitialized(ServletContextEvent); and
ServletContextListener.contextDestroyed(ServletContextEvent)?
I have the following situation:
In want to initialize a logging subsystem as soon as possible, should that be done in ContextRefreshedEvent or ContextStartedEvent?
I also want to destruct it as late as possible, should that be done in ContextClosedEvent or ContextStoppedEvent?

The documentation for these built-in events can be found here, specifically:
ContextRefreshedEvent
Published when the ApplicationContext is initialized or refreshed (for example, by using the refresh() method on the ConfigurableApplicationContext interface). Here, “initialized” means that all beans are loaded, post-processor beans are detected and activated, singletons are pre-instantiated, and the ApplicationContext object is ready for use. As long as the context has not been closed, a refresh can be triggered multiple times, provided that the chosen ApplicationContext actually supports such “hot” refreshes. For example, XmlWebApplicationContext supports hot refreshes, but GenericApplicationContext does not.
ContextStartedEvent
Published when the ApplicationContext is started by using the start() method on the ConfigurableApplicationContext interface. Here, “started” means that all Lifecycle beans receive an explicit start signal. Typically, this signal is used to restart beans after an explicit stop, but it may also be used to start components that have not been configured for autostart (for example, components that have not already started on initialization).
ContextStoppedEvent
Published when the ApplicationContext is stopped by using the stop() method on the ConfigurableApplicationContext interface. Here, “stopped” means that all Lifecycle beans receive an explicit stop signal. A stopped context may be restarted through a start() call.
ContextClosedEvent
Published when the ApplicationContext is closed by using the close() method on the ConfigurableApplicationContext interface. Here, “closed” means that all singleton beans are destroyed. A closed context reaches its end of life. It cannot be refreshed or restarted.
RequestHandledEvent
A web-specific event telling all beans that an HTTP request has been serviced. This event is published after the request is complete. This event is only applicable to web applications that use Spring’s DispatcherServlet.
Afaik, none of these correlate directly to the ServletContext. That's a different beast than Spring's application context, and there are separate events for that.
Setting up and tearing down a logging system can be complicated and will depend on which logging system you use. In short, you may want to try with ContextRefreshedEvent and ContextClosedEvent. The other two are only dispatched when you call start() or stop() on the application context, so you wouldn't want to use those.
If you're using Spring Boot, you may want to look at Spring Boot's
own abstraction of logging systems (org.springframework.boot.logging.LoggingSystem), which defines beforeInitialize, initalize, and cleanUp methods, and also a shutdownHandler that is called when the JVM exists.
And see org.springframework.boot.context.logging.LoggingApplicationListener for reference. Spring Boot comes with additional application events. The initialization of the logging system seems to be done on the ApplicationEnvironmentPreparedEvent. Cleanup is done on ContextClosedEvent and ApplicationFailedEvent.

Related

Correct way to find if spring context was started

In my spring bean I want to use send spring event functionality. The problem is event can't be sent if spring context was not initialized and my bean by some reasons can send events before that happen.
I used the following:
implement ApplicationContextAware and use ConfigurableApplicationContext.isActive() - this becomes true in the beginning of the context initialization phase
use ConfigurableApplicationContext.isRunning() - this throws exception IllegalStateException("LifecycleProcessor not initialized...
listen for ContextRefreshedEvent - this doesn't work because this is inner bean and is used as a property for the bean that implements BeanFactoryPostProcessor
implementing SmartLifecycle also doesn't work because for inner beans
So what is the EASY and correct way to determine if context is running and event can be sent?
Hopefully this is already fixed SPR-13667 in spring 4.1.7.

When is ContextRefreshedEvent fired in Spring?

I know that it is fired once when the ApplicationContext is fully loaded, but what about after that during runtime? The word "Refreshed" implies that it will be triggered on a refresh but I wonder what Spring qualifies as an ApplicationContext refresh?
Followup question:
Can this event be triggered by concurrent threads? Do I need to make the EventHandler for this event thread safe?
it is fired when properties, xml or any schema files are loaded/refreshed, http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/context/support/AbstractApplicationContext.html#refresh--
Load or refresh the persistent representation of the configuration,
which might an XML file, properties file, or relational database
schema.
It is fired implicitly by spring usually, but you should be able to fire that on certain instances, But here is java doc says when that happens
As this is a startup method, it should destroy already created
singletons if it fails, to avoid dangling resources. In other words,
after invocation of that method, either all or no singletons at all
should be instantiated.

Spring AMQP ListenerContainer lifecycle management

We are using Spring AMQP to connect to RabbitMQ in our Spring based web application.
When we declare our listener-containers as beans (using rabbit:listener-container) in application context, their lifecycle is managed by Spring.
If we declare a listener-container in a component inside a #PostConstruct method, or we create a bean with class org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer as a prototype scoped bean, we then have to manage the lifecycle i.e. start and stop the listener-container ourselves.
My question is, if we declare new queues, bindings and listener-containers inside a #PostConstruct method, just calling listener.stop/shutdown/destroy method inside the corresponding #PreDestroy method would be enough for a graceful shutdown? Or else what do I need to do?
Note: I am guessing I don't have to do anything for the new queues and bindings created in the #PostContruct, but I would be very glad if you also confirm this for me.
I would not recommend starting a listener container or declaring queues/bindings in an #PostConstruct method; the context is only half-baked at that time. It might work but it's not recommended to do stuff like that while the context is being initialized.
It's better to implement SmartLifecycle and start/stop them in the start()/stop() methods.
Then, the container lifecycles would be indirectly managed by the spring context.
You can also control exactly when your bean is started/stopped by putting it in a phase.

Spring session handling: HttpSessionDestroyedEvent not received

I have a web app where in the web.xml I have added the HttpSessionEventPublisher as listener. The web app runs on Jetty 7.x and we are using Spring 3.1.1.
This is supposed to fire HttpSessionCreatedEvent and HttpSessionDestroyedEvent to Spring context event listeners.
I have a Bean (#Controller) that implements ApplicationListener<ApplicationEvent>. ApplicationEvent is the common parent class of HttpSessionCreatedEvent and HttpSessionDestroyedEvent. When I now login to my web application or log out from it I'd expect these events to be fired to the onApplicationEvent(ApplicationEvent event) method. I have received other events like some request handling event, but the expected event's didn't show up. I have traced the app a bit, the HttpSessionEventPublisher definitely fires the event to the context, but the listener isn't approached. what do I miss here?
The issue is that HttpSessionEventPublisher publishes events on the "Spring Root WebApplicationContext" per the javadoc, this is the application context registered through ContextLoaderListener entry in your web.xml file. Your #Controller on the other hand is probably registered through a different application context altogether - the one registered through the DispatcherServlet. So I would suggest that you create a different ApplicationListener, register it to the Root WebapplicationContext, the events should come through then.
Facing a similar problem, if it not possible to move your ApplicationListener to the root context, you can use the org.springframework.security.context.DelegatingApplicationListener.
Just autowire a DelegatingApplicationListener in your Bean and use constructor / #PostConstruct to register your listener against the DelegatingApplicationListener (one should be already in place by spring-security)

#PreDestroy not called on session-scoped Spring bean on tomcat shutdown

Using Spring 3.0.5 GA
Have a Session-scoped bean with #PreDestroy method. Just noticed that if owning HttpSession times out (ie exceeds Servlet Container's session-timeout value) then #PreDestroy call back is issued. However, if I simply shutdown the app server, #PreDestroy is NOT called. Is that by design or is a bug? If latter, any suggestions for a work-around?
FWIW, #PreDestroy on singleton beans gets called in both cases.
thanks,
-nikita
PS. There is a possibly-related Spring bug - SPR-7359
Interesting. Session-scoped beans have their #Predestroy invoked when the session-close event occurs. If the container never sends that event, then Spring won't be informed. I'm not sure if this constitutes a bug or not, and if so, whetyer it's a bug in Spring or Tomcat. The latter seems more likely, but I don't know if a Servlet container is obliged to do this.
If this is a show-stopper for you, you might want to consider having the scoped bean register itself with a "registrar" singleton during its #PostConstruct, and deregister itself on #PreDestroy. If the registrar is shutdown, it can propagate that event to any remaining session-scoped beans still registered with it.
Not ideal, but a pragmatic solution.
I would guess this is by design.
Tomcat, for instance, stores its sessions in SESSIONS.ser, by default for each deployed application. If you shut down Tomcat and restart it, it re-reads SESSIONS.ser. So, I would guess that as far as Spring is concerned, shutting down the container doesn't necessarily mean terminating the session - so that means #PreDestroy methods aren't called on session-scoped beans.

Resources