I have already implemented a kinesis stream consumer which will run forever and I want to integrate that into spring framework for monitoring and graceful shutdown. But I found I wasn't able to stop the consumer by the http shutdown request. More specifically, only the spring web app is stopped but not the consumer. Here's what I did:
I created a main class for spring as follows:
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication application = new SpringApplication(Application.class);
application.addListeners(new ApplicationPidFileWriter("./app.pid"));
application.run(args);
System.out.println("I'm Here!!");
}
}
And in the entrance of consumer class, I added #EventListener(ApplicationReadyEvent.class) to the startConsumer method
#EventListener(ApplicationReadyEvent.class)
public static void startConsumer() throws Exception {
init();
...
int exitCode = 0;
try {
worker.run(); // will run forever
} catch (Throwable t) {
System.err.println("Caught throwable while processing data.");
t.printStackTrace();
exitCode = 1;
}
System.exit(exitCode);
}
The consumer successfully started after mvn package && java -jar myJar, but when I use use the http shutdown to stop the program, only the spring app stops. The consumer was still running.
Any idea on how to stop the consumer? Or more generally how to integrate a long running process into spring framework? I've tried non-web choice, but that prevents me from using http requests to do monitoring.
Any suggestion will be appreciated!!!
One important thing is that it is not correct to block execution in EventListener. You should start a thread from the event listener method and that thread will do processing for you. So you need to invoke Worker.run in a separate thread. Alternatively you can mark your event listener as #Async.
Now the problem is to stop it correctly when spring boot application is stopped.
In order to be able to react to shutdown events in spring you can implement SmartLifecycle in your bean.
When stop is invoked you need to stop the Worker. This answer has some good options how to do that.
Make sure you invoke the Runnable passed to stop when worker shutdown is complete. For more details see SmartLifecycle.stop javadoc.
Related
This question has been asked a few times but I have not found a post that describes my situation exactly. I have a JavaFX/Spring boot based application that is required to perform some cleanup tasks before shutting down. I can intercept the event for the X button being pressed like so :
primaryStage.setOnCloseRequest(event ->
{
shutdown(event);
});
private void shutdown(WindowEvent event)
{
if (event != null)
{
event.consume();
}
try
{
shutdownProcessHub();
Platform.exit();
}
catch (Exception ex)
{
logEntryService.logError(LogEntrySource.SERVICE, LogEntryType.CORE, "Error stopping process hub : "
+ ex.getMessage(), logger);
}
}
I have a shutdown button which calls the same method but with a null argument. Both these methods of shutting down my application result in the shutdownProcessHub() method being called which gracefully stops a bunch of threads and performs writes to the database.
The issue is that this application can also be run with no GUI. In this deployment mode, i use NSSM to create a windows service that points to a batch file which starts the app. Stopping said service results in a CTRL-C call to the app which completely bypasses my shutdown method. I have used the following code to register a shutdown hook :
Runtime.getRuntime().addShutdownHook(new Thread(() -> shutdown(null)));
Said shutdown hook clearly runs after any form of Spring bean has been long destroyed since i'm getting the following exception when sending CTRL-C to the CMD window running the JAR :
Exception in thread "Thread-5" org.springframework.transaction.CannotCreateTransactionException: Could not open JPA EntityManager for transaction; nested exception is java.lang.IllegalStateException: EntityManagerFactory is closed
What do i need to do in order to have the shutdown hook still be able to access the entity manager? I understand this might be too late in the Spring / JVM lifespan for any of these to still be accessible, what would be the alternative for a CTRL-C call to be correctly intercepted?
looks like the SmartLifeCycle interface's stop() method is what i needed. It is called when doing CTRL-C in the command prompt running the JAR and still has access to all of Spring's resources including JPA's entity manager. The only issue is that Log4J2 seems to not be available while this method executes but that is only a minor annoyance.
Cheers
My Spring Boot application is quite small and has one job: Act as a client by opening a websocket connection to a third-party website and listen for messages.
The problem is that after my javax.websocket.Endpoint implementation has been initialised and the connection has been created, my Spring boot application closes.
I would have thought that any open websocket connection would keep my application up and running?
I don't need an embedded servlet container so I have specifically set web-environment: false in application.yaml.
Is there a way to remedy this without adding a servlet container I will never use?
I fixed this by using OkHttpClient and initialising it with a #PostConstruct in my #Configuration. I annotated the listener with #Component and it now stays alive without needing the embedded servlet container.
#PostConstruct
void handleRequest() {
Runnable runnable = () -> {
while (true) {
try {
JSONObject requestBody = getRequestBodyFromInput(serverSocket.accept());
requestHandler.handleRequest(requestBody);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
};
new Thread(runnable).start();
}
I started a new thread on while socket would keep listening and I am not letting that new thread die by looping it over within while loop.
You simply could simply loop forever.
while (true) {}
I'm looking to restart the spring boot app, so using Spring Actuator /restart endpoint is working using curl, but i'm looking to call the same function using java code from inside the app, i've tried this code, but it's not working:
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
RestartEndpoint p = new RestartEndpoint();
p.invoke();
}
});
thread.setDaemon(false);
thread.start();
You need to inject the RestartEndpoint:
#Autowired
private RestartEndpoint restartEndpoint;
...
Thread restartThread = new Thread(() -> restartEndpoint.restart());
restartThread.setDaemon(false);
restartThread.start();
It works, even though it will throw an exception to inform you that this may lead to memory leaks:
The web application [xyx] appears to have started a thread named
[Thread-6] but has failed to stop it. This is very likely to create a
memory leak. Stack trace of thread:
Note to future reader of this question/answer, RestartEndpoint is NOT included in spring-boot-actuator, you need to add spring-cloud-context dependency.
Get the json here
#Autowired
private HealthEndpoint healthEndpoint;
public Health getAlive() {
return healthEndpoint.health();
}
Add custom logic
Is it necessary to shutdown an ExecutorService at some point if it runs in a Tomcat container for a Servlet. If yes, then where should I call the shutdown? I tried adding it after the submit() call but when I fire another request to the Servlet from a client browser, I get a RejectedExecutionException which is probably because I did a shutdown? I am trying to understand how it works in a Servlet within Tomcat and how I should use it.
I am doing the following in my webApplication (which seems to work fine without any shutdown):
// In configuration class
#Bean (name = "executorService")
public ExecutorService executorService() {
return Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() + 1);
}
// In some other class
#Qualifier("executorService")
#Autowired
private ExecutorService executorService;
....
private void load() {
executorService.submit(new Runnable() {
#Override
public void run() {
doSomethingInTheBackground();
}
});
// If I enable this I will get a RejectedExecutionException
// for a next request.
// executorService.shutdown();
}
The idea behind the ExecutorService is to reuse threads. Creating threads is expensive and usually it is more efficient to create a thread once and then use that same thread multiple times. This is exactly what an ExecutorService does: it manages a pool of (possibly idle) threads and assigns work to them when you call its submit methods.
In a typical application you therefore do not want to shutdown the ExecutorService. You should however shut the ExecutorService down properly if your application is terminated. Since you are using Spring you don't have to worry about that:
By default, beans defined using Java config that have a public close or shutdown method are automatically enlisted with a destruction callback. [See the documentation.]
That means, if you close the ApplicationContext, Spring will automatically shutdown the ExecutorService for you.
I have a simple API that my clients use in a standalone application. Behind the scenes my API uses Ehcache for performance.
Everything works fine except that my client needs to invoke a shutdown() method in my API to invoke CacheManager.shutdown() without which Ehcache continues to run in the background even though the main thread is completed.
Is there a way I can avoid this extra shutdown() call for my client?
I tried using #PreDestroy Spring annotation to invoke this call, but it didn't work?
Here I am adding a sample client program.
public class ClientApp{
#Autowired
private IClientService service;
public static void main(String[] args){
try{
service.getClients();
...
} finally {
service.shutdown(); // to shutdown the cache background thread
}
}
}
In ClientServiceImpl.java, I have the following lines
public void shutdown(){
LOGGER.info("Shutting the cache down...");
ehcacheManager.shutdown();
}
Your example confirms the standalone application setup.
Ehcache should not prevent the JVM from shutting down when the main thread terminates.
If it does, you will need to add thread dumps to this issue so we can analyse further the issue and its cause.
Adding the following line does what I was looking for.
System.setProperty(CacheManager.ENABLE_SHUTDOWN_HOOK_PROPERTY, "true");