There is two way to close the Spring ApplicationContext:
Solution 1:
ApplicationContext context = new ClassPathXmlApplicationContext(
"spring/application-context.xml");
// Application core ...
((AbstractApplicationContext) context).close();
Solution 2:
ApplicationContext context = new ClassPathXmlApplicationContext(
"spring/application-context.xml");
((AbstractApplicationContext) context).registerShutdownHook();
// Application core ...
What's the difference between this 2 solutions and what's the best in terms of performance ?
Solution 1 shuts down the application context
Solution 2 registers a callback, so spring will shut down, if the JVM is shut down, from javadoc :
Register a shutdown hook with the JVM runtime, closing this context on JVM shutdown unless it has already been closed at that time.
So both are two diffrent things, normally you will call registerShutdownHook() directly after you created the appication context. So when your user terminates the JVM, spring will be called and shuts itself down.
You should call close() when your application ends, to allow spring to destroy its beans.
Related
I have a springboot application with embedded tomcat. And on certain cases it should be restarted from code.
I have read several articles and SO posts regarding this,but yet to find a clean solution.
I am aware that 'context.close' , 'SpringApplication.exit(context)' exist and can be wrapped into something like this:
public static void restart() {
ApplicationArguments args = context.getBean(ApplicationArguments.class);
Thread thread = new Thread(() -> {
context.close();
context = SpringApplication.run(Application.class, args.getSourceArgs());
});
thread.setDaemon(false);
thread.start();
}
source: https://www.baeldung.com/java-restart-spring-boot-app
The problem is that using context.close() just doesn't work in a clean way. The context itself will be restarted though, but bunch of Threads will be left in the background (like Thread[pool-3-thread-1,5,main] Thread[Signal Dispatcher,9,system] Thread[OkHttp TaskRunner,5,main] ..etc).
And for every context restart these will be recreated, so the number of threads adds up gradually by each restart. Resulting in huge Thread mess as time passes.
Note1: A simple application exit by using 'context.close()' also wouldn't work because of these left over Threads. So the context close doesnt even close the application.
Note2: If I use System.exit(SpringApplication.exit(context)) I can kill the app gracefully, but can't restart it.
Note3: I don't want to use neither devtools nor actuator
So the question is how to perform a total restart for a springboot application?
You can use the RestartEndPoint in spring-cloud-context dependency to restart the Spring Boot application programmatically:
#Autowired
private RestartEndpoint restartEndpoint;
...
Thread restartThread = new Thread(() -> restartEndpoint.restart());
restartThread.setDaemon(false);
restartThread.start();
It appears that Spring Cloud Task lifecycle is incorrectly managed when spring boot application has hierarchical application contexts.
When i add #EnableTask annotation to the parent ApplicationContext, it registers the task, but records execution time from the parent context, failing to record accurate execution time and exit code (always success as parent context closes successfuly).
On the other hand, if i add annotation to the child context (which actually runs CommandlineRunner), it fails to start the task at all with below exception:
o.s.c.t.listener.TaskLifecycleListener : [] [] An event to end a task has been received for a task that has not yet started.
s.c.a.AnnotationConfigApplicationContext : [] [] Exception encountered during context initialization - cancelling refresh attempt: org.springframework.context.ApplicationContextException: Failed to start bean 'taskLifecycleListener'; nested exception is java.lang.IllegalArgumentException: Invalid TaskExecution, ID 132515 not found
Looking at the TaskLifecycleListener source, it appears that it reacts to ApplicationEvents from parent context and catches ApplicationReadyEvent from parent context before the task is started.
Spring Boot 2.2.6 / Spring Cloud Task 2.2.2
Any thoughts?
This is by design. The "task" is registered as a single execution of a Boot application, not the execution of an ApplicationContext. In a microservices world, you would want to break up your tasks into independent artifacts, therefore running them as independent Spring Boot applications. There is no support for Spring Cloud Task to support multiple "task" executions within a single Spring Boot application as that goes against what its intent is. If you feel this is something that should be added, feel free to open up an issue on Github where we can explore your use case deeper.
Actually, I realized what happens is that Spring auto configures task in my main parent context which closes the task. And same appears to be happening in my child context. Since executionId is passed as parameter, it is already updated by the parent as executed, so it fails in my child context.
Solution I found to this was to exclude autoconfiguration of SimpleTaskAutoConfiguration from my parent context.
#SpringBootApplication(exclude = {SimpleTaskAutoConfiguration.class})
Instead I manually import configuration class in my child context which executes the task:
#EnableTask
#Import(SimpleTaskAutoConfiguration.class)
That now tracks actual time it took to execute the command line runner as a task within child context, although it doesn't account for time of running whole app, at least it reflects more accurate picture.
Recently we ported our application from the web application running in tomcat to spring boot application with embedded tomcat.
After running the app for several days, memory and cpu usage have reached 100%.
In heap dump analysis it comes out that there was a bunch of http session objects which where not destroyed.
I can see in the debug that sessions created with configured timeout value, lets say, 5 minutes. But after this time the invalidation is not triggered. It invoked only if I do request again after the timeout period.
I have compared this behavior with app running in tomcat and I can see that session invalidation is triggered by ContainerBackgroungProcessor thread [StandardManager(ManagerBase).processExpires()]
I do not see this background thread in spring boot application.
What was done following some suggestions found:
session timeout set in application.properties:
server.session.timout=300
or in EmbeddedServletContainerCustomizer #Bean:
factory.setSessionTimout(5, TimeUnit.MINUTES)
Added HttpSessionEventPublisher and SessionRegistry beans
Nothing helps, sessions just not invalidated at the expiration time.
Some clue about this?
After some more debugging and documentation reading this is the reason and solution:
In tomcat, there is a thread spawned on behalf of the root container which scans periodically container and its child containers session pools and invalidates them. Each container/child container may be configured to have its own background processor to do the job or to rely on its host's background processor.
This controlled by context.backgroundProcessorDelay
Apache Tomcat 8 Configuration Reference
backgroundProcessorDelay -
This value represents the delay in seconds between the invocation of the backgroundProcess method on this engine and its child containers, including all hosts and contexts. Child containers will not be invoked if their delay value is not negative (which would mean they are using their own processing thread). Setting this to a positive value will cause a thread to be spawn. After waiting the specified amount of time, the thread will invoke the backgroundProcess method on this engine and all its child containers. If not specified, the default value for this attribute is 10, which represent a 10 seconds delay.
In spring boot application with embedded tomcat
there is TomcatEmbeddedServletContainerFactory.configureEngine() which sets this property -1 for the StandardEngine[Tomcat], which is the root container in tomcat hierarchy, as I understand.
All the child containers including web app also have this parameter set to -1.
And this means they all rely on someone else to do the job.
Spring do not do it, no-one do it.
The solution for me was to set this parameter for the app context:
#Bean
public EmbeddedServletContainerCustomizer servletContainerCustomizer() {
return new EmbeddedServletContainerCustomizer() {
#Override
public void customize(ConfigurableEmbeddedServletContainer container) {
if (container instanceof TomcatEmbeddedServletContainerFactory) {
TomcatEmbeddedServletContainerFactory factory = (TomcatEmbeddedServletContainerFactory) container;
TomcatContextCustomizer contextCustomizer = new TomcatContextCustomizer() {
#Override
public void customize(Context context) {
context.setBackgroundProcessorDelay(10);
}
};
List<TomcatContextCustomizer> contextCustomizers = new ArrayList<TomcatContextCustomizer>();
contextCustomizers.add(contextCustomizer);
factory.setTomcatContextCustomizers(contextCustomizers);
customizeTomcat(factory);
}
}
In a Spring-Boot Application I register an Apache-CXF SOAP Service like this:
#Bean(destroyMethod = "destroy")
org.apache.cxf.endpoint.Server server(MyService myService) {
JaxWsServerFactoryBean svrFactory = new JaxWsServerFactoryBean();
svrFactory.setServiceClass(MyService.class);
svrFactory.setAddress("http://0.0.0.0:4711/MyService");
svrFactory.setServiceBean(myService);
svrFactory.getInInterceptors().add(new LoggingInInterceptor());
svrFactory.getOutInterceptors().add(new LoggingOutInterceptor());
return svrFactory.create();
}
It may happen at other places of my spring configuration, that the context can not be initialized succesfully (Application startup failed) and the application shutdown is initiated.
Unfortunately the sever bean, which is already created, stays alive and prevents the application from shutting down completely. I thought that destroyMethod = "destroy" would do the trick, but that only destroys the webapp/SOAP endpoint (resultig in HTTP ERROR 404) but the embedded jetty is still running.
Do I have any chance to configure the Spring Context in a way that prevents the embedded Jetty from staying alive when the Spring Context initialization failes at some point?
We have a web application that uses Spring (3.0.5) and CXF (currently 2.4.2 for various reasons but upgrading is an option if that makes any difference) and is deployed on Tomcat.
The application is initialized using the org.springframework.web.context.ContextLoaderListener.
Starting and shutting the application down works like a charm but if I try to refresh the Spring application context, using
((ConfigurableApplicationContext)applicationContext).refresh();
I run into problems. The application context first destroys all its beans (including CXFBusImpl, or rather its subclass SpringBus). SpringBus however calls close() on its application context - leading to a NullPointerException when the application context shortly after tries to close its bean factory:
java.lang.NullPointerException
at org.springframework.context.support.AbstractRefreshableApplicationContext.closeBeanFactory(AbstractRefreshableApplicationContext.java:152)
at org.springframework.context.support.AbstractRefreshableApplicationContext.refreshBeanFactory(AbstractRefreshableApplicationContext.java:124)
at org.springframework.context.support.AbstractApplicationContext.obtainFreshBeanFactory(AbstractApplicationContext.java:467)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:397)
Is there anything I can do to avoid this (other than modifying CXF)? If I skip CXF everything works.
I don't think you can tell CXF not to work that way. What you could do though is to isolate the parts of your application that need restarting into their own context that you build and and tear down as you choose without involving the main context over much. Perhaps you'd do that with a ClassPathXmlApplicationContext, though there are a few choices. I think you'll be setting the outer context as the parent of the inner, and referring to outer beans with XML-config syntax like:
<ref parent="foo" />
You'll then need to create some way of proxying the activity with CXF in the outer context to the beans in the inner context. This is the tricky part, as it is usually considered bad form for references to go that way round. You'll probably have to have some kind of registry/proxy in the outer context that (relevant) inner beans connect to as part of their creation/init process (and deregister from at tear-down). You'll also have to decide how to handle the case where a request needs to be served when there is no inner context. Tricky, especially if you want to do it elegantly...