Use .yml variable into #Scheduled(cron = variable) - spring-boot

I got a groovy class
#Configuration
class SchedulerTest {
SomeService service
#Inject
SchedulerTest (SomeService service) {
this.service = service
}
#Scheduled(cron = '0/5 * * * * *')
void doSomething() {
service.someMethod()
}
}
and got a .yml file
scheduler:
cron: 0/5 * * * * *
I want to use the .yml variable to the cron value into #Scheduled, like
#Scheduled(cron = schdulers.cron)
How can I make this work?
Obs. I`m very new in development.

Put
#Scheduled(cron = '${scheduler.cron}')
void doSomething() {
service.someMethod()
}
With the parameter in single quotes
With double quotes, groovy is trying to evaluate the template

Related

Spring: endpoint to start a scheduled task

I have a scheduled task that works perfectly, like this:
#Scheduled(cron="*/5 * * * * MON-FRI")
public void doSomething() {
// something that should execute on weekdays only
}
I want to create a REST endpoint that would start this task out of it's normal schedule.
How would I programatically fire-and-forget this task?
You could do something really simple.
Your schedule:
#Component
#RequiredArgsConstructor
public class MySchedule {
private final MyClassThatHasTheProcessing process;
#Scheduled(cron = "*/5 * * * * MON-FRI")
public void doSomething() {
// the actual process is made by the method doHeavyProcessing
process.doHeavyProcessing();
}
}
Your Controller
#RestController
#RequestMapping(path = "/task")
#RequiredArgsConstructor
public class MyController {
private final MyClassThatHasTheProcessing process;
// the executor used to asynchronously execute the task
private final ExecutorService executor = Executors.newSingleThreadExecutor();
#PostMapping
public ResponseEntity<Object> handleRequestOfStartTask() {
// send the Runnable (implemented using lambda) for the ExecutorService to do the async execution
executor.execute(() - >{
process.doHeavyProcessing();
});
// the return happens before process.doHeavyProcessing() is completed.
return ResponseEntity.accepted().build();
}
}
This will keep your scheduled task working as well as being able do trigger the task on demand by hitting your endpoint.
The HTTP 202 Accepted will be returned and the actual thread released, while the ExecutorService will delegate the process.doHeavyProcessing execution to another thread, which means that it will run in a 'fire and forget' style, because the thread that is serving the request will return even before the other task is finally terminated.
If you don't know what is an ExecutorService, this may help.
This can be done by writing something like below
#Controller
MyController {
#Autowired
MyService myService;
#GetMapping(value = "/fire")
#ResponseStatus(HttpStatus.OK)
public String fire() {
myService.fire();
return "Done!!";
}
}
MyService {
#Async
#Scheduled(cron="*/5 * * * * MON-FRI")
public void fire(){
// your logic here
}
}

Prevent Spring Schedulers run at the same time

I have three Spring Schedulers like as shown below
Scheduler 1 (Runs every 15 min)
#Scheduled(cron = "0/15 * * * *")
public void scheduler1() {
// some logic
}
Scheduler 2 (Runs every 20 min)
#Scheduled(cron = "0/20 * * * *")
public void scheduler2() {
// some logic
}
Scheduler 3 (Runs every 25 min)
#Scheduled(cron = "0/25 * * * *")
public void scheduler3() {
// some logic
}
The schedulers are working fine, but there are times in sometimes they will run at the same time which will create some issues. I would like to know if there is any way in which we can prevent more than one scheduler to execute at a time in Spring

Spring 4.0 EL cron expression doesn't get resolve from #Bean method

As per requirement, I need to get cron expression from DB and set it to
Scheduled annotation's param but getting Invalid Cron expression error. Below is
my code and it works only when I add cron expression directly in #Scheduled param
#Component
#EnableScheduling
#Configuration
public class TicketReportScheduler
{
private static Log _log =
LogFactoryUtil.getLog(TicketReportScheduler.class);
#Autowired
NotificationUtility notificationUtility;
#Bean
public String getConfigRefreshValue() {
return "0 */1 * * * ?";
}
//#Scheduled(cron = "0 */1 * * * ?") - works
#Scheduled(cron = "#{#getConfigRefreshValue}") // error
public void demoServiceMethod() throws Exception {
_log.info("Cron job started " + new Date());
}
}
Error while run:
I googled a lot but unable to fix my problem. As per requirement, I have to pass cron expression from DB. For POC, I created a simple method and return cron expression but it doesn't work and I am getting below error:
Caused by: java.lang.IllegalStateException: Encountered invalid #Scheduled method 'demoServiceMethod': Cron expression must consist of 6 fields (found 1 in "#{#getConfigRefreshValue}")

Spring Boot Scheduler fixedDelay and cron

I'm running a spring boot scheduled process that takes 5-10 seconds to complete. After it completes, 60 seconds elapse before the process begins again (Note that I'm not using fixedRate):
#Scheduled(fixedDelay=60_000)
Now, I want to limit it to run every minute Mon-Fri 9am to 5pm. I can accomplish this with
#Scheduled(cron="0 * 9-16 ? * MON-FRI")
Problem here is that this acts similar to fixedRate - the process triggers EVERY 60 seconds regardless of the amount of time it took to complete the previous run...
Any way to to combine the two techniques?
it worked for me like this
I created a bean that returns a specific task executor and allowed only 1 thread.
#Configuration
#EnableAsync
public class AsyncConfig implements AsyncConfigurer {
#Bean(name = "movProcTPTE")
public TaskExecutor movProcessualThreadPoolTaskExecutor() {
ThreadPoolTaskExecutor exec = new ThreadPoolTaskExecutor();
exec.setMaxPoolSize(1);
exec.initialize();
return exec;
}
}
In my service, I injected my task executor and wrapped my logic with it, so even though my schedule runs every minute, my logic will only run when the task executor is free.
#Service
#EnableScheduling
public class ScheduledService {
#Autowired
private ReportDataService reportDataService;
#Autowired
private AsyncService async;
#Autowired
#Qualifier("movProcTPTE")
private TaskExecutor movProcTaskExecutor;
#Scheduled(cron = "0 * * 1-7 * SAT,SUN")
public void agendamentoImportacaoMovProcessual(){
movProcTaskExecutor.execute(
() -> {
reportDataService.importDataFromSaj();
}
);
}
}
try this:
#Schedules({
#Scheduled(fixedRate = 1000),
#Scheduled(cron = "* * * * * *")
})
You can try this one:
#Scheduled(cron="1 9-16 * * MON-FRI")
Also you can try write correct on this site https://crontab.guru/
You can pass fixed delay (and any other number of optional parameters) to the annotation, like so:
#Scheduled(cron="0 * 9-16 ? * MON-FRI", fixedDelay=60_000)
From the documentation: https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/scheduling/annotation/Scheduled.html

AEM 6.3 : Creating Scheduler using OSGi R6 annotations

I have written a scheduler using OSGi R6 annotations but it doesn't seem to run :
package com.aem.sites.interfaces;
import org.osgi.service.metatype.annotations.AttributeDefinition;
import org.osgi.service.metatype.annotations.AttributeType;
import org.osgi.service.metatype.annotations.ObjectClassDefinition;
#ObjectClassDefinition(name = "Scheduler Configuration for Weather", description = "Configuration file for Scheduler")
public #interface SchedulerConfiguration {
#AttributeDefinition(
name = "sample parameter",
description="Sample String parameter",
type = AttributeType.STRING
)
public String parameter() default "scheduler";
#AttributeDefinition(
name = "Concurrent",
description = "Schedule task concurrently",
type = AttributeType.BOOLEAN
)
boolean scheduler_concurrent() default true;
#AttributeDefinition(
name = "Expression",
description = "Cron-job expression. Default: run every minute.",
type = AttributeType.STRING
)
String scheduler_expression() default "0 * * * * ?";
}
and
package com.aem.sites.schedulers;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.metatype.annotations.Designate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.aem.sites.interfaces.SchedulerConfiguration;
#Component(immediate = true,
configurationPid = "com.aem.sites.schedulers.WeatherServiceScheduler")
#Designate(ocd=SchedulerConfiguration.class)
public class WeatherServiceScheduler implements Runnable {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
private String myParameter;
#Override
public void run() {
logger.info("*******************************************Sample OSGi Scheduler is now running", myParameter);
}
#Activate
public void activate(SchedulerConfiguration config) {
logger.info("*******************************************weather service scheduler"+ myParameter);
myParameter = config.parameter();
}
}
I am following this https://github.com/nateyolles/aem-osgi-annotation-demo/blob/master/core/src/main/java/com/nateyolles/aem/osgiannotationdemo/core/schedulers/SampleOsgiScheduledTask.java but looks like I am doing something wrong here. Not sure what though.
Thanks in advance
In your WeatherSchedulerService class, you are not registering it as a service. Instead of configurationPid, you can do like this service = Runnable.class.
The correct way to create a SlingScheduler using OSGi R6 annotations is as follows -
Create your OSGi configuration class
import org.osgi.service.metatype.annotations.AttributeDefinition;
import org.osgi.service.metatype.annotations.AttributeType;
import org.osgi.service.metatype.annotations.ObjectClassDefinition;
#ObjectClassDefinition(name = "Sling Scheduler Configuration", description = "This configuration is used to demonstrates a sling scheduler in action")
public #interface SchedulerConfiguration {
#AttributeDefinition(
name = "Scheduler name",
description = "Name of the scheduler",
type = AttributeType.STRING)
public String name() default "Custom Sling Scheduler";
#AttributeDefinition(
name = "Enabled",
description = "Flag to enable/disable a scheduler",
type = AttributeType.STRING)
public boolean enabled() default false;
#AttributeDefinition(
name = "Cron expression",
description = "Cron expression used by the scheduler",
type = AttributeType.STRING)
public String cronExpression() default "0 * * * * ?";
#AttributeDefinition(
name = "Custom parameter",
description = "Custom parameter to showcase the usage of a sling scheduler",
type = AttributeType.STRING)
public String customParameter();
}
Create your Scheduler class as a service. For creating an OSGi service using R6 annotations we use #Component(service=<your-interface>.class,...).
Thus, create a service as follows
import org.apache.sling.commons.scheduler.ScheduleOptions;
import org.apache.sling.commons.scheduler.Scheduler;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Deactivate;
import org.osgi.service.component.annotations.Modified;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.metatype.annotations.Designate;
import org.redquark.aem.learning.core.configurations.SchedulerConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
#Component(immediate = true, service = Runnable.class)
#Designate(ocd = SchedulerConfiguration.class)
public class CustomScheduler implements Runnable {
// Logger
private final Logger log = LoggerFactory.getLogger(this.getClass());
// Custom parameter that is to be read from the configuration
private String customParameter;
// Id of the scheduler based on its name
private int schedulerId;
// Scheduler instance injected
#Reference
private Scheduler scheduler;
/**
* Activate method to initialize stuff
*
* #param schedulerConfiguration
*/
#Activate
protected void activate(SchedulerConfiguration schedulerConfiguration) {
schedulerId = schedulerConfiguration.name().hashCode();
customParameter = schedulerConfiguration.customParameter();
}
/**
* Modifies the scheduler id on modification
*
* #param schedulerConfiguration
*/
#Modified
protected void modified(SchedulerConfiguration schedulerConfiguration) {
// Removing scheduler
removeScheduler();
// Updating the scheduler id
schedulerId = schedulerConfiguration.name().hashCode();
// Again adding the scheduler
addScheduler(schedulerConfiguration);
}
/**
* This method deactivates the scheduler and removes it
*
* #param schedulerConfiguration
*/
#Deactivate
protected void deactivate(SchedulerConfiguration schedulerConfiguration) {
// Removing the scheduler
removeScheduler();
}
/**
* This method removes the scheduler
*/
private void removeScheduler() {
log.info("Removing scheduler: {}", schedulerId);
// Unscheduling/removing the scheduler
scheduler.unschedule(String.valueOf(schedulerId));
}
/**
* This method adds the scheduler
*
* #param schedulerConfiguration
*/
private void addScheduler(SchedulerConfiguration schedulerConfiguration) {
// Check if the scheduler is enabled
if (schedulerConfiguration.enabled()) {
// Scheduler option takes the cron expression as a parameter and run accordingly
ScheduleOptions scheduleOptions = scheduler.EXPR(schedulerConfiguration.cronExpression());
// Adding some parameters
scheduleOptions.name(schedulerConfiguration.name());
scheduleOptions.canRunConcurrently(false);
// Scheduling the job
scheduler.schedule(this, scheduleOptions);
log.info("Scheduler added");
} else {
log.info("Scheduler is disabled");
}
}
/**
* Overridden run method to execute Job
*/
#Override
public void run() {
log.info("Custom Scheduler is now running using the passed custom paratmeter, customParameter {}",
customParameter);
}
In the activate() method, we are reading the required values. Then we are getting the schedulerId from the scheduler name.
The modified() method recalculates the schedulerId in case the OSGi configuration is modified.
In the addScheduler() method, we are registering the scheduler using the Scheduler API.
For more information and step by step execution, you can see my blog post as well - Day 13: Schedulers in AEM
I hope this helps. Happy coding!
There is no need for configurationPid in the class annotation, and also you are missing service=Runnable.class which should follow immediate=true, i.e. the class declaration should look like:
#Component(immediate = true, service=Runnable.class)
#Designate(ocd=SchedulerConfiguration.class)
public class WeatherServiceScheduler implements Runnable {

Resources