I am spring boot beginner and I have a spring boot project which contain a controller and this controller contains two methods as shown bellow:
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class Controller {
#GetMapping("/preliminate")
public void OpentextWebService(){
try {
OTService.threadOne();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
#GetMapping("/final")
public void OpentextWebService2(){
OTService.threadTwo();
}
}
the problem is that when I run : localhost:8080/preliminate and localhost:8080/final at the same time, only the first one will work. So is there is any way to run them at the same time?
I did some research and I found the #Async way but unfortunately I did not know how to impliment it in my code specially that I don't have a Service or Configuration Class.
The best way to implement this, is with #Async methods,
Create a Bean for you job Async.
#SpringBootApplication
#EnableAsync
public class ApplicationRun {
public static void main(String[] args) {
SpringApplication.run(ApplicationRun.class, args);
}
#Bean("threadPool")
public TaskExecutor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(20);
executor.setMaxPoolSize(1000);
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.setThreadNamePrefix("Async-");
return executor;
}
}
And then, create a service bean with a method like
#Async("threadPool")
public void asyncTask1(){
OTService.threadOne();
}
#Async("threadPool")
public void asyncTask2(){
OTService.threadTwo();
}
And finally, call this service methods on your controller. Thats maybe works for you!
My Spring Boot application is not a web server, but it's a server using custom protocol (using Camel in this case).
But Spring Boot immediately stops (gracefully) after started. How do I prevent this?
I'd like the app to stop if Ctrl+C or programmatically.
#CompileStatic
#Configuration
class CamelConfig {
#Bean
CamelContextFactoryBean camelContext() {
final camelContextFactory = new CamelContextFactoryBean()
camelContextFactory.id = 'camelContext'
camelContextFactory
}
}
I found the solution, using org.springframework.boot.CommandLineRunner + Thread.currentThread().join(), e.g.:
(note: code below is in Groovy, not Java)
package id.ac.itb.lumen.social
import org.slf4j.LoggerFactory
import org.springframework.boot.CommandLineRunner
import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication
#SpringBootApplication
class LumenSocialApplication implements CommandLineRunner {
private static final log = LoggerFactory.getLogger(LumenSocialApplication.class)
static void main(String[] args) {
SpringApplication.run LumenSocialApplication, args
}
#Override
void run(String... args) throws Exception {
log.info('Joining thread, you can press Ctrl+C to shutdown application')
Thread.currentThread().join()
}
}
As of Apache Camel 2.17 there is a cleaner answer. To quote http://camel.apache.org/spring-boot.html:
To keep the main thread blocked so that Camel stays up, either include the spring-boot-starter-web dependency, or add camel.springboot.main-run-controller=true to your application.properties or application.yml file.
You will want the following dependency too:
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-spring-boot-starter</artifactId>
<version>2.17.0</version>
</dependency>
Clearly replace <version>2.17.0</version> or use the camel BOM to import dependency-management information for consistency.
An example implementation using a CountDownLatch:
#Bean
public CountDownLatch closeLatch() {
return new CountDownLatch(1);
}
public static void main(String... args) throws InterruptedException {
ApplicationContext ctx = SpringApplication.run(MyApp.class, args);
final CountDownLatch closeLatch = ctx.getBean(CountDownLatch.class);
Runtime.getRuntime().addShutdownHook(new Thread() {
#Override
public void run() {
closeLatch.countDown();
}
});
closeLatch.await();
}
Now to stop your application, you can look up the process ID and issue a kill command from the console:
kill <PID>
Spring Boot leaves the task of running the application to the protocol around which the application is implemented. See, for example, this guide:
Also required are some housekeeping objects like a CountDownLatch to keep the main thread alive...
So the way of running a Camel service, for example, would to be to run Camel as a standalone application from your main Spring Boot application class.
This is now made even simpler.
Just add camel.springboot.main-run-controller=true to your application.properties
All threads are completed, the program will close automatically.
So, register an empty task with #Scheduled will create a loop thread to prevent shutdown.
file application.yml
spring:
main:
web-application-type: none
file DemoApplication.java
#SpringBootApplication
#EnableScheduling
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
file KeepAlive.java
#Component
public class KeepAlive {
private static final Logger log = LoggerFactory.getLogger(ScheduledTasks.class);
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
#Scheduled(fixedRate = 1 * 1000 * 60) // 1 minute
public void reportCurrentTime() {
log.info("Keepalive at time {}", dateFormat.format(new Date()));
}
}
My project is NON WEB Spirng Boot.
My elegant solution is create a daemon thread by CommandLineRunner.
Then, Application do not shutdown immediately.
#Bean
public CommandLineRunner deQueue() {
return args -> {
Thread daemonThread;
consumer.connect(3);
daemonThread = new Thread(() -> {
try {
consumer.work();
} catch (InterruptedException e) {
logger.info("daemon thread is interrupted", e);
}
});
daemonThread.setDaemon(true);
daemonThread.start();
};
}
To keep the java process alive when not deploying a web application set the webEnvironment property to false like so:
SpringApplication sa = new SpringApplication();
sa.setWebEnvironment(false); //important
ApplicationContext ctx = sa.run(ApplicationMain.class, args);
for springboot app to run continously it has to be run in a container, otherwise it is just like any java app all threads are done it finishes,
you can add
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
and it will turn it into webapp, if not you are responsible keeping it alive in your implementation
Currently running SpringBoot applications in a containerised environment (ECS) and I've observed scenarios in which the container gets terminated during start-up and while it's still holding the Liquibase changelock.
This leads to issues in all containers that are spun afterwards and ends up requiring manual intervention.
Is it possible to ensure that if the process receives a SIGTERM, it will gracefully handle termination and release the lock?
I've already ensured that the container is receiving the signals by enabling via InitProcessEnabled (in the CloudFormation template) and use of "exec java ..." as a java agent we use does gracefully shutdown on this circumstances.
Heyo,
As mentioned in the GitHub issue I have a workaround. A solution is yet to be implemented.
You can manually register a shutdown hook before running spring boot.. That hook should assure that the Termination is postponed until liquibase is done.
package dang;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
#EnableJpaRepositories
#SpringBootApplication
public class DangApplication {
public static void main(String[] args) throws InterruptedException {
Thread thread = new GracefulShutdownHook();
Runtime.getRuntime().addShutdownHook(thread);
new SpringApplicationBuilder(DangApplication.class)
.registerShutdownHook(true)
.logStartupInfo(true)
.build()
.run();
Runtime.getRuntime().removeShutdownHook(thread);
}
}
And the hook:
package dang;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import java.util.Map;
#Slf4j
public class GracefulShutdownHook extends Thread {
#SneakyThrows
#Override
public void run() {
super.run();
log.info("Shutdown Signal received.. Searching for Liquibase instances!");
boolean liquibaseIsRunning = true;
while (liquibaseIsRunning) {
Map<Thread,StackTraceElement[]> stackTraces = Thread.getAllStackTraces();
for(Map.Entry<Thread, StackTraceElement[]> entry : stackTraces.entrySet()) {
StackTraceElement[] stackTraceElements = entry.getValue();
for (StackTraceElement stackTraceElement : stackTraceElements) {
if (stackTraceElement.getClassName().contains("liquibase") && stackTraceElement.getMethodName().contains("update")) {
try {
log.warn("Liquibase is currently updating");
entry.getKey().join();
liquibaseIsRunning = false;
} catch (InterruptedException e) {
log.error("Shutdown Hook was interrupted.. Fatal databaselock may be imminent", e);
if (Thread.interrupted()) {
throw e;
}
}
}
}
}
}
}
}
EDIT
After implementing my workaround a contributor of liquibase shared a different solution (It's actually the same solution just through Spring functionality) which is much better than what I did:
package dang;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
#EnableJpaRepositories
#SpringBootApplication
public class DangApplication {
public static void main(String[] args) throws InterruptedException {
new SpringApplicationBuilder(DangApplication.class)
.initializers(ConfigurableApplicationContext::registerShutdownHook) // Registers application hook before liquibase executes.
.logStartupInfo(true)
.build()
.run();
}
}
I am trying to hook into the creation of the context using a custom application listener like this
#Component
public class ContextStartedListener implements ApplicationListener<ContextStartedEvent> {
#Override
public void onApplicationEvent(ContextStartedEvent event) {
System.out.println("Context started"); // this never happens
}
}
But the onApplicationEvent method never fires. If I use a different event such as ContextRefreshedEvent then it works just fine, but I need to hook into before it is created. Any advice? Thanks!
[Edit]
Editing answer adding more info because of the downvote.
The reason why you are not getting a callback by the listener is because you are not explicitly calling the LifeCycle start() method (JavaDoc).
This cascades down to your ApplicationContext normally via the AbstractApplicationContext on in Spring Boot case via the ConfigurableApplicationContext.
Example of working code below demonstrating how your callback would work (just explicitly call the start() method)
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.event.ContextStartedEvent;
import org.springframework.stereotype.Component;
#SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
ConfigurableApplicationContext applicationContext = SpringApplication.run(DemoApplication.class, args);
applicationContext.start();
}
#Component
class ContextStartedListener implements ApplicationListener<ContextStartedEvent> {
#Override
public void onApplicationEvent(ContextStartedEvent event) {
System.out.println("Context started");
}
}
}
The reason why I suggested below the ContextRefreshedEvent callback instead is because behind the scenes the refresh() code is getting invoked.
If you drill down the SpringApplication#run() method you'll eventually see it.
Again here's a working example of how this would work using the ContextRefreshedEvent:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;
#SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
#Component
class ContextStartedListener implements ApplicationListener<ContextRefreshedEvent> {
#Override
public void onApplicationEvent(ContextRefreshedEvent event) {
System.out.println("Context refreshed");
}
}
}
[Before Edit]
Change the Generic type to ContextRefreshedEvent instead and then it should work.
For more details read this article from the Spring Blog. Just to quote the part about the ContextRefreshedEvent:
[..]This allows MyListener to be notified when the context has refreshed
and one can use that to run arbitrary code when the application
context has fully started.[..]
I'm trying to initialize some beans during application start that will be reading from static shared in-memory structures. I was previously using #PostContruct but would like to shift to a more event-based initialization so that I can make use of Spring AOP features (Config, Resources, etc.) and avoid repeating myself.
All data beans implement this interface:
public interface DataInterface {
public void loadData();
public List<String> getResults(String input);
}
I have tried implementing both ServletContextListener and WebApplicationInitializer interfaces, but neither seem to get called.
#Service
public class AppInit implements WebApplicationInitializer {
#Autowired
DataInterface[] dataInterfaces;
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
// This does not get called
for (DataInterface interface : dataInterfaces)
interface.loadData();
}
}
#WebListener
public class AppContextListener implements ServletContextListener {
#Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
// does not get called
}
#Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
// does not get called
}
}
I could also try to initialize these classes at the end of the main() function that returns after I start the SpringApplication.
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
// Can I initialize the DataInterfaces here???
}
}
Seems like there should be a better way.
Edit:
I ended up using the following solution since I wasn't able to receive any of the Context* events listed in the Spring docs.
#Component
public class DalInitializer implements ApplicationListener {
#Autowired
DataInterface[] dataInterfaces;
#Override
public void onApplicationEvent(ApplicationEvent applicationEvent) {
if (applicationEvent.getClass() == ApplicationReadyEvent.class) {
for (DataInterface interface : dataInterfaces)
interface.loadData();
}
}
}
Use spring application event listeners, see Better application events in Spring Framework