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?
Related
I have developed a Camel Rout using Spring. I used the STS4 IDE to develop the same.
When I started the application using Run As-> Spring Boot App, the application starts and also I can see from the logs that the route is started.
My route is a basic app, the exposes a rest endpoint and logs a Hello World
#Override
public void configure() throws Exception {
restConfiguration()
.enableCORS(true)
.apiContextPath("/api-doc")
.apiProperty("api.title", "Test REST API")
.apiProperty("api.version", "v1")
.apiContextRouteId("doc-api")
.component("servlet")
.bindingMode(RestBindingMode.json);
rest("/api/")
.id("api-route")
.consumes("application/json")
.get("/test")
.to("direct:log");
from("direct:log")
.id("log")
.log(LoggingLevel.INFO,"Test successful");
}
What I am expecting is that if I do localhost:8080/api/test, i will see some logs "Test Susccessful" in the Tomcat logs. I am using tomcat 9, Instead what I get WhiteLable error. I thought there is an issue with my code so I tried localhost:8080 and i was expecting the Tomcat Management server to open. Even that is not working.
I am not starting Tomcat separately. What I am doing is Run As-> Spring Boot App and I guess it is calling the embedded tomcat.
Credit for this answer goes to #Bedla. I had to add camel in the URL path http://localhost:8080/camel/api/test
How can we shutdown or stop a Spring application by itself programmatically?
We have currently using ConfigurableApplicationContext#close(), and yes, it does close the application but not shutdown or stop the application.
From the tomcat manager, the application status still is running, so the client still can connect to the web app and get the front-end resource. Besides, all servlets seems still working and can accept the request.
The below code shows how we do currently.
#Autowired
private ConfigurableApplicationContext configurableApplicationContext;
#Scheduled(cron="0 0/5 * * * ?")
private void validateLicenseExpired()
{
try
{
LicenseUtils.isLicensingPeriodValid();
} catch (LicenseInvalidException lie) {
logger.error("\t The licensing is expired", lie);
configurableApplicationContext.close();
}
}
Any help is appreciated.
If its a boot application the you can use following link to stop the application.
Programmatically shut down Spring Boot application
Otherwise you can programmatically call the shutdown.sh like below to stop the server.
String command = "<path-to-tomcat>shutdown.sh"; // for windows use bat
Process child = Runtime.getRuntime().exec(command);
[Edit]
This thread explains about stoping application like tomcat manager
Start / stop a web application from itself?
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);
}
}
i'm writing webapp that uses few workers. I decide to use JMS ActiveMQ to communication between them. My problem is that when i change workers configuration to java (when config was in xml i couldn't run jar) i have problem when message returns to webapp :
[org.springframework.jms.listener.DefaultMessageListenerContainer#4-3] WARN o.s.j.l.DefaultMessageListenerContainer - Execution of JMS message listener failed, and no ErrorHandler has been set.
java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
Earlier i had configuration like this (in xml):
<jms:listener-container>
<jms:listener destination="sample.reply" ref="sampleMessageListener" />
</jms:listener-container>
All worked correctly and then in my workers i change it to config like this:
#Bean
public static DefaultMessageListenerContainer configureListener() {
DefaultMessageListenerContainer container = new DefaultMessageListenerContainer();
container.setDestinationName("xxxxx.xxxxx");
container.setMessageListener(context.getBean("someBean"));
container
.setConnectionFactory(context.getBean(ConnectionFactory.class));
container.setConcurrency("5-10");
return container;
}
This also works but when i send message back to webapp i see problem with lost context.
I have an app that uses CXF as a SOAP client and Jersey to present REST services, with the Jersey classes managed by Spring. This works fine running in Tomcat; however when attempting to test with JerseyTest I get Spring conflicts; it appears JerseyTest doesn't shut-down the Spring context correctly.
The test initialisation for Jersey looks like:
public MailProviderTest()
throws Exception
{
super(new WebAppDescriptor.Builder("net.haltcondition.service.rest")
.contextPath("")
.contextParam("contextConfigLocation", "classpath:applicationContext.xml")
.servletClass(SpringServlet.class)
.contextListenerClass(ContextLoaderListener.class)
.build());
}
The CXF tests (which talk to our upstream provider's test servers) looks like:
#Before
public void setup()
{
// We need to do this the hard way to set the test endpoint rather than the default
JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
factory.setServiceClass(Soap.class);
factory.setAddress("https://webservice.test.provider.com/Service.asmx");
soap = (Soap) factory.create();
Map<String,Object> outProps= new HashMap<String,Object>();
outProps.put(WSHandlerConstants.ACTION, WSHandlerConstants.USERNAME_TOKEN);
outProps.put(WSHandlerConstants.USER, "TESTUSER");
outProps.put(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PW_TEXT);
outProps.put(WSHandlerConstants.PW_CALLBACK_REF, new WSAuthHandler("XXXX"));
Client cxfClient = ClientProxy.getClient(soap);
Endpoint cxfEndpoint = cxfClient.getEndpoint();
cxfEndpoint.getOutInterceptors().add(new WSS4JOutInterceptor(outProps));
}
When Maven runs the tests the Jersey class is run first; this results in the following error when running the CXF tests:
Caused by: java.lang.IllegalStateException: BeanFactory not initialized or already closed - call 'refresh' before accessing beans via the ApplicationContext
at org.springframework.context.support.AbstractRefreshableApplicationContext.getBeanFactory(AbstractRefreshableApplicationContext.java:153)
at org.springframework.context.support.AbstractApplicationContext.containsBean(AbstractApplicationContext.java:892)
at org.apache.cxf.configuration.spring.ConfigurerImpl.configureBean(ConfigurerImpl.java:143)
at org.apache.cxf.configuration.spring.ConfigurerImpl.configureBean(ConfigurerImpl.java:113)
at org.apache.cxf.transport.http.AbstractHTTPTransportFactory.configure(AbstractHTTPTransportFactory.java:228)
Unfortunately there doesn't seem to be any way to force the shutdown of the Spring application-context at the end of the Jersey tests and forcing per-test forks hasn't helped. It looks like I need to reset the Spring application-context as part of setting-up the CXF test, but I can't see how I would do this. Any pointers would be appreciated.