How to do some preprocessing before starting Spring Boot Scheduler? - java

I want to have some basic preprocessing code that need to be run only once before starting the scheduler everytime. How can we achieve the same in Spring Boot ?

Are you looking for this? There are other options as well. But please elaborate the question.
#Component
public class Cache {
...
#PostConstruct
public void initializeCache() {
...
}
#Scheduled(fixedRate = 60L * 1000L)
public void refreshCache() {
...
}
}
Credits: Will a method annotated with #PostConstruct be guaranteed to execute prior to a method with #Scheduled within the same bean?

If you want to run code only once you could wait until Spring is ready and then run the code. To achieve this you could listen for the event like this:
#EventListener(ApplicationReadyEvent.class)
public void doSomethingAfterStartup() {
System.out.println("run your code here");
}
You can put that code in the application class to see the results.

Related

#PostConstruct and #Schedule with cron

I was using the following code-snippet for a long time to trigger an export right after the application was run and as well at the provided crontab.
#PostConstruct
#Scheduled(cron = "${" + EXPORT_SCHEDULE + "}")
public void exportJob()
{
exportService().exportTask();
}
After updating to Spring-Boot 2.6.x the policy according to "cyclic bean definition" got stricter and I couldn't use both annotations on this method anymore. Some answers recommended merging the #PostConstruct into the #Scheduled as initialDelay = 0 but crontab and the other properties of #Scheduled are not compatible. Resulting in following exception:
Caused by: java.lang.IllegalStateException: Encountered invalid #Scheduled method 'exportJob': 'initialDelay' not supported for cron triggers
Another solution may be to implement a CommandLineRunner bean. This interface contains a run() method that is executed after the application startup
#Bean
CommandLineRunner cronJobRunner(CronJobService cronJobService) {
return args -> cronJobService.cronJob();
}
A working solution I found was to use just the #Scheduled annotation but also have another method be annotated with #Scheduled and initialDelay=0 which just proxies the other method.
#Scheduled(cron = "${" + EXPORT_SCHEDULE + "}")
public void cronJob()
{
exportService().exportTask();
}
#Scheduled(fixedRate = Integer.MAX_VALUE, initialDelay = 0)
public void exportJob()
{
cronJob();
}
In theory, the cronJob is triggered more often, hence I chose it to do the service call. One could structure the call chain in the opposite direction as well.

Spring Boot multiple CommandLineRunner execution order and state

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.

Spring Scheduler: toggle between using cron and running once on startup

I managed to get a simple app running with spring application context and managed to get it working on a schedule via #Scheduled:
#Scheduled(cron= "0 30 9 * * *, zone="Australia/Sydney")
Is there a way to get #Scheduled to run on startup?
What I had in mind is to have a toggle in application.properties e.g:
scheduler.triggernow=true
Where if it is set to true, the app ignores the spring cron schedule and runs now (runs once on startup), and if set to false then it will use the spring cron schedule above and runs every day at 9:30am
I went to https://start.spring.io to get a basic application to work with (maven project, java, spring boot 2.3.4)
I saw a YouTube video to help set up a scheduler:
In the main class i have
#SpringBootApplication
#EnableConfigurationProperties
public class Demo{
public static void main(String[] args){
ConfigurationApplicationContext ctx = new SpringApplicationBuilder(Demo.class)
.web(WebApplicationType.None)
.run(args)
}
}
In a new class I added:
#Configuration
#EnableScheduling
#ConditionalOnProperty(name="scheduling.enabled", matchIfMissing=true)
public class Jobsched{
#Scheduled(cron="0 30 9 * * *",zone="Australia/Sydney")
public void test(){
System.out.println("hello");
}
}
On its own the cron scheduler works. And I have a property that can disable it. I would only want to disable it if I want to run it once on startup (currently disabling it will mean it does nothing)
This is a nonprod application by the way. Plan is to deploy this with scheduler running. And if I want to run it locally then just disable the scheduler and have it run on startup
Use the #PostConstruct in your class component (or service ...) to execute the code in startup,
below example showing how to proceed :
#Component
public class MyClass {
#PostConstruct
private void methodToExecute() {
doScheduledWOrk(); // call here for startup execution
}
#Scheduled(cron= "0 30 9 * * *, zone="Australia/Sydney")
public void scheduleMethod() {
doScheduledWOrk(); // sample function
}
public void doScheduledWOrk() {
//doing some scheduled stuff here...
}
}

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.

Annotation version of ApplicationListener<ContextClosedEvent> and similar interfaces

You can use code like the following to do some events based on when your web service is shut down (or refreshed/started).
public class APIService implements ApplicationListener<ContextClosedEvent>
{
#Override
public void onApplicationEvent(ContextClosedEvent contextClosedEvent) {
//Do shutdown work.
}
}
I was told that there is an annotation driven way to do this that does not require you to implement this interface. Specifically, you should be able to define a function with any name you like and annotate it so that it executes on service start-up or shut-down.
I'm having trouble finding references to this in my spring book or via google though. Can someone provide an example of how to do the above code via annotations alone?
As far as the Spring 4+ documentation is concerned, there is no such feature exactly as you describe it.
You can simulate the behavior with
#Component // defaults to eager initialization
class Setup {
#Autowired
private ApplicationContext context;
#PostConstruct
public void anyNameYouWant() {
System.out.println("starting");
}
#PreDestroy
public void hereToo() {
System.out.println("closing");
}
}
But you won't have access to the ContextClosedEvent, nor the full range of ApplicationContextEvents.
#EventListener introduced in Spring 4.2 Here you go

Categories

Resources