How to skip a particular job execution in Spring Quartz Scheduler - java

I have a CronExpression set for the job to be executed at every 30 minutes. But I need to skip a particular job if the earlier job is not complete.
For eg. I have 100 Employee whose Names to be updated in the database and I terms it as "Job1" which starts at 10AM. Now the case is like "Job1" is in process and by the time I have another job- "Job2" aligned where I need to update another 50 Employee's names. My problem is this,I need to skip "Job2" and further jobs till my currently running Job is completed.
<bean name="employeeNameUpdateJob" class="org.springframework.scheduling.quartz.JobDetailBean">
<property name="name" value="Employee Update Job"/>
<property name="group" value="Employee Update Group Job"/>
<property name="jobClass"
value="com.emp.scheduler.EmployeeUpdateScheduler" />
<property name="volatility" value="false" />
</bean>
<bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="name" value="Employee Update Trigger"/>
<property name="group" value="Employee Update Group Trigger"/>
<property name="volatility" value="false" />
<property name="jobDetail" ref="employeeNameUpdateJob"/>
<property name="cronExpression" value="0 0/30 * * * ?"/>
</bean>

One way is to implement TriggerListener interface, which provides a vetoJobExecution(Trigger trigger, JobExecutionContext context) method to veto the execution of a next job. Returning true from this method will stop the execution of job.
Interface documentation: http://quartz-scheduler.org/api/2.0.0/org/quartz/TriggerListener.html#vetoJobExecution(org.quartz.Trigger, org.quartz.JobExecutionContext)
Sample:
//SampleTriggerListener.java
public class SampleTriggerListener implements TriggerListener {
#Override
public boolean vetoJobExecution(Trigger trigger, JobExecutionContext ctx) {
if(!previousJobCompleted)
return true;
return false;
}
}
//Main.java
//init jobs, trigger & scheduler
this.scheduler.addTriggerListener(new SampleTriggerListener());
this.scheduler.start();

If they are the same job class : #DisallowConcurrentExecution
Else it sounds like you need to use a single threadpool executor. Inject the same executor to both classes ( or alternatively implement an orchestrator class to manage this ) and add the work units to the queue this way.

If you will never need to run multiple jobs in parallel you could set the worker thread pool that Quartz uses to 1. Then it will only ever run one job at a time. In your quartz.properties file, set:
org.quartz.threadPool.threadCount: 1

Related

How to Change Property Value at Runtime without restart application server in Spring MVC

We have Spring scheduled job with cron expression which is configured in database. I need to change schedule time without restart the application server. But i could not achieve this and i have tried many ways using SO solutions in other link, but none worked for me. Below is my code snippet.
Scheduler time will get from database during dispatcher servlet initializing and assign in the property variable test.scheduler
Scheduler.java
#Scheduled(cron = "${test.scheduler}")
public void testScheduler() {
System.out.println("Dynamic Scheduler Run Test"+new java.util.Date().getTime());
}
dispatcher-servlet.xml
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
<property name="properties">
<bean class="org.apache.commons.configuration.ConfigurationConverter" factory-method="getProperties">
<constructor-arg>
<bean class="org.apache.commons.configuration.DatabaseConfiguration">
<constructor-arg>
<ref bean="dataSource" />
</constructor-arg>
<constructor-arg value="TABLE_NAME" />
<constructor-arg value="TABLE_KEY" />
<constructor-arg value="TABLE_VALUE" />
</bean>
</constructor-arg>
</bean>
</property>
</bean>
From the table (TABLE_NAME), there is column's key is test.scheduler & value is 0 0/5 * * * ?
As far as I am aware, SpEL won't let you change the value of your scheduler once it has been initialised.
If you are using Spring Boot, you will be able to use Actuator to trigger a refreshed event:-
http://localhost:8080/actuator/refresh
In which case, you can make the bean implement RefreshedScope and update your scheduler by triggering the RefreshScopeRefreshedEvent:
#EventListener(RefreshScopeRefreshedEvent.class)
public void onRefresh(RefreshScopeRefreshedEvent event) {
// Read the database, update the scheduler.
}
An example implementation of this can be seen here.
This might not be the perfect solution to your problems but I am posting it for visibility and to help others.
An alternative solution may involve using a Trigger to determine the next execution time. An example StackOverflow answer can be seen here.

SPRING BATCH : How to configure remote chunking for multiple jobs running in a task executor

I am new to spring batch processing. I am using remote chunking where there is a master , multiple slaves and ActiveMQ for messaging.
Master has a job and a job launcher and the job launcher has a task-executor which is having following configuration
<task:executor id="batchJobExecutor" pool-size="2"queue-capacity="100" />.
Chunk configuration is
<bean id="chunkWriter"
class="org.springframework.batch.integration.chunk.ChunkMessageChannelItemWriter" scope="step">
<property name="messagingOperations" ref="messagingGateway" />
<property name="replyChannel" ref="replies" />
<property name="throttleLimit" value="50" />
<property name="maxWaitTimeouts" value="60000" />
</bean>
<bean id="chunkHandler"
class="org.springframework.batch.integration.chunk.RemoteChunkHandlerFactoryBean">
<property name="chunkWriter" ref="chunkWriter" />
<property name="step" ref="someJobId" />
</bean>
<integration:service-activator
input-channel="requests" output-channel="replies" ref="chunkHandler" />
So we are allowed to run two jobs at a time and the remaining jobs will be in the queue.
When two jobs are submitted Master is creating the chunk and submitting to the queue and slave is processing.
But the acknowledgment from the slave to master is giving error
java.lang.IllegalStateException: Message contained wrong job instance id [9331] should have been [9332].
at org.springframework.util.Assert.state(Assert.java:385) ~[Assert.class:4.1.6.RELEASE]
at org.springframework.batch.integration.chunk.ChunkMessageChannelItemWriter.getNextResult
Please help me with this.
The ChunkMessageChannelItemWriter is only designed for one concurrent step - you need to put it in step scope so each job gets its own instance - see this test case
EDIT
Actually, no; that won't work - since the bean instances are using the same reply channel, they could get each other's replies. I opened a JIRA Issue.
This is a very old post, but I think the issue you see here might be related to the throttle limit being larger than the maxWaitTimouts value 4.
What we have seen is that the implementation will not read more than maxWaitTimeouts entries from the reply queue after the job finished. I think this is a bug.
See also the question I asked on stackoverflow here : Remote batch job does not read all responses in afterStep method
I made a bug report for this as well: https://jira.spring.io/browse/BATCH-2651 and am creating a PR to fix the issue.

Calling Java Web Service Method In A Regular Time Period

I have a java web service in centos 7 on tomcat 7, and i use this service to update a database. I need to call its method regulary, example:
createCustomer will called in every 12 hour,
createOrder will called in every 3 minute etc.
How can i write this triger and where this code have run (in same server or something else)
You can give quartz scheduler a shot. Here you can find more infomation regarding the subject :
http://quartz-scheduler.org/generated/2.2.1/html/qs-all/#page/Quartz_Scheduler_Documentation_Set/_qs_all.1.009.html#
In a nutshell you can define an xml file, stating which task would work on which condition ( or time frame ). In the example below :
<!-- Order Recorder Job -->
<bean id="orderRecorderJob" class="com.cemgunduz.btcenter.job.OrderRecorderJob"/>
<bean id="orderTask" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject" ref="orderRecorderJob"/>
<property name="targetMethod" value="execute"/>
</bean>
<bean id="orderRecorderJobTrigger"
class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<property name="jobDetail" ref="orderTask" />
<property name="cronExpression" value="0 0/5 * * * ?" />
</bean>
A task is defined named orderTask, which is the execute method in orderRecorderJob.
This task is associated with a cron expression, thereby the trigger is defined. So to sum it all up the example above would trigger execute method of orderRecorderJob class every five minutes as stated on its cron expression ( 0 0/5 * * * ?). More on cron expression syntax :
http://www.quartz-scheduler.org/documentation/quartz-1.x/tutorials/crontrigger

Make Spring Quartz Job Scheduler Run on a user action

I am using Spring Quartz Job Scheduler in order to run a job in user selected days. Instead of automatically calls the job scheduler when application start I need the job scheduler start run on a a particular user action
<bean id="scheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref bean="testme"/>
</list>
</property>
</bean>
<bean id="testme" class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="jobDetail">
<ref bean="testme1"/>
</property>
<property name="cronExpression">
<value>0 15 10 1,15 * ?</value>
</property>
</bean>
Also I need to first user to select the dates he needs to run the job eg: monday and friday and then after click on submit button the scheduler will start from that point? So its like cronExpression values also change depending on the user selected dates. More over user can later change it to different dates too. So is it possible to do this or does Quartz Job Scheduler is not the approach to achieve what i needs?
Try to get your CronTriggerBean object testme programmatically
and set into it the desired cronExpression. This should work, I think.
The scheduler will run at startup. As Julien mentioned, the key is to create the jobs or triggers when the user takes action.
More over user can later change it to different dates too. So is it
possible to do this or does Quartz Job Scheduler is not the approach
to achieve what i needs?
Quartz can do this. You just need to reschedule the jobs.
You can autowire an instance of SchedulerFactoryBean into your own bean. From SchedulerFactoryBean you can get the Scheduler and tweak the job's triggers as needed or create new jobs/triggers:
Scheduler scheduler = schedulerFactory.getScheduler();
JobDetail job = scheduler.getJobDetail("yourJobName", "yourJobGroup");
// possibly do some stuff with the JobDetail...
// get the current trigger
CronTrigger cronTrigger = (CronTrigger) scheduler.getTrigger("yourTriggerName", "yourTriggerGroupName");
// possibly do some stuff with the current Trigger...
if (cronTrigger != null)
something.doSomeStuff();
// replace the old trigger with a new one. unless your users are sysadmins, they
// probably don't want to enter cron-type entries. there are other trigger types
// besides CronTrigger. RTFM and pick the best one for your needs
CronTrigger newTrigger = new CronTrigger();
newTrigger.setName("newTriggerName");
newTrigger.setGroup("yourTriggerGroup");
newTrigger.setCronExpression("valid cron exp here");
// the new trigger must reference the job by name and group
newTrigger.setJobName("yourJobName");
newTrigger.setJobGroup("yourJobGroup");
// replace the old trigger. this is by name, make sure it all matches up.
scheduler.rescheduleJob("yourJobName", "yourTriggerGroup", newTrigger);

Scheduling tasks to run once, using the Spring task namespace

I'm setting up a scheduled tasks scheme in spring, using the task namespace.
I want to schedule most tasks to fire according to a cron expression, and some to fire just once, a fixed delay after startup, and then never again (i.e. what setting repeatCount to 0 on a SimpleTriggerBean would achieve).
Is it possible to achieve this within the task namespace, or do I need to revert to defining beans for my triggers?
If you don't need an initial delay, you can make it run 'just once' on startup as follows:
<task:scheduled-tasks>
<!-- Long.MAX_VALUE ms = 3E8 years; will run on startup
and not run again for 3E8 years -->
<task:scheduled ref="myThing" method="doStuff"
fixed-rate="#{ T(java.lang.Long).MAX_VALUE }" />
</task:scheduled-tasks>
(Of course, if you think your code is going to run for longer than 3E8 years, you may need a different approach...)
If you need an initial delay, you can configure it as follows (I'm testing with Spring 3.1.1) - this doesn't require any additional dependencies and you don't have to write your own trigger, but you do have to configure the PeriodicTrigger provided by Spring:
<bean id="onstart" class="org.springframework.scheduling.support.PeriodicTrigger" >
<!-- Long.MAX_VALUE ms = 3E8 years; will run 5s after startup and
not run again for 3E8 years -->
<constructor-arg name="period" value="#{ T(java.lang.Long).MAX_VALUE }" />
<property name="initialDelay" value="5000" />
</bean>
<task:scheduled-tasks>
<task:scheduled ref="myThing" method="doStuff" trigger="onstart" />
</task:scheduled-tasks>
Spring 3.2 appears to support the "initial-delay" attribute directly, but I haven't tested this; I'd guess this works:
<task:scheduled-tasks>
<task:scheduled ref="myThing" method="doStuff"
fixed-rate="#{ T(java.lang.Long).MAX_VALUE }"
initial-delay="5000"/>
</task:scheduled-tasks>
My working example:
<bean id="whateverTriggerAtStartupTime" class="org.springframework.scheduling.quartz.SimpleTriggerBean">
<property name="jobDetail" ref="whateverJob"/>
<property name="repeatCount" value="0"/>
<property name="repeatInterval" value="10"/>
</bean>
If you have a look at the Task namespace XSD, you'll see that there are only three different configuration types: fixed-delay, fixed-rate and cron.
And if you look at the source of ScheduledTasksBeanDefinitionParser, you'll see that no more than one of these values are evaluated. Here is the relevant part:
String cronAttribute = taskElement.getAttribute("cron");
if (StringUtils.hasText(cronAttribute)) {
cronTaskMap.put(runnableBeanRef, cronAttribute);
}
else {
String fixedDelayAttribute = taskElement.getAttribute("fixed-delay");
if (StringUtils.hasText(fixedDelayAttribute)) {
fixedDelayTaskMap.put(runnableBeanRef, fixedDelayAttribute);
}
else {
String fixedRateAttribute = taskElement.getAttribute("fixed-rate");
if (!StringUtils.hasText(fixedRateAttribute)) {
parserContext.getReaderContext().error(
"One of 'cron', 'fixed-delay', or 'fixed-rate' is required",
taskElement);
// Continue with the possible next task element
continue;
}
fixedRateTaskMap.put(runnableBeanRef, fixedRateAttribute);
}
}
So there is no way to combine these attributes. In short: the namespace won't get you there.
This works and is way easier than the other answers.
// Will fire the trigger 1 + repeatCount number of times, start delay is in milliseconds
simple name: 'mySimpleTrigger', startDelay: 5000, repeatCount: 0

Categories