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

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

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 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>

Spring + Quartz resheduling or updating the trigger?

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.

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

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