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);
Related
I have following configuration to run task--
<bean id="trendDataJob" class="com.ge.og.realtrack.scheduler.TrendDataJob"> </bean>
<task:scheduled-tasks>
<task:scheduled ref="trendDataJob" method="trendJob" cron="#{trendDataJob.configMap['corn_exp']}"></task:scheduled>
<task:scheduled ref="trendDataJob" method="metaDataTrendJob" cron="#{trendDataJob.configMap['metadata_corn_exp']}"></task:scheduled>
</task:scheduled-tasks>
cron expression for this is corn_exp=0 0/1 * * * ? to run every minute.
Here is problem as both method of trendDataJob schedule to run every minute but they are executing one after another first trendJob once its completed then its executing metaDataTrendJob i am not able to understand this behavior .
Also another problem is in case of method takes more than one minute to finish finish..its not triggering next call till current call finish and return.
By default the scheduler uses a ConcurrentTaskScheduler with a single thread. If you want another one configure it and pass it to the scheduled-tasks scheduler attribute.
The easiest way, in XML, is to use the scheduler element. (See this section in the reference guide).
<task:scheduler id="scheduler" pool-size="10"/>
Then simply register it on the other element.
<task:scheduled-tasks scheduler="scheduler"> ...
Have you used #EnableScheduling in your java code?
#EnableScheduling ensures that a background task executor is created. Without it, nothing gets scheduled.
For more, you can go through
Spring 3 #Scheduled – 4 Ways to Schedule Tasks
Spring Batch + Spring TaskScheduler example
Scheduling Tasks
Enable scheduling annotations
To enable support for #Scheduled and #Async annotations add #EnableScheduling and #EnableAsync to one of your #Configuration classes:
#Configuration
#EnableAsync
#EnableScheduling
public class AppConfig {
}
You are free to pick and choose the relevant annotations for your application. For example, if you only need support for #Scheduled, simply omit #EnableAsync. For more fine-grained control you can additionally implement the SchedulingConfigurer and/or AsyncConfigurer interfaces. See the javadocs for full details.
If you prefer XML configuration use the <task:annotation-driven> element.
<task:annotation-driven executor="myExecutor" scheduler="myScheduler"/>
<task:executor id="myExecutor" pool-size="5"/>
<task:scheduler id="myScheduler" pool-size="10"/>
Notice with the above XML that an executor reference is provided for
handling those tasks that correspond to methods with the #Async
annotation, and the scheduler reference is provided for managing those
methods annotated with #Scheduled.
If you're using a default task scheduler in spring, i'm pretty sure it only runs on a single thread, hence why you cannot make them run in parallel.
You need to configure some kind of BatchScheduler with a pool size, to make it run in parallel.
I'm a bit stuck migrating to latest quartz 2.2 and spring 4.1... Here's a cron trigger, I omit the job and other fluff for clarity:
...
<bean id="timeSyncTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<property name="jobDetail" ref="timeSyncJob"/>
<property name="startDelay" value="10000"/>
<property name="cronExpression" value="0 0 1 * * ? *"/>
</bean>
...
Now, I need to change its cronExpression at run time, and it's not as simple as I thought. I can't reference the bean and change the property because its a factory giving CronTrigger interface which in turn doesn't have setCronExpression method any longer, it has become immutable. Before I could simply fish out a trigger from the context and set its new cron expression. It worked very well for many years, until the upgrade become unavoidable.
So, how do we accomplish this simple task today? Totally lost in documentations and versions.. Thanks in advance!
In addition to the CronTriggerFactoryBean you probably have a SchedulerFactoryBean, which provides access to the Quartz scheduler as well as the CronTrigger. The Quartz scheduler allows you to reschedule a job with a new/modified trigger:
#Autowired private SchedulerFactoryBean schedulerFactoryBean;
...
public void rescheduleCronJob() {
String newCronExpression = "..."; // the desired cron expression
Scheduler scheduler = schedulerFactoryBean.getScheduler();
TriggerKey triggerKey = new TriggerKey("timeSyncTrigger");
CronTriggerImpl trigger = (CronTriggerImpl) scheduler.getTrigger(triggerKey);
trigger.setCronExpression(newCronExpression );
scheduler.rescheduleJob(triggerKey, trigger);
}
Will the CronTriggerFactoryBean.setCronExpression() method work?
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
I need to configure a scheduling algorithm that is beyond the capabilities of Spring's in-build scheduling (basically "every 5 minutes, but only between 4:00h and 16:00h"). It seems that implementing the org.springframework.scheduling.Trigger interface is the way to go, which seems simple enough.
The part I can't figure out and that doesn't seem to be answered in the documentation is: how does this mix with the XML configuration? There doesn't seem to be any way of specifying a custom trigger bean in the elements of the task namespace (apart from the Quartz example).
How do I use a custom trigger in a Spring 3 application? Ideally using the Bean XML configuration.
Take a look at DurationTrigger I wrote a year ago.
public class DurationTrigger implements Trigger {
/**
* <p> Create a trigger with the given period, start and end time that define a time window that a task will be
* scheduled within.</p>
*/
public DurationTrigger( Date startTime, Date endTime, long period ) {...}
// ...
}
Here is how you would schedule such a task with this trigger:
Trigger trigger = new DurationTrigger( startTime, endTime, period );
ScheduledFuture task = taskScheduler.schedule( packageDeliveryTask, trigger );
Alternatively, you can use a CronTrigger / cron expression:
<!-- Fire every minute starting at 2:00 PM and ending at 2:05 PM, every day -->
<task:scheduled-tasks>
<task:scheduled ref="simpleProcessor" method="process" cron="0 0-5 14 * * ?"/>
</task:scheduled-tasks>
Check out this JIRA as well as this Spring Integration article
EDIT:
From the JIRA discussion, you can configure the DurationTrigger above, or any other custom trigger for that matter, using Spring Integration:
<inbound-channel-adapter id="yourChannelAdapter"
channel="yourChannel">
<poller trigger="durationTrigger"/>
</inbound-channel-adapter>
<beans:bean id="durationTrigger" class="org.gitpod.scheduler.trigger.DurationTrigger">
<beans:constructor-arg value="${start.time}"/>
<beans:constructor-arg value="${end.time}"/>
<beans:constructor-arg value="${period}"/>
</beans:bean>
It is quite simple to use Spring Integration in your project, even if you did not plan to. You can use as little as the above scheduling piece, or as much as relying on many other Enterprise Integration patterns that Spring Integration has available.
It seems using XML to configure any but the two standard triggers is not possible in Spring 3.0. It has been added as a new feature in the 3.1M2 release, though: https://jira.springsource.org/browse/SPR-8205
Thanks to Mark Fisher for pointing this out.
I have a problem where in I need to receive a series of messages from an MQ queue and write this to a file and initiate a spring batch job with the file as input. Right now I'm thinking of launching the job with wired #Autowired JobLauncher jobLauncher and #Autowired Job job; from the MDB itself.But I feel this is not a good approach as spring batch may create a series of threads and EJB as such doesnt support multi threading.
Is there any other effective way to do this ? I dont want to use quartz scheduler or anything else since it adds complexity. Is there any interface in spring batch itself which launches a job soon after a file comes in a directory ? Any leads in doing this better would be appreciated.
Thanks.
I have a problem where in I need to receive a series of messages from an MQ queue and write this to a file and initiate a spring batch job with the file as input
One way to do that would be engage a bit of Spring Integration, where you would have a file poller, that would poll for a new file:
<file:inbound-channel-adapter id="filePoller"
channel="filesAreComing"
directory="file:${input.directory}"
filename-pattern="test*" />
Adapt a file message ( java.io.File ) to a file name ( String ), since that is what Spring Batch needs. This can be done with a JobLauncher adapter, that is already available from Spring Batch Admin here:
#ServiceActivator
public JobLaunchRequest adapt(File file) throws NoSuchJobException {
JobParameters jobParameters = new JobParametersBuilder().addString(
"input.file", file.getAbsolutePath()).toJobParameters();
return new JobLaunchRequest(job, jobParameters);
}
wrap it to a JobLaunchRequest ( which is just a holder for a Job and JobParameters ) and send this request [as a message] to JobLaunchingMessageHandler:
<service-activator input-channel="jobLauncher">
<beans:bean class="org.springframework.batch.integration.launch.JobLaunchingMessageHandler">
<beans:constructor-arg ref="jobLauncher" />
</beans:bean>
</service-activator>
that would launch the job.
"input.file" is a parameter that is bound at runtime ( hence #{...} ):
<bean id="reader" class="org.springframework.batch.item.file.FlatFileItemReader" scope="step">
<property name="resource" value="#{jobParameters[input.file]}" />
... line mapper and other props
</bean>
I'm not sure I understand why you have a message queue, a message-driven POJO/EJB, AND a batch job.
One way to do it is to have the message driven POJO/EJB do the work. It's already an asynch process. You can pool the message driven beans so there are sufficient workers to handle the load. Why add complexity?
If you'd rather not do that, forget the queue and use Spring Batch on its own. I wouldn't do both.