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

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.

Related

What is difference between ContextRefreshedEvent, ContextStartedEvent, ContextStoppedEvent and ContextClosedEvent

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.

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 #PreDestroy method

I found out that #PreDestroy only work with singleton scoped bean. I was thinking what could go wrong if we use it with prototype scoped bean. Anything at all??? I dont think so. I think this is just not implemented in spring as they would have to keep the references to all the beans created. Tell me if i am wrong
Spring can only initialize/destroy beans it also controllers and basically prototype scoped beans aren't under the control of spring (after construction). It doesn't know when it is cleaned up, destroyed or what so ever. As such the #PreDestroy method isn't callable for prototype beans (as they do not have a clearly defined lifecycle like singletons or request scoped beans).
For "prototype" scoped beans, Spring does not call the #PreDestroy method.
Here is the answer from the Spring reference manual. Section 7.5.2
http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#beans-factory-scopes-prototype
In contrast to the other scopes, Spring does not manage the complete lifecycle of a
prototype bean: the container instantiates, configures, and otherwise assembles a
prototype object, and hands it to the client, with no further record of that prototype
instance.
Thus, although initialization lifecycle callback methods are called on all objects regardless of scope, in the case of prototypes, configured destruction lifecycle callbacks are not called. The client code must clean up prototype-scoped objects and release expensive resources that the prototype bean(s) are holding.
To get the Spring container to release resources held by prototype-scoped beans, try using a custom bean post-processor, which holds a reference to beans that need to be cleaned up.
The #PreDestroy annotation does not belong to Spring, it’s located in the jsr250-api library jar under javax.annotation package.
By default, Spring will not aware of the #PreDestroy annotation. To enable it, you have to either register CommonAnnotationBeanPostProcessor or specify the <context:annotation-config /> in bean XML configuration file.

Jersey 2.3 not invoking HK2 service locator shutdown when servlet container is shut down

Question: Is there a proper way to ensure that HK2 shuts down with Jersey 2; if not, would this be considered a bug?
Using jersey-server 2.3.1, I've noticed that HK2 #PreDestroy methods on managed singletons and dispose() methods on Factory<T> implementations are not being invoked. Doing a bit of poking around, I see that ConfigHelper defines a lifecycle listener that invokes preDestroy() on the service locator, but nothing more. This gets invoked from the the Jersey ServletContainer on both shutdown() and reload(), but nothing further is done with the service locator in terms of shutdown.
I've looked through ServletContainer, but there doesn't seem to be a way of accessing the HK2 service locator through any public or protected APIs.
I had the same problem. For me solution is to invoke serviceLocator.shutdown() (I don't know why this method is not called automatically when service is shutdown). After call this method all dispose() methods will be called. Question where is a good place to invoke serviceLocator.shutdown()? Whell I have implementetion of ApplicationEventListener so service can listen for an event DESTROY_FINISHED, RELOAD_FINISHED. When service catch this event then serviceLocator.shutdown() is called. If you figure out better solution, please let me know.
I ended up injecting ServiceLocator into my Application instance (assuming this is topmost ServiceLocator for my app) and calling locator.shutdown() in MyApplication.preDestroy() (its marked with #PreDestroy and called from jersey's ContainerLifecycleListener). Worked ok for me. Not sure however, that this is recommended way of doing it.
This issue was resolved in jersey 2.7 (https://java.net/jira/browse/JERSEY-2299) and jersey 2.11 (https://java.net/jira/browse/JERSEY-2549). Updating your jersey dependency should fix things for you.

Can we undeploy a Spring container managed bean which is no more needed

I a get a get a bean from Spring container say
MyClass obj1 = Context.getBean("obj1");
After using obj1, I am sure that it will not be needed in rest of my application.
Then is there any way to ask Spring container to destroy the bean.
Atleast giving hint to Spring container that it is no more needed and spring my decide whether to destroy it or not (Similar to garbage collection)?
Make "obj1" a prototype-scoped bean. Then Spring will create a new instance of it each time you ask for it (make sure you are ok with this), and then it will not manage the instance any further, so when you are done with it and release all your references it can be garbage collected.
Prototype scope is like new, only giving you Spring-configured beans.
I don't think it will be possible for you to tell Spring container to destroy that bean. If you notice most of these beans created by Spring framework are Singleton that are supposed to give you same instance of the bean every time you get it injected into your code. A singleton by nature is supposed to live through the life of the application hence it cannot be destroyed.

Resources