8

I have a @Scheduled task in my application which is setup using CRON and run every 4 hours. The problem I face is that the CRON job does not start immediately after application startup but it starts only 4 hours after the application startup.

I tried to use a @PostConstruct method inside the task to invoke it, but that results in an error due to an uninitialized Spring context.

Please tell me how I can make the Scheduled task run immediately on application deployment and then on every 4 hours after the deployment.

EDIT:
I would not use a @PostConstruct since my scheduled method depends on other Beans , which are not initialized when this PostConstruct method runs for some reason.

v1shnu
  • 2,211
  • 8
  • 39
  • 68
  • Possible duplicate of [Execute method on startup in spring](http://stackoverflow.com/questions/2401489/execute-method-on-startup-in-spring) – aUserHimself Feb 15 '17 at 10:19
  • I tried `@PostContruct` but it always threw an Failed to load application context exception – v1shnu Feb 15 '17 at 10:23
  • 1
    Why not have two scheduled methods, one calling the business code upon startup with a fixedDelay of Long.maxValue and the other with a cron annotation to call it regularly every 4 hours? You would have to factor out your business code into a separate method. – user152468 Feb 15 '17 at 10:49
  • yes, that seems to be a possible option. but sounds more like a hack :( – v1shnu Feb 15 '17 at 11:08

9 Answers9

8

By 0 */4 * * * you specify "At minute 0 past every 4th hour (0:00, 4:00, 8:00 etc.)", which is not at startup time and then every 4 hours as I think you want. You can specify initial delay and rate by:

@Scheduled(initialDelay=0, fixedRate=4*60*60*1000)

If you are worried about hard-coded values, you can still provide config value:

@Scheduled(initialDelay=0, fixedRateString = "${some.config.string}")
Tomas F.
  • 610
  • 1
  • 6
  • 13
  • A cron is more appropriate in my use case. So I would prefer not using a fixed delay. Because, the 4 hours might be changed to every one or two days during non peak seasons of my application. So it would be right to use a cron since its more explanatory – v1shnu Feb 15 '17 at 11:09
8

I had @EnableScheduling in my app config. I had @Scheduled(fixedDelay=5000) in my scheduled task class. Even then it didn't work.

I added @Service to my scheduled task class and everything worked fine.

Hexfire
  • 5,945
  • 8
  • 32
  • 42
3

I was having the same situation. I need to run cron scheduler every 1st day of the month and also when the application starts.

This is how I achieved using ApplicationListener.

import org.springframework.context.ApplicationListener;
import org.springframework.boot.context.event.ApplicationReadyEvent;

@Component
public class ApplicationReadyListner implements ApplicationListener<ApplicationReadyEvent> {
    @Override
    public void onApplicationEvent(ApplicationReadyEvent event) {
        // callYourTask();
    }
}
Vishal Patel
  • 554
  • 6
  • 15
1

I'm not sure if you have tried this but you can use @Scheduled with an initialDelay

For fixed-delay and fixed-rate tasks, an initial delay may be specified indicating the number of milliseconds to wait before the first execution of the method.

https://docs.spring.io/spring/docs/current/spring-framework-reference/html/scheduling.html#scheduling-annotation-support-scheduled

@Scheduled(initialDelay=0, fixedRate=5000)
public void doSomething() {
    // something that should execute periodically
}

I assume this will execute your scheduled method when the application is run.

px06
  • 2,256
  • 1
  • 27
  • 47
  • Yes, I thought about this. An initial delay of 0 will start the job immediately, but I am using a cron instead of fixedDelay or fixedRate. So will this work ? – v1shnu Feb 15 '17 at 10:24
  • It seems that they are incompatible, see [this](http://stackoverflow.com/questions/36765624/the-initial-delay-attribute-may-not-be-used-with-cron-and-trigger-tasks), you might be able to find a solution. It is recommended to use `fixedDelay`. – px06 Feb 15 '17 at 10:29
  • A cron is more appropriate in my use case. So I would prefer not using a fixed delay. Because, the 4 hours might be changed to every one or two days during non peak seasons. So it would be right to use a cron. – v1shnu Feb 15 '17 at 10:34
  • I suggest you look at implementing an `ApplicationRunner` class which will allow you to do some work when the application starts and the cron can do its job every `n` hours after that. – px06 Feb 15 '17 at 10:39
1

If is not possible to use initialDelay attribute with the cron rule and your intention is execute this Job after every restart, you can implement a CommandLineRunner on your application class:

@SpringBootApplication
public class MyApplication implements CommandLineRunner {

    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }

    @Autowired
    private TaskService taskService;

    @Override
    public void run(final String... args) throws Exception {

        taskService.run();
    }

}

It's not the best strategy, but works.

Dherik
  • 17,757
  • 11
  • 115
  • 164
0

Just specify the method you want to run in the init_method attribute of the bean.

Java config: @Bean(init_method="methodWhichStartsTask")

XML config: <bean id="someId" class="com.example.Clazz" init-method="methodWhichStartsTask"/>

This will invoke the method just after the bean is properly initialized and if the method is scheduled, then it will be called afterwards every 4 hours.

Andrei Balici
  • 749
  • 5
  • 12
  • Please tell me if the init method will be called only after all the application is fully loaded. Because my method uses methods in other classes which has autowired components which are yet to be loaded. – v1shnu Feb 15 '17 at 10:25
  • yeah. Spring should only call the init-method when it is safe to do so (i.e. the whole application context has been initialized) – Andrei Balici Feb 15 '17 at 10:26
  • can this init_method be used as an option for `@Service` ? – v1shnu Feb 15 '17 at 10:28
  • I am not sure why you'd use it there. it is a directive for the IoC container to immediately call that method once your bean has been initialized. for sure you have a Java-based config or XML config in which you can add that attribute to the bean in question. – Andrei Balici Feb 15 '17 at 10:32
  • oh I see what you mean...the equivalent of init-method is a method annotated with @PostConstruct. I am not sure why you aare getting that error. what version of Java are you running? – Andrei Balici Feb 15 '17 at 10:36
  • I have a DAO layer which is annotated with `@Service`. I have a method inside the DAO which needs to be run on schedule basis. I have setup the cron for that and it works as expected (runs every 4 hours) . But the problem is I am not able to run it on application startup. I am using 1.8 – v1shnu Feb 15 '17 at 10:38
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/135756/discussion-between-andrei-balici-and-vichu). – Andrei Balici Feb 15 '17 at 10:39
  • Does not CRON specify time pattern instead of delays? By `0 */4 * * *` you specify "At minute 0 past every 4th hour", which is not at startup time and then every 4 hours as OP wants. Id use `@Scheduled(initialDelay=0, fixedRate=4*60*60*1000)` – Tomas F. Feb 15 '17 at 10:46
0

Instead of cron inside @Scheduled, use fixedRate like below. By default initialDelay is -1, which will start immediately or you can set 0.

@Scheduled(fixedRate=4*60*60*1000)
public void test() {
    System.out.println("helloworld");
}
Monzurul Shimul
  • 8,132
  • 2
  • 28
  • 42
  • A cron is more appropriate in my use case. So I would prefer not using a fixed delay. Because, the 4 hours might be changed to every one or two days during non peak seasons. So it would be right to use a cron. – v1shnu Feb 15 '17 at 10:33
  • Also , since initialDelay is -1 by default, will it work along with cron ? – v1shnu Feb 15 '17 at 10:34
  • aah ! ok , I will look for other options to start the scheduled job – v1shnu Feb 15 '17 at 10:36
0

user @EnableScheduling annotation on your class which contains you1 scheduled method:

see spring document:

To enable support for @Scheduled and @Async annotations add @EnableScheduling and @EnableAsync to one of your @Configuration classes:

@Configuration
@EnableAsync
@EnableScheduling
public class AppConfig {
}

link : https://docs.spring.io/spring/docs/current/spring-framework-reference/html/scheduling.html

M2E67
  • 937
  • 7
  • 23
0

@EnableScheduling annotation should be added in the main class for the Spring application.

Example :

@SpringBootApplication
@EnableScheduling
public class MyApplication {

public static void main(String[] args) {
    SpringApplication.run(MyApplication.class, args);
}

}

and above the method where the actual scheduling needed we need to add

@Scheduled(initialDelay = 1000, fixedDelay = 4*60*60*1000)

Example:

@Scheduled(initialDelay = 1000, fixedDelay = 4*60*60*1000)
public void schleduleWork() {
    /* Code Here */
}

For the above scheduler the first hit will be at 1000 milli second and subsequest hits will be at the interval of 4 hrs