Springboot Scheduler runs 2 times and ends - spring-boot

I trying to start Scheduled job, but instead printing xxx every 5 seconds infinitely, what happens is 'xxx' printed twice - on the same second (doesn't wait 5 secs) , then prints 'EXIT' .
Why does it happen?
#EnableScheduling
#SpringBootApplication
public class App implements CommandLineRunner{
#Autowired
private Job job;
public static void main(final String... args) {
try (final ConfigurableApplicationContext context = new SpringApplicationBuilder(App.class)
.web(WebApplicationType.NONE).run(args)) {
}
System.out.println("EXIT");
}
#Override
public void run(String... args) throws Exception {
job.run();
}
}
#Component
public class Job {
#Scheduled(fixedRateString = "5000")
public void run(){
System.out.println("xxx");
}
}
in console I only see:
z.r.AsyncReporter$BoundedAsyncReporter : Timed out waiting for in-flight spans to send.
o.s.s.c.ThreadPoolTaskScheduler : Shutting down ExecutorService 'taskScheduler'

You would need to add the #EnableScheduling annotation for enabling scheduled tasks capabilities. Additionally, you will need to change how you're using SpringApplicationBuilder as its scope (and lifetime) is currently limited by the try-with-resources statement. The following should work:
#EnableScheduling
#SpringBootApplication
public class App implements CommandLineRunner {
public static void main(final String... args) {
new SpringApplicationBuilder(App.class)
.web(WebApplicationType.NONE)
.build()
.run(args);
}
[...]

The fixedRate immediately fires. Therefore if you want to wait for the initial run you should add initialDelay as follows;
#Scheduled(fixedRate = 5000, initialDelay = 5000)
And when the applicaiton finishes the schedule cannot stop termination...
And of course #EnableScheduling is required...

Related

How to schedule a cron job in spring boot without using #Scheduled() annotation

In spring boot, can I schedule a spring job by not using #Scheduled annotation to a method?
I am working with spring job in the spring boot. I want to schedule a job by using cron expression, but without using #Scheduled(cron = " ") annotation to the method.
I know that I can schedule a job inside this method as below.
#Scheduled (cron = "0 10 10 10 * ?")
public void execute() {
/ * some job code * /
}
But I want it to be dynamic so that I can take a cron expression as input from the user and schedule it.
I came up with a working example since I found your question interesting and have been interested in this problem before. It's based entirely on the source code so I have no idea if it comes close to following best practice. Nonetheless, you may be able to tune it to your needs. FYI, you don't necessarily need to create a new ScheduledTaskRegistrar object - I figured that since your objective is a dynamic scheduler, you wouldn't be interested in defining your tasks purely in the overwritten method.
#SpringBootApplication
public class TaskScheduler implements SchedulingConfigurer, CommandLineRunner {
public static void main(String[] args){SpringApplication.run(TaskScheduler.class, args);}
List<CronTask> cronTasks;
#Override
public void run(String... args) throws Exception {
CronTask task = this.createCronTask(new Runnable() {
#Override
public void run() {
System.out.println(LocalDateTime.now());
}
}, "1/10 * * * * *");
ScheduledTaskRegistrar taskRegistrar = new ScheduledTaskRegistrar();
taskRegistrar.addCronTask(task);
configureTasks(taskRegistrar);
Thread.sleep(51);
taskRegistrar.destroy();
taskRegistrar = null;
ScheduledTaskRegistrar taskRegistrar2 = new ScheduledTaskRegistrar();
taskRegistrar2.addCronTask(task);
configureTasks(taskRegistrar2);
}
#Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
// "Calls scheduleTasks() at bean construction time" - docs
taskRegistrar.afterPropertiesSet();
}
public CronTask createCronTask(Runnable action, String expression) {
return new CronTask(action, new CronTrigger(expression));
}
}
I have experience using cron jobs in Azure and other places. Programming in Java, I have typically used #Scheduled with fixed times just for the sake of simplicity. Hope this is useful to you though.
Here is my working example, If somebody wants to use TaskScheduler without using #Scheduled Annotation
#Configuration Class
#Configuration
public class SchedulerConfig implements SchedulingConfigurer {
final Logger LOGGER = LogManager.getLogger(SchedulerConfig.class);
#Override
public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
LOGGER.debug("Creating Async Task Scheduler");
scheduledTaskRegistrar.setTaskScheduler(taskScheduler());
}
// This is mandatory otherwise it will to be able to find bean of
// taskScheduler. Without this it was giving runtime error says, can not find
// taskScheduler bean.
#Bean
public TaskScheduler taskScheduler() {
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setPoolSize(20); // Better to read it from property file.
scheduler.setThreadNamePrefix("ThreadScheduler-");
scheduler.initialize();
return scheduler;
}
}
Scheduler Class which is called from Application class.
#Component
public class MyTaskScheduler {
private TaskScheduler taskScheduler;
// Here we are auto-wiring taskScheduler, that's why need to create
// taskScheduler bean in configuration class
#Autowired
public void setScheduler(TaskScheduler scheduler) {
this.taskScheduler = scheduler;
}
public void schedule() {
taskScheduler.scheduleWithFixedDelay(new Runnable(){
#Override
public void run() {
System.out.println("I am running after every 1 second");
}
}, 1000);
}
}
If in any chance #Configuration annotation is not working so put #EnableConfigurationProperties at main running class.
Make sure that you put #EnableScheduling at Application class, so main runnable class will look like
#SpringBootApplication
#EnableScheduling
#EnableConfigurationProperties
public class MainApplication implements CommandLineRunner {
#Autowired
MyTaskScheduler myTaskScheduler;
public static void main(String[] args) {
final Logger logger = LogManager.getLogger(MainApplication.class);
SpringApplication.run(MainApplication.class, args);
logger.info("Application started");
}
#Override
public void run(String... args) throws Exception {
myTaskScheduler.schedule();
}
}
This answer is similar to the two previous ones, but is more compact because it leverages the scheduling registrar already provided in the application context:
#Configuration
#EnableScheduling
public class Schedule implements SchedulingConfigurer {
private final transient WorkflowTriggerService workflowTriggerService;
public Schedule(final WorkflowTriggerService workflowTriggerService) {
this.workflowTriggerService = workflowTriggerService;
}
#Override
public void configureTasks(final ScheduledTaskRegistrar taskRegistrar) {
for (final WorkflowTrigger trigger : workflowTriggerService.getWorkflowTriggersWithSchedules()) {
taskRegistrar.addCronTask(new WorkflowTask(trigger), trigger.getSchedule());
}
}
}
Each WorkflowTrigger returned by the service has its own cron schedule, and therefore allows dynamic registration of scheduled tasks that are not known at compilation time.

How to ensure Spring Cloud Stream Listener to wait to process messages until Application is fully initialized on Start?

With Spring Cloud Stream Kafka app, how can we ensure that the stream listener waits to process messages until some dependency tasks (reference data population, e.g.) are done? Below app fails to process messages because messages are delivered too early. How can we guarantee this kind of ordering within a Spring Boot App?
#Service
public class ApplicationStartupService implements ApplicationRunner {
private final FooReferenceDataService fooReferenceDataService;
#Override
public void run(ApplicationArguments args) throws Exception {
fooReferenceDataService.loadData();
}
}
#EnableBinding(MyBinding.class)
public class MyFooStreamProcessor {
#Autowired FooService fooService;
#StreamListener("my-input")
public void process(KStream<String, Foo> input) {
input.foreach((k,v)-> {
// !!! this fails to save
// messages are delivered too early before foo reference data got loaded into database
fooService.save(v);
});
}
}
spring-cloud-stream: 2.1.0.RELEASE
spring-boot: 2.1.2.RELEASE
I found this is not available in spring cloud stream as of May 15, 2018.
Kafka - Delay binding until complex service initialisation has completed
Do we have a plan/timeline when this is supported?
In the mean time, I achieved what I wanted by using #Ordered and ApplicationRunner. It's messy but works. Basically, stream listener will wait until other works are done.
#Service
#Order(1)
public class ApplicationStartupService implements ApplicationRunner {
private final FooReferenceDataService fooReferenceDataService;
#Override
public void run(ApplicationArguments args) throws Exception {
fooReferenceDataService.loadData();
}
}
#EnableBinding(MyBinding.class)
#Order(2)
public class MyFooStreamProcessor implements ApplicationRunner {
#Autowired FooService fooService;
private final AtomicBoolean ready = new AtomicBoolean(false);
#StreamListener("my-input")
public void process(KStream<String, Foo> input) {
input.foreach((k,v)-> {
while (ready.get() == false) {
try {
log.info("sleeping for other dependent components to finish initialization");
Thread.sleep(10000);
} catch (InterruptedException e) {
log.info("woke up");
}
}
fooService.save(v);
});
}
#Override
public void run(ApplicationArguments args) throws Exception {
ready.set(true);
}
}

Shutting down after task completion

I have spring application written in the following way -
#SpringBootApplication
#Import({
ApplicationLoader.class
})
public class MyApplication {
#Bean
public Clock clock() {
return Clock.systemDefaultZone();
}
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
The ApplicationLoader class looks like below
public class ApplicationLoader {
private final MyService myService;
public ApplicationLoader(MyService myService) {
// create
this.myService = myService;
}
#PostConstruct
void startJob() {
// perform some long running task
myService.runLongRunningTask();
}
}
Once ApplicationLoader finishes its job, Spring Application continues to run while I want it to stop.
Is there a way I can introduce a stop to this entire application lifecycle which runs after the application is done performing the long running task? I don't mind a rewrite of ApplicationLoader or MyApplication if there is a better way. My aim is to instantiate MyService and run myService.runLongRunningTask() based on certain runtime conditions.

execute spring job with scheduler except holidays

I have a java job configured with #EnableScheduling and I am able to run job with annotation
#Scheduled(cron = "${job1.outgoingCron:0 45 15,18,20 * * MON-FRI}", zone = "America/New_York")
public void Process() {
}
But I don't want to run this job for US holidays what is the best way to update schedule.
Try to set your schedule explicitly, using CronTrigger or custom Trigger, for example:
#SpringBootApplication
#EnableScheduling
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
#Bean
public TaskScheduler taskScheduler() {
return new ThreadPoolTaskScheduler();
}
}
#Component
#RequiredArgsConstructor // Lombok annotation
public class StartUp implements ApplicationRunner {
#NonNull private final TaskScheduler scheduler;
#Override
public void run(ApplicationArguments args) throws Exception {
// Variant 1
scheduler.schedule(this::myTask, new CronTrigger(/* your schedule */));
// Variant 2
scheduler.schedule(this::myTask, this::myTriger);
}
private void myTask() {
//...
}
private Date myTrigger(TriggerContext triggerContext) {
//...
}
}

Spring : #Scheduled task triggered through the #Controller and Websocket

I have an #Scheduled task which send data to a client every sec throught a websocket.
My need is to start running my scheduled task only when the client ask for it.
Instead of, my task starts when my server starts. it's not the behavior i want.
currently, I have a bean of my scheduled task which is declared in my SchedulingConfigurer :
#Configuration
#EnableScheduling
public class SchedulingConfigurer implements org.springframework.scheduling.annotation.SchedulingConfigurer {
#Bean
public ThreadPoolTaskScheduler taskScheduler() {
return new ThreadPoolTaskScheduler();
}
#Bean
public ScheduledTask scheduledTask() {
return new ScheduledTask();
}
#Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.setTaskScheduler(taskScheduler());
}
}
Here is my spring controller code :
#MessageMapping("/hello")
public void greeting() throws Exception {
//How do I start my scheduled task here ?
}
Maybe isn't possible to do that with #Scheduled annotation and i have to use the TaskScheduler interface ?
remove #Scheduled declaration from ScheduledTask class
implements Runnable interface instead of
#Component
//#Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class ScheduledTask implements Runnable {
private static final Logger log = LoggerFactory.getLogger(ScheduledTask.class);
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
public void doWork() {
printMessage();
// TODO real work
}
private void printMessage() {
log.info("time to work: {}", dateFormat.format(new Date()));
}
#Override
public void run() {
doWork();
}
}
schedule Your task in controller area like this
#Controller
public class ScheduledTaskController {
#Autowired
private TaskScheduler taskScheduler;
#Autowired
private ScheduledTask scheduledTask;
#RequestMapping(value = "/task/run", method = RequestMethod.GET)
public String runTask() {
// start to run task every 5 sec.
taskScheduler.schedule(scheduledTask, new CronTrigger("0/5 * * * * ?"));
// ok, redirect
return "redirect:/task";
}
}
#Schedule is the declarative way, so not the point you're trying to achieve here.
You could create a Bean using one of the TaskScheduler implementations, such as ThreadPoolTaskScheduler and inject that bean in your application.
It has all the necessary methods to dynamically schedule tasks.

Resources