Spring Boot multiple CommandLineRunner execution order and state - java

I have some startup operations to be executed one after another. Both of these runners performs some heavy database operations.
#Slf4j
#Data
#Component
#Order(1)
public class MyFailureHandlerA implements CommandLineRunner {
#Override
public void run(String... args) throws Exception {
some_io_calls_a();
}
}
#Slf4j
#Data
#Component
#Order(2)
public class MyFailureHandlerB implements CommandLineRunner {
#Override
public void run(String... args) throws Exception {
some_io_calls_b();
}
}
I need to make sure that #Order(2) runner starts only after #Order(1) has finished it's processing.
Is there any way to achieve this, like registering the runners onto some strategies so that they can be monitored for their completion.
I found a way, like, I can go for waiting till the value of a singleton scoped bean variable turns true, which is set only when first runner finishes it's task. But I'm afraid if this is recommended or not.
Or, can I rely on #DependsOn for the task completion? Is is guaranteed that, by using #DependsOn("MyFailureHandlerA") over MyFailureHandlerB will guaranteee that 1st runner has completed it's entire operations?
Another way I was trying is, invoking the services call one after another from one single runner, like this:
#Slf4j
#Data
#Component
public class MyFailureHandler implements CommandLineRunner {
#Override
public void run(String... args) throws Exception {
some_io_calls_q();
some_io_calls_b();
}
}
Would this makes some sense and will ensure that ops are executed in order one after another?
spring-boot-2.3.2

some_io_calls_q();
some_io_calls_b();
If both your methods do synchronized tasks, then using order is enough.
Your command will be executed following the order annotation.
You can print few logs to check about it.
Otherwise, it's not enough with async task. You have to control when/how the task is completed...
The most simple solutions as you describe is calling both method in one commandlinerunner in case you are not sure the internal process of spring.
#DependsOn: only working with process creating bean. It's not relative the execution of commands.

Related

How to save jpa entities on application shutdown?

I have a service which handles CRUD operations for entities using H2 database. Previously during testing phase I used to have a csv file which "acted" as a database and upon application shutdown I saved all in-memory changes to entities to this csv file. Now I want to do the same, but for some reason I am not hitting any breakpoints in my onExit() method and therefore not saving data on application shutdown. I am still working out proper logic for selecting which entities to save, but for now I just want to be able to automatically save when I press Stop 'Application' button in Intellij.
#Component
public class MangaDataProvider {
private static MangaService mangaService;
#Autowired
public MangaDataProvider(MangaService mangaService) {
MangaDataProvider.mangaService = mangaService;
}
#PreDestroy
public static void onExit() {
mangaService.saveAll();
}
}
And then I have a onExit() method in Application:
#SpringBootApplication
public class Application extends SpringBootServletInitializer implements AppShellConfigurator {
public static void main(String[] args) {
LaunchUtil.launchBrowserInDevelopmentMode(SpringApplication.run(Application.class, args));
}
#PreDestroy
public void onExit() {
MangaDataProvider.onExit();
}
}
There is no guarantee that the shutdown hooks will be executed. They have a very limited time to execute. That means you code may be partially executed. You can try to add a Print statement, or a debug breakpoint. You will observe that the first statement is executed because it's super fast, but there is no time left to execute the rest of the method.
I good practice would be to call a service or an endpoint which "prepares" (in your case, it will persist your data) your application for shutdown. You call this service/endpoint then, when execution completed, you can safely terminate the application.
Many server application have a "prepare for shutdown" endpoint. Per example Jenkins ;)

Spring issue with #PostConstruct and #PreDestroy

I have a Spring application that I am trying to test with EmbededRedis. So I created a component like below to Initialize and kill redis after test.
#Component
public class EmbededRedis {
#Value("${spring.redis.port}")
private int redisPort;
private RedisServer redisServer;
#PostConstruct
public void startRedis() throws IOException {
redisServer = new RedisServer(redisPort);
redisServer.start();
}
#PreDestroy
public void stopRedis() {
redisServer.stop();
}
}
But now I am facing a weird issue. Because spring caches the context, PreDestroy doesnt get called everytime after my test is executed, but for some reason, #PostConstruct gets called, and EmbededRedis tries to start the running redis server again and again, which is creatimg issues in the execution.
Is there a way to handle this situation by any mean?
Update
This is how I am primarily defining my tests.
#SpringBootTest(classes = {SpringApplication.class})
#ActiveProfiles("test")
public class RedisApplicationTest {
Ditch the class and write an #Configuration class which exposed RedisServer as a bean.
#Configuration
public void EmbeddedRedisConfiguration {
#Bean(initMethod="start", destroyMethod="stop")
public RedisServer embeddedRedisServer(#Value("${spring.redis.port}") int port) {
return new RedisServer(port);
}
}
So I debuged the ContextInitialization as suggested by #M. Deinum.
For me, the porblem was, Our application was mocking different classes in order to mix mocking with Spring context.
Now, when you use mocks, MockitoContextInitializer also becomes part of your cache key, which results in cache miss. Reason is, The classes under mock are obviously different for different test classes.
Looking at the situation, I preferred to go ahead with #DirtiesContext to invalidate the contest after the test is done, so that I can reinitialize the context later on for different test.
Note #DirtiesContext is in a way recommended to be avoided as it slows down your tests.

When and why do we need ApplicationRunner and Runner interface?

I'm learning Spring boot. What's some typical use cases for ApplicationRunner or any runner interface?
import org.junit.jupiter.api.Test;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.test.context.SpringBootTest;
#SpringBootTest
class PersistencedemoApplicationTests implements ApplicationRunner {
#Test
void contextLoads() {
}
#Override
public void run(ApplicationArguments args) throws Exception {
// load initial data in test DB
}
}
This is one case I'm aware of. Anything else?
These runners are used to run the logic on application startup, for example spring boot has ApplicationRunner(Functional Interface) with run method
ApplicationRunner run() will get execute, just after applicationcontext is created and before spring boot application startup.
ApplicationRunner takes ApplicationArgument which has convenient methods like getOptionNames(), getOptionValues() and getSourceArgs().
And CommandLineRunner is also a Functional interface with run method
CommandLineRunner run() will get execute, just after applicationcontext is created and before spring boot application starts up.
It accepts the argument, which are passed at time of server startup.
Both of them provides the same functionality and the only difference between CommandLineRunner and ApplicationRunner is CommandLineRunner.run() accepts String array[] whereas ApplicationRunner.run() accepts ApplicationArguments as argument. you can find more information with example here
In order to use ApplicationRunner or CommandLineRunner interfaces, one needs to create a Spring bean and implement either ApplicationRunner or CommandLineRunner interfaces, both perform similarly. Once complete, your Spring application will detect your bean.
In addition, you can create multiple ApplicationRunner or CommandLineRunner beans, and control the ordering by implementing either
org.springframework.core.Ordered interface
org.springframework.core.annotation.Order annotation.
use case:
One might wish to log some command line arguments.
You could provide some instructions to the user on termination of this application.
consider:
#Component
public class MyBean implements CommandLineRunner {
#Override
public void run(String...args) throws Exception {
logger.info("App started with arguments: " + Arrays.toString(args));
}
}
Details on ApplicationRunner
ApplicationRunner and CommandLineRunner are two interfaces Spring Boot provides to run any custom code just before application is fully started.
Spring-batch is a batch processing framework. This uses CommandLineRunner to register and start batch jobs at application startup.
You can also use this interface to load some master data into cache/perform health checks.
The use-case varies from application to application.
Or like this:
#Bean
ApplicationRunner appStarted() {
return args -> {
logger.info("Ready to go - args: {}", args.getNonOptionArgs());
};
}

Spring boot application for background workers

I have defined the following Spring boot application:
#SpringBootApplication
public class Application implements CommandLineRunner {
public static void main(String[] args) {
new SpringApplicationBuilder(Application.class).web(WebApplicationType.NONE).run(args);
}
public void run(String... args) throws Exception {
Thread.currentThread().join();
}
}
I also have a package of workers (i.e. could be classes which implements Runnable). They are supposed to run indefinitely.
What is the "Spring way" to run them? (and doing so automatically, without explicitly knowing them)
Thanks
and doing so automatically, without explicitly knowing them
There's no mechanism to automatically run some Runnables from a certain place. You need to find a way to inform Spring about these classes.
Three common scenarios:
Execute some code during the application startup: ApplicationRunner and CommandLineRunner.
You either congregate Runnables and wrap them up into an [Application|CommandLine]Runner which should be a bean (e.g. #Bean, #Component, etc) or make each Runnable a separate [Application|CommandLine]Runner.
Execute some code at some point of time: TaskExecutor.
You inject a TaskExecutor and give it previously gathered Runnables.
Execute some code repeatedly: TaskScheduler.
You inject a TaskScheduler and give it previously gathered Runnables, plus a trigger.
More details: Task Execution and Scheduling
You could (1) have your classes that implement Runnable be annotated with #Component, so that Spring can find them. You can then (2) write a WorkManager, annotated with #Service, with an #Autowired List - which Spring would initialize with a list instances of all your classes from (1). And could (3) write a #PostConstruct method in your WorkManager that would iterate over that list of Runnables, and pass each one to a TaskExecutor to run...

Spring cache using #Cacheable during #PostConstruct does not work

related to the commit in spring framework https://github.com/spring-projects/spring-framework/commit/5aefcc802ef05abc51bbfbeb4a78b3032ff9eee3
the initialisation is set to a later stage from afterPropertiesSet() to afterSingletonsInstantiated()
In short:
This prevents the caching to work when using it in a #PostConstruct use case.
Longer version:
This prevents the use case where you would
create serviceB with #Cacheable on a methodB
create serviceA with #PostConstruct calling serviceB.methodB
#Component
public class ServiceA{
#Autowired
private ServiceB serviceB;
#PostConstruct
public void init() {
List<String> list = serviceB.loadSomething();
}
This results in org.springframework.cache.interceptor.CacheAspectSupport not being initialised now and thus not caching the result.
protected Object execute(CacheOperationInvoker invoker, Object target, Method method, Object[] args) {
// check whether aspect is enabled
// to cope with cases where the AJ is pulled in automatically
if (this.initialized) {
//>>>>>>>>>>>> NOT Being called
Class<?> targetClass = getTargetClass(target);
Collection<CacheOperation> operations = getCacheOperationSource().getCacheOperations(method, targetClass);
if (!CollectionUtils.isEmpty(operations)) {
return execute(invoker, new CacheOperationContexts(operations, method, args, target, targetClass));
}
}
//>>>>>>>>>>>> Being called
return invoker.invoke();
}
My workaround is to manually call the initialisation method:
#Configuration
public class SomeConfigClass{
#Inject
private CacheInterceptor cacheInterceptor;
#PostConstruct
public void init() {
cacheInterceptor.afterSingletonsInstantiated();
}
This of course fixes my issue but does it have side effects other that just being called 2 times (1 manual and 1 by the framework as intended)
My question is:
"Is this a safe workaround to do as the initial commiter seemed to have an issue with just using the afterPropertiesSet()"
As Marten said already, you are not supposed to use any of those services in the PostConstruct phase because you have no guarantee that the proxy interceptor has fully started at this point.
Your best shot at pre-loading your cache is to listen for ContextRefreshedEvent (more support coming in 4.2) and do the work there. That being said, I understand that it may not be clear that such usage is forbidden so I've created SPR-12700 to improve the documentation. I am not sure what javadoc you were referring to.
To answer your question: no it's not a safe workaround. What you were using before worked by "side-effect" (i.e. it wasn't supposed to work, if your bean was initialized before the CacheInterceptor you would have the same problem with an older version of the framework). Don't call such low-level infrastructure in your own code.
Just had the exact same problem as OP and listening to ContextRefreshedEvent was causing my initialization method to be called twice. Listening to ApplicationReadyEvent worked best for me.
Here is the code I used
#Component
public class MyInitializer implements ApplicationListener<ApplicationReadyEvent> {
#Override
public void onApplicationEvent(ApplicationReadyEvent event) {
//doing things
}
}
Autowire ApplicationContext and invoke method call using :
applicationContext.getBean(RBService.class).getRawBundle(bundleName, DEFAULT_REQUEST_LANG);
where getRawBundle is Cacheable method.

Categories

Resources