Dynamically Scheduled Jobs with Quartz - java

I implemented a Scheduled Job in Spring, I'm using Spring 3.1.1 with Hibernate and Struts2. The configuration works fine, but I want to change de cron dynamically, so I found several examples but I could not implement them, everywhere are different configurations, I only need to read cron values from the database instead of configuration file. Is this possible somehow?
My configuration now looks like that:
<!-- Scheduler Tasks -->
<bean name="statTask" class="com.bvc.spring.schedulers.MarketStatusJob"></bean>
<!-- Scheduler jobs -->
<bean id="statJobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject" ref="statTask" />
<property name="targetMethod" value="execute" />
</bean>
<!-- Cron Triggers -->
<bean id="statCronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="jobDetail" ref="statJobDetail" />
<property name="cronExpression" value="0 30 12 1/1 * ? *"/>
</bean>
<!-- Triggers -->
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref bean="statCronTrigger"/>
</list>
</property>
</bean>
Thanks in advance for help guys.

The possibly easiest approach would be, to subclass CronTriggerBean and implement database property resolution in an overridden setCronExpression(..) method, where you go to the database, fetch the desired cron, and call super.setCronExpression(dbValue)
An alternative, harder approach is to implement a PropertyPlaceholderConfigurer that reads them from a database rather than a properties file. It may not be trivial, though. There is no support for that, because it is more customary to read the values from a properties file. Also note that you won't be able to change the cron dynamically during execution.

You dont need to have statCronTrigger, need to implement quartz trigger in your main class
Job detail is fine.
CronTrigger trigger = null;
JobDetail jobD;
//Load context
ApplicationContext context = new ClassPathXmlApplicationContext("YOUR_CONTEXT_FILES.xml");
//Setup JobDetail
jobD = (JobDetail) context.getBean("statJobDetail");
//Setup CronTrigger
try {
trigger = new CronTrigger();
trigger.setName("AppTrigger");
trigger.setGroup(jobD.getGroup());
trigger.setJobName(jobD.getName());
trigger.setJobGroup(jobD.getGroup());
trigger.setCronExpression("*/10 * * * * ?");// you can read this from DB as well or any other configured string
} catch (ParseException e1) {
e1.printStackTrace();
}
//Scheduler
try{
Scheduler scheduler = (Scheduler) context.getBean("Scheduler");
scheduler.scheduleJob(jobD, trigger);
You may add quartz scheduler bean in context
<bean id="Scheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean"></bean>

Related

Using batch mode in Quartz

With the intent to improve performance of quartz, I intend to use batching in quartz as suggested in Performance Tuning on Quartz Scheduler
We create our quartz scheduler in integration with spring as below.
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<!-- Quartz requires a separate 'quartz.properties' file -->
<property name="configLocation" value="classpath:/quartz.properties"/>
<!-- Naturally, Quartz with the DB requires references
to the data source and transaction manager beans -->
<property name="dataSource" ref="quartzDataSource"/>
<!--<property name="transactionManager" ref="transactionManager"/>-->
<!-- reference to our 'autowiring job factory bean', defined above: -->
<property name="jobFactory" ref="quartzJobFactory"/>
<!-- Boolean controlling whether you want to override
the job definitions in the DB on the app start up.
We'll talk about it more in the next section. -->
<property name="overwriteExistingJobs" value="true"/>
<!-- I will not explain the next three properties, just use it as shown: -->
<property name="autoStartup" value="${isQuartzEnabled}" />
<property name="schedulerName" value="quartzScheduler"/>
<property name="applicationContextSchedulerContextKey" value="applicationContext"/>
<!-- Controls whether to wait for jobs completion on app shutdown, we use 'true' -->
<property name="waitForJobsToCompleteOnShutdown"
value="true"/>
<!-- Tell the Quartz scheduler about the triggers.
We have implemented the 'quartzTriggers' bean in the 'Jobs and triggers' section.
No need to pass job definitions, since triggers created via Spring know their jobs.
Later we'll see a case when we'll have to disable this and pass the jobs explicitly.-->
<property name="triggers" ref="quartzTriggers"/>
</bean>
How do I specify maxBatchSize & batchTimeWindow of createScheduler in DirectSchedulerFactory ?
I found that we can achieve by configuring the below property in quartz.properties file
org.quartz.scheduler.batchTriggerAcquisitionMaxCount
Reference : Configure Main Scheduler Settings
org.quartz.scheduler.batchTriggerAcquisitionFireAheadTimeWindow will set the batchTimeWindow.
org.quartz.scheduler.batchTriggerAcquisitionMaxCount will set the maxBatchSize property.
Source : https://github.com/quartz-scheduler/quartz/blob/master/quartz-core/src/main/java/org/quartz/impl/StdSchedulerFactory.java

How to start scheduled jobs dynamically with Spring (and Quartz)?

I'm following this tutorial to schedule jobs with Spring.
In the tutorial scheduling is started by the following code:
public static void main(String args[]){
AbstractApplicationContext context = new ClassPathXmlApplicationContext("app-config.xml");
}
Instead of using main, i'd like to start jobs with a method that can be called from anywhere in my application, for example:
public void startJobs() {
// what should this method do to start the jobs?
}
Can the following work?
public void startJobs() {
AbstractApplicationContext context = new ClassPathXmlApplicationContext("app-config.xml");
}
Is this considered good practice?
Basically what i want to achieve is to be able to start the jobs whenever i want (whenever i call the startJobs() method), not on startup in the main method.
How can i do this?
Have you done with scheduling using quartz and spring. If yes, is it running fine? The example link you shared belongs to "The Task Namespace"
Quartz uses Trigger, Job and JobDetail objects to realize scheduling of all kinds of jobs. For the basic concepts behind Quartz, have a look at
http://quartz-scheduler.org/documentation/quartz-2.2.x/quick-start
To integrate it with Spring, Please have a look at this also
http://docs.spring.io/spring/docs/current/spring-framework-reference/html/scheduling.html#scheduling-quartz-jobdetail
XML Configuration of Spring and quartz.
<bean id="jobLauncher" class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
<property name="jobRepository" ref="jobRepository" />
</bean>
<bean
class="org.springframework.batch.core.configuration.support.JobRegistryBeanPostProcessor">
<property name="jobRegistry" ref="jobRegistry" />
</bean>
<bean id="jobRegistry"
class="org.springframework.batch.core.configuration.support.MapJobRegistry" />
<bean name="csvLoaderJobDetail"
class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<property name="jobClass" value="com.example.CSVloader.ScheduledJob" />
<property name="jobDataMap">
<map>
<entry key="csvData" value="value1" />
<entry key="date" value="25/09/2015" />
<entry key="csvId" value="1" />
<entry key="jobName" value="csvLoadJob" />
<entry key="jobLocator" value-ref="jobRegistry" />
<entry key="jobLauncher" value-ref="jobLauncher" />
</map>
</property>
<property name="durability" value="true" />
</bean>
<bean id="csvLoaderTrigger"
class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<property name="jobDetail" ref="csvLoaderJobDetail" />
<property name="cronExpression" value="0 0 12 * * ?" />
</bean>
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="jobDetails">
<list>
<ref bean="csvLoaderJobDetail" />
</list>
</property>
<property name="triggers">
<list>
<ref bean="csvLoaderTrigger" />
</list>
</property>
<property name="quartzProperties">
<props>
<prop key="org.quartz.scheduler.skipUpdateCheck">true</prop>
</props>
</property>
</bean>
To fire the job manually, you need to inject SchedulerFactoryBean in your spring bean. First you need to get all the jobs created in quartz scheduler, then you can trigger any job manually by using Job Key and Job group of each job.
#Autowired
private SchedulerFactoryBean schedulerFactory;
org.quartz.Scheduler scheduler = schedulerFactory.getScheduler();
// loop jobs by group
for (String groupName : scheduler.getJobGroupNames()) {
// get jobkey
for (JobKey jobKey : scheduler.getJobKeys(GroupMatcher
.jobGroupEquals(groupName))) {
String jobName = jobKey.getName();
String jobGroup = jobKey.getGroup();
scheduler.triggerJob(jobName, jobGroup);
}
}
Now you can create a list of Object, which contain jobName and jobGroup to trigger any job manually.
A better and easy way , use #Scheduled annotation.
Method 1) Task scheduling using fixed delay attribute in #Scheduled annotation
#Scheduled(fixedDelay = 5000)
Method 2) Task scheduling using cron expression in #Scheduled annotation
#Scheduled(cron="*/5 * * * * ?")
Method 3) Task scheduling using cron expression from properties file
#Scheduled(cron = "${cron.expression}")
You can get complete example here

Quartz CRON triggers job only once

i have a spring batch job which needs to be scheduled at specific hour of the day. I have setup a Quartz CRON scheduler to accomplish this. However i see that the job is getting triggered only once.
What could be wrong ?
following is the XML file snippet -
<batch:job id="getFleetUpdatesJob" job-repository="jobRepository">
<batch:step id="step0">
<batch:tasklet ref="fleetUpdatesID" transaction-manager="jobRepository-transactionManager" />
</batch:step>
<!-- <batch:step id="step1" next="step2">
<batch:tasklet ref="world" transaction-manager="jobRepository-transactionManager" />
</batch:step> -->
</batch:job>
<!-- Quartz related beans START -->
<bean name="updateDataFeedJobDetail" class="org.springframework.scheduling.quartz.JobDetailBean">
<property name="jobClass" value="<package>.schedule.UpdateDataFeedJob" />
</bean>
<bean id="cronTriggerId" class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="jobDetail" ref="updateDataFeedJobDetail" />
<!-- run every morning at 3AM -->
<!-- <property name="cronExpression" value="0 0 3 * * ?" /> -->
<!-- run the job at 8pm everyday -->
<property name="cronExpression" value="0 0 20 * * ?" />
</bean>
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref bean="cronTriggerId" />
</list>
</property>
</bean>
<!-- Quartz related beans END -->
I suspect the cron expression is working correctly, however a Spring Batch job will only execute once unless it's parameters change.
You can verify this if you get a stack trace similar to:
2011-08-18 00:40:26,155 INFO [org.springframework.batch.core.launch.support.SimpleJobLauncher] - <Job: [FlowJob: [name=job1]] completed with the following parameters: [{run.id=1}] and the following status: [COMPLETED]>
2011-08-18 00:40:30,002 INFO [com.beny23.test.JobLauncherDetails] - <Quartz trigger firing with Spring Batch jobName=job1>
2011-08-18 00:40:30,015 ERROR [com.beny23.test.JobLauncherDetails] - <Could not execute job.>
org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException: A job instance already exists and is complete for parameters={run.id=1}. If you want to run this job again, change the parameters.
at org.springframework.batch.core.repository.support.SimpleJobRepository.createJobExecution(SimpleJobRepository.java:122)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
In order to ensure that the parameters for each job invokation are different, you could modify the UpdateDataFeedJob class to call your job like this:
JobParametersBuilder builder = new JobParametersBuilder();
builder.addLong("run.ts", System.currentTimeMillis());
JobParameters jobParameters = builder.toJobParameters();
Job job = jobLocator.getJob(jobName);
jobLauncher.run(job, jobParameters);
I dont now spring but i use Quartz .I think problem is ,you are giving two cron expression.You must give a single cron expression like 0 0 3-20 * * ? (some thing like that ) ,crontrigger can have only one cron expression rewrite cron expression that fire at 3 and 20 .

Configuration whie using Quartz with spring

I am currently using quartz schedular which comes with spring framework.Our requirement is to schedule a method on daily basis which will call a webservice(only one method on the webservice).My configuration is as below.
<bean id="downloadJob"
class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject" ref="adapter" />
<property name="targetMethod" value="getData" />
</bean>
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="jobDetails">
<list>
<ref bean="downloadJob" />
</list>
</property>
<property name="triggers">
<list>
<ref bean="cronTrigger" />
</list>
</property>
</bean>
<bean id="cronTrigger"
class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="jobDetail" ref="downloadJob" />
<property name="cronExpression" value="" />
</bean>
I am reading the cronExpression value from properties file.
Please provide me some pointers to implement the schedular in better way. i have seen in some other projects where only using quartz with out spring.They are taking care of thread pool and some other properties as below.I am first time working on schedular implementation.Please provide me some suggestions/pointers on how to take care of these below properties while using quartz with spring(org.springframework.scheduling.quartz.SchedulerFactoryBean).Please suggest me if i need to take care of anyother things apart from these.
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount = 15
org.quartz.threadPool.threadPriority = 5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true
That's a fine way to implement a scheduler in Spring. The Spring reference has a whole section on Quartz integration that should help get you started. For setting Quartz properties, use the SchedulerFactoryBean's quartzProperties property. You'll have to decide yourself if there's anything else to take care of by reading up on Quartz in general and learning more about Quartz configuration.

making Quartz scheduler persistent without JDBC

We're building an app around MongoDB, and have a need to run cron-like jobs periodically. I've used Quartz before for this kind of thing when projects were based around an RDBMS with JDBC.
Since we're already using MongoDB for our main datastore in this project, I'd prefer to not introduce an RDBMS simply to persist Quartz jobs, but there doesn't seem to be any kind of JobStore implementatiom for MongoDB.
Can anyone recommend either a way to back Quartz with MongoDB, or a simple alternative to Quartz? My needs are fairly simple (run various java jobs with some manner of configuration, à la cron).
Edit: Latest implementation https://github.com/michaelklishin/quartz-mongodb forked from below repo
I wrote a MongoDB JobStore for Quartz which is located here: https://github.com/mulesoft/quartz-mongodb It doesn't support everything, but it works for a bunch of use cases.
We run quartz with Spring and it's just an XML file with the jobs defined and cron expressions.
Declare a job in Spring:
<bean name="myJob" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="concurrent" value="false"/>
<property name="targetBeanName" value="myBean"/>
<property name="targetMethod" value="myScheduledMethod"/>
</bean>
<bean id="myJobTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="jobDetail" ref="myJob"/>
<!-- every 30s -->
<property name="cronExpression" value="0/30 * * * * ?"/>
</bean>
Quartz Wiring:
<bean id="schedulerFactoryBean" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<!-- List of batch jobs to be fed to the scheduler. -->
<list>
<ref bean="myTrigger"/>
</list>
</property>
</bean>
Run it with:
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App
{
public static void main( String[] args ) throws Exception
{
new ClassPathXmlApplicationContext("jobs-context.xml");
}
}

Categories