Spring + Quartz resheduling or updating the trigger? - java

I am using Spring 3.2 and Quartz 2.2.
My target Class and method,
public class Producer {
public void executeListener() {
System.out.println(" Test Method . ");
}
}
In my spring applicationContext.xml ,
<bean id="producer" class="com.service.Producer" />
<bean id="jobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject" ref="producer" />
<property name="targetMethod" value="executeListener" />
<property name="concurrent" value="false" />
</bean>
<bean id="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean">
<property name="jobDetail" ref="jobDetail" />
<!-- 10 seconds -->
<property name="startDelay" value="10000" />
<!-- repeat every 5 seconds -->
<property name="repeatInterval" value="5000" />
</bean>
<bean id="mySheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<!-- <ref bean="cronTrigger" /> -->
<ref bean="simpleTrigger" />
</list>
</property>
</bean>
The above code is working and the executeListener() nethod is executing continously with the interval of 5 seconds and the starting delay will be 10 sec.
I have tried in spring controller to update the trigger as ,
#Controller
public class SpringController {
#Autowired
org.springframework.scheduling.quartz.SchedulerFactoryBean myScheduler;
#Autowired
org.springframework.scheduling.quartz.SimpleTriggerFactoryBean oldTrigger;
#RequestMapping(value="/reSheduleTrigger.html",method=RequestMethod.POST)
public #ResponseBody String reSheduleTrigger(#RequestParam(value="triggerInSec") String triggerInSec ){
System.out.println("----------------- Changing the repeat interval request -----------");
// obtain a builder that would produce the trigger
TriggerBuilder<SimpleTrigger> tb = oldTrigger.getObject().getTriggerBuilder();
Trigger newTrigger = tb.withSchedule(simpleSchedule().withIntervalInSeconds(triggerInSec).build());
myScheduler.getObject().rescheduleJob(oldTrigger.getObject().getKey(),newTrigger );
return "success";
}
}
But I am getting the compile time error Trigger newTrigger = tb.withSchedule(simpleSchedule().withIntervalInSeconds(triggerInSec).build()); .
My need :
I want to reshedule the job to execute the executeListener() method with the 2 sec interval dynamically.
Thanks.

Try one of the following:
Trigger newTrigger = tb.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(2)).build();
or
Trigger newTrigger = tb.withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(2)).build();
Both are syntactically valid. It looks like your issue with Trigger newTrigger = tb.withSchedule(simpleSchedule().withIntervalInSeconds(triggerInSec).build()); is caused by incorrect bracketing. ie build() should not have an extra bracket after it, as it returns a trigger.

Related

How do I set priorities quartz triggers in Spring MVC

I use Quartz scheduler with cron expressions for few tasks to be executed on a regular intervals in my Java Spring MVC application. In my root-context.xml file, I have the following:
<!--Quartz Scheduler Beans -->
<bean id="emailNotificationJob"
class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject" ref="eventsService" />
<property name="targetMethod" value="sendEventEmailNotification" />
<property name="concurrent" value="false" /> <!-- this is the property to prevent concurrent execution -->
</bean>
<bean id="deleteWebContentsJob"
class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject" ref="webContentDefinitionService" />
<property name="targetMethod" value="deleteWebContents" />
<property name="concurrent" value="false" />
</bean>
<bean id="saveStaticContentsJob"
class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject" ref="webContentDefinitionService" />
<property name="targetMethod" value="saveStaticContents" />
<property name="concurrent" value="false" />
</bean>
<bean id="emailTrigger"
class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<property name="jobDetail" ref="emailNotificationJob" />
<property name="cronExpression" value="0 0/1 * 1/1 * ? *" />
</bean>
<bean id="deleteWebContentsTrigger"
class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<property name="jobDetail" ref="deleteWebContentsJob"/>
<property name="cronExpression" value="0 1 0 1/1 * ? *" />
</bean>
<bean id="staticContentsUploadTrigger"
class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<property name="jobDetail" ref="saveStaticContentsJob" />
<property name="cronExpression" value="0 0/1 * 1/1 * ? *" />
</bean>
<!-- Scheduler factory bean to glue together jobDetails and triggers to Configure Quartz Scheduler -->
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="jobDetails">
<list>
<ref bean="emailNotificationJob" />
<ref bean="deleteWebContentsJob" />
<ref bean="saveStaticContentsJob" />
</list>
</property>
<property name="triggers">
<list>
<ref bean="emailTrigger" />
<ref bean="deleteWebContentsTrigger" />
<ref bean="staticContentsUploadTrigger" />
</list>
</property>
</bean>
<!-- Quartz Bean End -->
I use Quartz Scheduler 2.2.1 in my application.
I want to set priorities for various triggers. Here I have two triggers that are set to fire at same time intervals. I am trying to set one of them as priority 1 and the other one as priority 2. Is there a way to do that.
CronTriggerFactoryBean has a property called priority (spring-context-support:4.2.4.RELEASE). This is in turn used to set the priority of the CronTriggerImpl, which has a javadoc that states:
The priority of a Trigger acts as a tie breaker such that if two Triggers have the same scheduled fire time, then Quartz will do its best to give the one with the higher priority first access to a worker thread.
If not explicitly set, the default value is 5.
Then again, if there are enough threads in the thread pool for both processes, I'm not sure if the priority will have any effect (depending on whether the priority is also assigned to the actual worker thread).

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

How to get the number of entries written to DB in Spring Batch?

I have a JobExecutionListener that writes the job status after execution into a log file. How can I also get the number of items processed?
#Component
public class JobListener implements JobExecutionListener {
#Override
public void afterJob(JobExecution task) {
log.info(task.getStatus());
//number of entries??
}
}
Using the JobExplorer, you can look at the previous StepExecutions to get the number of items read, etc in each step. You can read more about the JobExplorer here: http://docs.spring.io/spring-batch/trunk/apidocs/org/springframework/batch/core/explore/JobExplorer.html
Update
You actually don't even need to use the JobExplorer. Since you have the JobExecution, you already have references to all the StepExecutions. Each StepExecution contains the number of items read, processed, written, skipped, etc.
I see 2 ways to do it.
You can implement the ItemProcessListener. This interface is called after/before an item is processed. This interface also reported any errors.
public class ItemCountsListener implements ItemProcessListener<Object, Object> {
private static final AtomicLong count = new AtomicLong(1);
public void afterProcess(Object item, Object result) {
count.getAndIncrement();
}
public void beforeProcess(Object item) {}
public void onProcessError(Object item, Exception e) { }
}
Or you can call the method jobExecution.getStepExecutions(). This method returns a Collection<StepExecution> object. In this class, there is a method getWriteCount which returns the current number of items written for this execution.
I would do something similar to this code :
public void afterJob(JobExecution jobExecution) {
int nbItemsProcessed;
for (StepExecution stepExecution : jobExecution.getStepExecutions()) {
nbItemsProcessed += stepExecution.getWriteCount();
}
}
this is how i did it:
JobExecution job = jobLauncher.run(praepJob, getJobParameters());
job.getStepExecutions().stream().findFirst().get().getWriteCount()
Two ways,
Use JobExecution, here you can get access to StepExecution and thereby can get step read, write or errors
Spring Batch stores all its job and step data in batch tables. You can refer the docs here : Spring Batch Meta-Data
If you are using Meta-data tables, then you can configure jobExplorer that provides you to get the meta-data data.
Using xml you can configure it as,
<bean id="jobOperator"
class="org.springframework.batch.core.launch.support.SimpleJobOperator">
<property name="jobRegistry" ref="jobRegistry" />
<property name="jobExplorer" ref="jobExplorer" />
<property name="jobLauncher" ref="jobLauncher" />
<property name="jobRepository" ref="jobRepository" />
</bean>
<bean id="jobRegistry" class="org.springframework.batch.core.configuration.support.MapJobRegistry" />
<bean id="jobExplorer"
class="org.springframework.batch.core.explore.support.JobExplorerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="tablePrefix" value="BATCH_" />
</bean>
<bean id="jobLauncher"
class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
<property name="jobRepository" ref="jobRepository" />
<property name="taskExecutor" ref="batchTaskExecutor"></property>
</bean>
<bean id="jobRepository"
class="org.springframework.batch.core.repository.support.JobRepositoryFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="databaseType" value="oracle" />
<property name="transactionManager" ref="transactionManager" />
</bean>
<bean id="batchTaskExecutor"
class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
<property name="corePoolSize" value="10" />
<property name="maxPoolSize" value="25" />
<property name="queueCapacity" value="30" />
</bean>

Why is our Quartz job executing 15 minutes after its corresponding Trigger fires?

We've recently rolled out a new Java/Spring/Quartz/MyBatis stack to handle our large, data-intensive jobs. Mostly it's been working well, but we've noticed something interesting.
We have a pair of similar jobs that run once a day, a half hour apart. Each performs the same business logic, executing over its own discreet type of users in our database. The jobs have run successfully every day since we released them to production (about a week or so ago). But on two separate occasions, our logs have shown that the trigger had fired right on time, but the actual job didn't execute until 15 minutes later. Once it was the first trigger/job that exhibited this behavior, and once it was the second.
Our logs are written out by the GlobalTriggerListener and GlobalJobListener, as well as periodically by the business logic performed by the job itself, and they all indicate that, on those days, the job ran 15 minutes after the trigger fired. There are no indications whatsoever of any errors occurring around that time.
Below is from our spring/quartz context XML file:
<bean name="AnnualXXXRenewals" class="org.springframework.scheduling.quartz.JobDetailBean">
<property name="jobClass" value="com.aaa.cron.base.TaskFactory" />
<property name="jobDataAsMap">
<map>
<entry key="taskClassName" value="com.aaa.cron.tasks.AnnualRenewalTask" />
<entry key="arg.mode" value="xxx" />
</map>
</property>
</bean>
<bean name="AnnualYYYRenewals" class="org.springframework.scheduling.quartz.JobDetailBean">
<property name="jobClass" value="com.aaa.cron.base.TaskFactory" />
<property name="jobDataAsMap">
<map>
<entry key="taskClassName" value="com.aaa.cron.tasks.AnnualRenewalTask" />
<entry key="arg.mode" value="yyy" />
</map>
</property>
</bean>
<!-- Daily Trigger 8 PM PST (3AM GMT)-->
<bean id="dailyTrigger0800PM" class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="jobDetail" ref="AnnualXXXRenewals"/>
<property name="cronExpression" value="0 0 13 ? * * *"/>
</bean>
<!-- Daily Trigger 8:30 PM PST (3:30AM GMT)-->
<bean id="dailyTrigger0830PM" class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="jobDetail" ref="AnnualYYYRenewals"/>
<property name="cronExpression" value="0 30 13 ? * * *"/>
</bean>
<bean id="schedulerFactory"
class="com.aaa.wrapper.spring.UpdatingSchedulerFactoryBean"
lazy-init="false">
<property name="configLocation"
value="classpath:com/aaa/config/quartz.properties" />
<property name="autoStartup" value="true" />
<property name="applicationContextSchedulerContextKey" value="applicationContext" />
<property name="overwriteExistingJobs" value="true" />
<property name="dataSource">
<ref bean="dataSource" />
</property>
<property name="transactionManager">
<ref bean="transactionManager" />
</property>
<property name="jobFactory">
<bean class="com.aaa.wrapper.spring.AutowiringSpringBeanJobFactory" />
</property>
<property name="jobDetails">
<list>
<ref bean="AnnualXXXRenewals" />
<ref bean="AnnualYYYRenewals" />
</list>
</property>
<property name="triggers">
<list>
<ref bean="dailyTrigger0800PM" />
<ref bean="dailyTrigger0830PM" />
</list>
</property>
</bean>
And below is from our quartz.properties:
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.scheduler.skipUpdateCheck = true
#org.quartz.jobStore.useProperties=true
org.quartz.jobStore.tablePrefix = QRTZ_
org.quartz.jobStore.isClustered = true
# Vendor-specific; MySql uses StdJDBCDelegate
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
# Needed to manage cluster instances
org.quartz.scheduler.instanceId=AUTO
org.quartz.scheduler.instanceName=AAA_SCHEDULER
org.quartz.scheduler.jmx.export=true
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount = 2
org.quartz.threadPool.threadPriority = 5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true
# Job and Trigger Listener
org.quartz.jobListener.GlobalJobListener.class = com.aaa.cron.base.GlobalJobListener
org.quartz.jobListener.GlobalJobListener.name = GlobalJobListener
org.quartz.triggerListener.GlobalTriggerListener.class = com.aaa.cron.base.GlobalTriggerListener
org.quartz.triggerListener.GlobalTriggerListener.name = GlobalTriggerListener
Other info: Our server is set to UTC time. The jobs have been taking a matter of seconds to run. The jobs are clustered, currently across 5 Tomcat instances.
Has anyone run into anything like this before? So far it's not caused any problems, but it would be good to figure out what's going on.

Null pointer exception when querying database from a Quartz job

My scheduler is triggering but I am not able to connect to the database. When I tried to query the database using a test case it worked so I tried to implement it using Quartz, but it's giving a NullPointerException.
public class JobScheduler extends QuartzJobBean {
#Autowired
ISourceService sourcedao;
#Override
protected void executeInternal(JobExecutionContext arg0)
throws JobExecutionException {
Client client = new Client();
client.setClientKey(300);
Source sourceobj = sourcedao.getSourceByClient(client);
String sourcetype = sourceobj.getSourceType();
System.out.println(sourcetype);
}
}
my application context.xml
<bean id="jobScheduler" class="org.springframework.scheduling.quartz.JobDetailBean">
<property name="jobClass" value="com.dca.scheduling.JobScheduler" />
<property name="jobDataAsMap">
<map>
<entry key="timeout" value="5" />
</map>
</property>
</bean>
<bean id="cronTriggerjobScheduler" class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="jobDetail" ref="jobScheduler" />
<property name="cronExpression" value="0/15 0 * * * ?" />
</bean>
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="jobDetails">
<list>
<ref bean="jobScheduler" />
</list>
</property>
<property name="triggers">
<list>
<ref bean="cronTriggerjobScheduler" />
</list>
</property>
</bean>
<bean id="jobClass"
class="com.dca.scheduling.JobScheduler">
</bean>
I checked many examples but didn't get any ideas.
The JobScheduler needs to be a Spring bean, too. You don't show how you annotate it. I would make it a Component and see if you fare better.
in the application context i have added a map
<bean id="jobScheduler" class="org.springframework.scheduling.quartz.JobDetailBean">
<property name="jobClass" value="com.dca.scheduling.JobScheduler" />
<property name="jobDataAsMap">
<map>
<entry key ="DATA_MANAGER_MAP_KEY" value-ref="sourceDao"/>**i have added the bean id here**
<entry key="timeout" value="5" />
</map>
</property>
</bean>
and in the Jobscheduler class
sourceDao= (SourceDaoImpl)jobContext.getJobDetail().getJobDataMap().get("DATA_MANAGER_MAP_KEY");

Categories