in our system we use a settings class to point to the properties file, depends on which eve it loads different property file. To access specific property, we call 'Settings.getString('property_name_here')'.
In my code, i loaded the #scheduled cron expression to a variable and try to passed to the #scheduled annotation, but it won't work,
here is my code:
in properties file:
cron.second=0
cron.min=1
cron.hour=14
in constructor i have:
this.cronExpression = new StringBuilder()
.append(settings.getString("cron.second"))
.append(" ")
.append(settings.getString("cron.min"))
.append(" ")
.append(settings.getString("cron.hour"))
.append(" ")
.append("*").append(" ").append("*").append(" ").append("*")
.toString();
which create a String of "0 1 14 * * *", it's a valid con expression
in the scheduled task i have:
#Scheduled(cron = "${this.cronExpression}")
public void scheduleTask() throws Exception {
....
}
when I ran the code it complaint:
Caused by: java.lang.IllegalStateException: Encountered invalid #Scheduled method 'scheduleTask': Cron expression must consist of 6 fields (found 1 in "${this.cronExpression}")
then I changed this.cronExpression to a list of String:
this.cronExpression = Lists.newArrayList();
this.cronExpression.add(settings.getString("cron.second"));
this.cronExpression.add(settings.getString("cron.min"));
this.cronExpression.add(settings.getString("cron.hour"));
this.cronExpression.add("*");
this.cronExpression.add("*");
this.cronExpression.add("*");
but still got the same error, so what exactly is the cron expression supposed to be?
What I did and it worked for me:
In properties file:
my.cron.expression=0 3 * * * ?
In code:
#Scheduled(cron = "${my.cron.expression}")
public void scheduleTask(){
...
}
Related
I would Like to schedule a method using The annotation #Scheduled using cron, For example I want that the method should be executed everyday in the time specified by the client.
So I would like to get the cron value from the DB, in order to give the client the possibility of executing the method whenever he wants.
Here is my method, it sends emails automatically at 10:00 am to the given addresses, so my goal is to make the 10:00 dynamic.
Thanks for your help.
#Scheduled(cron = "0 00 10* * ?")
public void periodicNotification() {
JavaMailSenderImpl jms = (JavaMailSenderImpl) sender;
MimeMessage message = jms.createMimeMessage();
MimeMessageHelper helper;
try {
helper = new MimeMessageHelper(message, MimeMessageHelper.MULTIPART_MODE_MIXED_RELATED, StandardCharsets.UTF_8.name());
List<EmailNotification> emailNotifs = enr.findAll();
for (EmailNotification i : emailNotifs)
{
helper.setFrom("smsender4#gmail.com");
List<String> recipients = fileRepo.findWantedEmails(i.getDaysNum());
//List<String> emails = recipientsRepository.getScheduledEmails();
String[] to = recipients.stream().toArray(String[]::new);
helper.setTo(to);
helper.setText(i.getMessage());
helper.setSubject(i.getSubject());
sender.send(message);
System.out.println("Email successfully sent to: " + Arrays.toString(to));
}
}
catch (MessagingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
So I'm thinking at the next solution. ( + using the answer accepted here )
Let's say you have a class that imlpements Runnable interface -> this will be your job that gets executed. Let's call it MyJob
Also assume that we have a map that hold the id of the job and it's execution reference ( you'll see in a sec what i'm talking about). Call it something like currentExecutingJobs
Assume you have an endpoint that gets the name of the job and a cron expression from the client
When that endpoints gets called:
You'll look in the map above to see if there is any entry with that job id. If it exists, you cancel the job.
After that, you'll create an instance of that job ( You can do that by using reflection and having a custom annotation on your job classes in which you can provide an id. For example #MyJob("myCustomJobId" )
And from the link provided, you'll schedule the job using
// Schedule a task with the given cron expression
ScheduledFuture myJobScheduledFutere = executor.schedule(myJob, new CronTrigger(cronExpression));
And put the result in the above map currentExecutingJobs.put("myCustomJobId", myJobScheduledFutere)
ScheduledFuture docs
In case you want to read property from database you can implement the EnvironmentPostProcessor and read the necessary values from DB and add it to Environment object, more details available at https://docs.spring.io/spring-boot/docs/current/reference/html/howto.html#howto-spring-boot-application
I have implemented a scheduler with shedlock in my current spring project as follows:
#Scheduled(cron = "0 0/1 * * * *")
#SchedulerLock(name = "syncData",
lockAtMostFor = "${shedlock.myScheduler.lockAtMostFor}",
lockAtLeastFor = "${shedlock.myScheduler.lockAtLeastFor}")
public void syncSAData() {
//To assert that the lock is held
LockAssert.assertLocked();
...
}
Now I would like to write unit test for this function. Here the problem I am facing is: I am unable to mock first statement: LockAssert.assertLocked().
This should do the trick LockAssert.TestHelper.makeAllAssertsPass(true);
Just add it at the beginning of the test method.
Docs: https://github.com/lukas-krecan/ShedLock#lock-assert
I have a ScheduledLockConfiguration bean configuration.
#Bean
public ScheduledLockConfiguration taskScheduler(LockProvider lockProvider) {
return ScheduledLockConfigurationBuilder
.withLockProvider(lockProvider)
.withPoolSize(5)
.withDefaultLockAtMostFor(Duration.ofMinutes(5))
.build();
}
I just upgraded to shedlock-spring 3.0, and I don't know what to use instead of this Bean?
We can configure like below.
#Component
class TaskScheduler {
#Scheduled(cron = "0 0 10 * * ?")
#SchedulerLock(name = "TaskScheduler_scheduledTask", lockAtLeastForString = "PT5M", lockAtMostForString = "PT14M")
public void scheduledTask() {
// ...
}
}
#Scheduled will support corn format.
#SchedulerLock, the name parameter has to be unique and ClassName_methodName is typically enough to achieve that. We don't want more than one run of this method happening at the same time, and ShedLock uses the unique name to achieve that.
First, we've added lockAtLeastForString so that we can put some distance between method invocations. Using “PT5M” means that this method will hold the lock for 5 minutes, at a minimum. In other words, that means that this method can be run by ShedLock no more often than every five minutes.
Next, we added lockAtMostForString to specify how long the lock should be kept in case the executing node dies. Using “PT14M” means that it will be locked for no longer than 14 minutes.
In normal situations, ShedLock releases the lock directly after the task finishes. Now, really, we didn't have to do that because there is a default provided in #EnableSchedulerLock, but we've chosen to override that here.
I'm using Spring 3.0.2 version to run my Java/Flex application.
The task is that "Every new year at 12.00 A.M I need to reset a column value to zero on database". But the scheduler is not executing the configured method.
But if I change the corn expression to other form, for eg: run every the scheduler for every 50 secs it works perfectly.
Below is the cron expression which has been configured on properties file.
cron1.expression = 0 0 0 1 1 ?
Configuration in Application context.
<task:annotation driven/>
<util:properties id="javaScheduler" location="WEB-INF/javaScheduler.properties"/>
<context:property-placeholder properties-ref="javaScheduler"/>
<task:scheduled-tasks>
<task:scheduled ref="schedulerDAO" method="updateSampleRegistrationSeqno"
cron="#{javaScheduler['cron1.expression']}"/>
</task:scheduled-tasks>
Configured Method:
public void updateSampleRegistrationSeqno() throws Exception
{
try
{
logging.info("<<<<<<<<<<<<<<<<<<<----Updating sample registration series code in Seqnotransgenerator of table name All ---->>>>>>>>>>>>>>>>>>");
//String sp = "exec dbo.sp_SequenceUpdateNewYear";
getJdbcTemplate().execute("update SequencenoTransGenerator set nsequenceno = 0 where stablename = 'All'");
}
catch(Exception e)
{
logging.info("Exception occured at updateSampleRegistrationSeqno :------->"+e.getLocalizedMessage());
}
}
Add these two lines in your application context:
<context:property-placeholder location="classpath:/properties/app.properties"/>
or
<context:property-placeholder location="file:/properties/app.properties"/>
<task:annotation-driven></task:annotation-driven>
Schedule task:
#Scheduled(cron = "${cronexp}")
public void executeTask() {
//execute sql script
}
Add this cron expression in your properties file:
cronexp=0 0 0 1 1 ?
It worked for me.
Note: at location you should provide you properties path
We are using spring 3 and struts 2. We use spring #value annotation to get values from property files.
We want to get validation rules from property files instead of hard-coding them in action.
Here is sample property
system.properties
transfer.account.min.amount=10
Here is the action:
public class TransferToAccount implements Preparable {
#Value("${transfer.account.min.amount}") public String minAmount;
//...........execute and other methods omitted
#IntRangeFieldValidator(type = ValidatorType.FIELD, min = "${minAmount}", key = "validate.int.min")
public void setAmount(Integer amount) {
this.amount = amount;
}
The minAmount is populated correctly by value 10, but the validation is not working.
To see if parameters are passed correctly, I make a test as below.
Assume we want to get a key from spring managed property file ( This is just a test ;) )
system.properties
transfer.account.min.amount.key=validate.int.min
The resource bundle is:
validate.int.min = This field must be more than ${min}
...and we change validation as below:
#IntRangeFieldValidator(type = ValidatorType.FIELD, min = "${minAmount}", key = "${transfer.account.min.amount.key}")
Now when an error happens the validation message shows validate.int.min, instead of fetching this value from resource bundle!
Of course, when you run below code:
#IntRangeFieldValidator(type = ValidatorType.FIELD, min = "${minAmount}", key = "validate.int.min")
The error message is fetched resource bundle correctly!
If I can use annotation in this way, please let me know what is my mistake!
If I can not use annotations like this, please let me know what is the best way to avoid hard coding the validaiton rolls in actions.