making Quartz scheduler persistent without JDBC - java

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");
}
}

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

Quartz schedulers in two different clusters for the same application are firing at same time

i am using Quartz scheduler to pushing automatic emails daily specific time. My application was configured in two clusters. Schedulers in both the clusters are firing at same time and sending duplicate emails to users. Please suggest me the code to make sure that only one scheduler will fire.
I have done googling and found that JDBC-JobStore will resolve the issue.But i dont want to store schedule informaltion in db. Will RAMJobStore will resolve the issue? below is my existng code.
<bean id="scheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref bean="cronTrigger" />
</list>
</property>
</bean>
<bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="jobDetail" ref="jobDetails" />
<property name="cronExpression" value="0 51 10 * * ?"/>
</bean>
<bean id="jobDetails"
class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject" ref="sendEmails" />
<property name="targetMethod" value="executeJob" />
<property name="concurrent" value="false" />
</bean>
<bean id="sendEmails" class="com.westin.agi.PushNotification"></bean>
Usually the RAM of each server is separated, so the behaviour that both cluster members have their scheduler fire at the same time is expected.
If you do not want to use a database for synchronization, you can use a memory grid solution like Hazelcast.
Actually there is a project to achieve exactly your use case with Hazelcast and Quartz:
https://github.com/mufumbo/quartz-hazelcast

Dynamically Scheduled Jobs with Quartz

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>

Spring - Switch SchedulerFactoryBean To Be Used

I'm using Spring's SchedulerFactoryBean to run some Quartz jobs within a Spring based java application. At present, this is a single instance application in development, but as soon as we start horizontally scaling this we will want to use a jdbc based JobStore for Quartz so no more than one app will run a given job.
Right now, SchedulerFactoryBean is configured as follows:
<bean id="schedulerFactoryBean" class="org.springframework.scheduling.quartz.SchedulerFactoryBean" >
<property name="taskExecutor" ref="taskExecutor"/>
<property name="triggers">
<list>
<!-- a bunch of triggers here -->
</list>
<property name="applicationContextSchedulerContextKey">
<value>applicationContext</value>
</property>
</bean>
and with using a jdbc based JobStore it will look like this
<bean id="schedulerFactoryBean" class="org.springframework.scheduling.quartz.SchedulerFactoryBean" >
<property name="dataSource" ref="mysqlJobDataSource"/>
<property name="taskExecutor" ref="taskExecutor"/>
<property name="triggers">
<list>
<!-- a bunch of triggers here -->
</list>
</property>
<property name="applicationContextSchedulerContextKey">
<value>applicationContext</value>
</property>
<property name="quartzProperties">
<props>
<prop key="org.quartz.jobStore.class">org.quartz.impl.jdbcjobstore.JobStoreTX</prop>
<prop key="org.quartz.jobStore.driverDelegateClass">org.quartz.impl.jdbcjobstore.StdJDBCDelegate</prop>
<!-- and a bunch of other quartz props -->
</props>
</property>
</bean>
Ideally, I'd like to continue using the default RAMJobStore version (the first one) for developers, but use the jdbc version for deployed environments. However, there doesn't seem to be a very good way to switch between the two through something like a property, since the jdbc store involves lots more configuration and the mere existence of the dataSource property on SchedulerFactoryBean means it tries to a JDBC based job store.
Also, Since SchedulerFactoryBean is an initializing bean where the initializing basically starts running all of the jobs, so I can't have both of those beans defined in a config file loaded into the spring context either, which means I'll have parallel jobs running.
I've also read through this answer, but this situtation differs in that I'm dealing with two InitializingBeans that should never be in the same context at the same time.
What would be the simplest way to configure switching between these two configurations of SchedulerFactoryBean?
From Spring 3.1 you can use Spring profiles:
<bean name="schedulerFactoryBean" profile="dev" ...
<bean name="schedulerFactoryBean" profile="prd" ...
Then you can instruct Spring container which profile to use, see How to set active spring 3.1 environment profile via a properites file and not via an env variable or system property and Spring autowire a stubbed service - duplicate bean.
If you can't use 3.1 or profiles, the old-school of solving such issues is to have two context files: schedulerContext-dev.xml and schedulerContext-prd.xml`. Then you can import them selectively:
<import resource="schedulerContext-${some.property}"/>
A better option would be using quartz properties file. As part of your release you can have different files per environment. The context that way is the same for all the environments, the only thing that changes is the configuration file. Using maven profiles you can solve it

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.

Categories