how to suspend job in quartz scheduler? - java

Hi i am creating an application that executes the method of a class based on a cron expression. For that i am using spring quartz in which i have to configure all these stuffs in my spring-file it works fine and jobs are executing based on the cron expression, But now i want to suspend the next execution of particular job in java class based on the choice of a user from UI. Then is there any way to do this ??
can i get the details of all running job it the context ? if so then i can filter the jobs and try to suspend that job for next execution.

Inject your SchedulerFactoryBean. Use it's getScheduler method to obtain a quartz Scheduler and use rescheduleJob or other methods from quartz API to perform your task.

I got it work by use of following code
stdScheduler is a scheduleFactoryBean
String groupnames[] = stdScheduler.getJobGroupNames();
for (String groupName : groupnames) {
String[] jobNames = stdScheduler.getJobNames(groupName);
for (String jobName : jobNames) {
if (jobName.equals("jobName")) {
stdScheduler.pauseJob(jobName, groupName);
}
}
}

We can use the stdScheduler.pauseJob(JobKey jobKey) method to reduce the number of loops mentioned in the code above

If you got hold of the SchedulerFactoryBean by injection or somehow else there are the convenience methods:
schedulerFactoryBean.getScheduler().standby();
schedulerFactoryBean.getScheduler().start();
When using Quartz directly also this works:
#Autowired
private Scheduler scheduler;
...
scheduler.standby();
...
scheduler.start();

Related

Start Quartz Scheduler without firing the triggers

I have got q requirement where I need to start the scheduler but the existing triggers should not run. I have spring boot application, I want to implement a case where I will start the application but it should not run the existing triggers into the system.
It can be any flag or parameter base. Is there any way to achieve this? I have been checking on web related to this but could not find anything related to this.
Possibly what you can do, create the scheduler instance, but do not set autostart to true. Then application will be started, but no triggers will be executed.
The code fragment is on kotlin, but it should be similar on java
#Bean
open fun quartzScheduler(springBeanJobFactory: SpringBeanJobFactory): SchedulerFactoryBean {
val quartzSchedulerFactory = SchedulerFactoryBean()
quartzSchedulerFactory.setJobFactory(springBeanJobFactory)
quartzSchedulerFactory.setConfigLocation(ClassPathResource("quartz.properties"))
quartzSchedulerFactory.setOverwriteExistingJobs(true)
quartzSchedulerFactory.isAutoStartup = false
return quartzSchedulerFactory
}
Then you can create some simple controller to start the scheduler
schedulerFactory.scheduler.start()
Variation written in Java and using application properties
#Bean
#Inject
SchedulerFactoryBeanCustomizer schedulerCustomizer(
#Value("${spring.quartz.properties.org.quartz.scheduler.instanceName:schedulerFactoryBean}") String schedulerName,
#Value("${spring.quartz.properties.org.quartz.scheduler.enabled:true}") boolean enabled) {
return (schedulerFactoryBean) -> {
schedulerFactoryBean.setOverwriteExistingJobs(true);
schedulerFactoryBean.setBeanName(schedulerName);
schedulerFactoryBean.setAutoStartup(enabled);
};
}

Update Cron expression in SpringBoot #Scheduled

I have around 10 jobs scheduled with #Scheduled and a hardcoded cron expression like this:
#Scheduled(cron = "* * 1 * * *")
public void testMethod(){
doSomething();
}
Now i want to be able to through the database update this cron expression and reschedule the specific job in runtime.
Does anyone knows how to do this?
Thanks
If you want to configure the scheduling of job at runtime, I don't think you can use the annotation #Scheduled.
You can use your own scheduler instead from Spring documentation :
scheduler.schedule(task, new CronTrigger("0 15 9-17 * * MON-FRI"));
Then, if you want to change the configuration, you can cancel the scheduling and create a new one.
TaskScheduler return a ScheduledFuture that you should save somewhere and it can be cancelled with cancel(...) method.
I think that #Scheduled no support this feature (must be interesting implement that). For advance scheduling feature you need to use quartz or other scheduler solution. My answer is based on Quartz Solution:
#Component
class ReschedulerComponent{
#Autowired
private SchedulerFactoryBean schedulerFactoryBean;
public void reSchedule(){
Trigger oldTriger = schedulerFactoryBean.getScheduler().getTrigger("my_custom_trigger");
Trigger myNewTrigger = TriggerBuilder
.newTrigger()
.forJob(jobDetail) // Name of your job
.withIdentity("my_custom_trigger")
.startAt(myNewDATE)
.withSchedule(SimpleScheduleBuilder.simpleSchedule().withMisfireHandlingInstructionFireNow())
.build();
schedulerFactoryBean.getScheduler().rescheduleJob(oldTriger.getKey(), myNewTrigger);
}
}
Quick introduction: https://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/html/scheduling.html
This can be done by specifying the cron expression in your property place holder as mentioned below. Add below code in #configuration class.
#Bean
public PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
PropertySourcesPlaceholderConfigurer properties = new PropertySourcesPlaceholderConfigurer();
properties.setLocation(new ClassPathResource("test.properties"));
return properties;
}
Now test.properties will be available in your placeholder. Test.properties shown below
variable.name.inside.properties= 00 39 05 * * *
Then inside your scheduler class add
#Scheduled(cron = "${variable.name.inside.properties}")
public void testMethod(){
doSomething();
}
I think you should look at this resource
You can programmatically create scheduled jobs. So if you annotate your method with #PostConstruct it should pick it when the application starts and run at the scheduled time
https://www.programcreek.com/java-api-examples/index.php?api=org.quartz.impl.triggers.SimpleTriggerImpl
http://www.quartz-scheduler.org/api/2.2.1/org/quartz/impl/triggers/SimpleTriggerImpl.html
If you want to configure schedule of the job, so that you don't need to change the code, I suggest you to extract value in property stored in some configuration.properties, then access to it in code with #Value.
UPD: found this topic, maybe you find it useful
Spring Scheduler change cron expression dynamically

java scheduler spring vs quartz

Currently I am building a spring standalone program in order to learn new methods and architectures.
The last few days I tried to learn scheduler. I never used them before so I read some articles handling the different possible methods. Two of them are especially interesting: The spring nativ #Scheduler and Quartz.
From what I read, Spring is a little bit smaller then Quartz and much more basic. And quartz is not easy to use with spring (because of the autowired and components).
My problem now is, that there is one thing I do not understand:
From my understanding, both methods are creating parallel Threads in order to asynchronously run the jobs. But what if I now have a spring #Service in my main Application, that is holding a HashMap with some information. The data is updated and changed with user interaction. Parallel there are the scheduler. And a scheduler now whants to use this HashMap from the main application as well. Is this even possible?
Or do I understand something wrong? Because there is also the #Async annotation and I did not understand the difference. Because a scheduler itself is already parallel to the main corpus, isn't it?
(summing up, two questions:
can a job that is executed every five seconds, implemented with a scheduler, use a HashMap out of a service inside the main program? (in spring #Scheduler and/or in Quartz?)
Why is there a #Async annotation. Isn't a scheduler already parallel to the main process?
)
I have to make a few assumptions about which version of Spring you're using but as you're in the process of learning, I would assume that you're using spring-boot or a fairly new version, so please excuse if the annotations don't match your version of Spring. This said, to answer your two questions the best I can:
can a job that is executed every five seconds, implemented with a scheduler, use a HashMap out of a service inside the main program? (in spring #Scheduler and/or in Quartz?)
Yes, absolutely! The easiest way is to make sure that the hashmap in question is declared as static. To access the hashmap from the scheduled job, simply either autowire your service class or create a static get function for the hashmap.
Here is an example of a recent Vaadin project where I needed a scheduled message sent to a set of subscribers.
SchedulerConfig.class
#Configuration
#EnableAsync
#EnableScheduling
public class SchedulerConfig {
#Scheduled(fixedDelay=5000)
public void refreshVaadinUIs() {
Broadcaster.broadcast(
new BroadcastMessage(
BroadcastMessageType.AUTO_REFRESH_LIST
)
);
}
}
Broadcaster.class
public class Broadcaster implements Serializable {
private static final long serialVersionUID = 3540459607283346649L;
private static ExecutorService executorService = Executors.newSingleThreadExecutor();
private static LinkedList<BroadcastListener> listeners = new LinkedList<BroadcastListener>();
public interface BroadcastListener {
void receiveBroadcast(BroadcastMessage message);
}
public static synchronized void register(BroadcastListener listener) {
listeners.add(listener);
}
public static synchronized void unregister(BroadcastListener listener) {
listeners.remove(listener);
}
public static synchronized void broadcast(final BroadcastMessage message) {
for (final BroadcastListener listener: listeners)
executorService.execute(new Runnable() {
#Override
public void run() {
listener.receiveBroadcast(message);
}
});
}
}
Why is there a #Async annotation. Isn't a scheduler already parallel to the main process?
Yes, the scheduler is running in its own thread but what occurs to the scheduler on long running tasks (ie: doing a SOAP call to a remote server that takes a very long time to complete)?
The #Async annotation isn't required for scheduling but if you have a long running function being invoked by the scheduler, it becomes quite important.
This annotation is used to take a specific task and request to Spring's TaskExecutor to execute it on its own thread instead of the current thread. The #Async annotation causes the function to immediately return but execution will be later made by the TaskExecutor.
This said, without the #EnableAsync or #Async annotation, the functions you call will hold up the TaskScheduler as they will be executed on the same thread. On a long running operation, this would cause the scheduler to be held up and unable to execute any other scheduled functions until it returns.
I would suggest a read of Spring's Documentation about Task Execution and Scheduling It provides a great explanation of the TaskScheduler and TaskExecutor in Spring

How do I get a list of scheduled quartz job in spring?

I am having some trouble figuring out how to get the list of scheduled jobs. I have the jobs, triggers, and scheduler declared as beans in my servlet-context.xml file. The job fires correctly and completes. I know I am suppose to use the scheduleFactory to grab a scheduler and do something like:
for(String group: sched.getJobGroupNames()) {
// enumerate each job in group
for(JobKey jobKey : sched.getJobKeys(groupEquals(group))) {
System.out.println("Found job identified by: " + jobKey);
}
Though, I do not know how to get the scheduleFactory instance from the bean since I am not suppose to be creating a new one. I have gathered all this info from searching many forums including here.
Please help me regarding this.
You have to inject the scheduleFactory in your class (using #Autowired if you use annotations).

Running a Job only once Using Quartz

Is there a way I could run a job only once using Quartz in Java? I understand it does not make sense to use Quartz in this case. But, the thing is, I have multiple jobs and they are run multiple times. So, I am using Quartz.
Is this even possible?
You should use SimpleTrigger that fires at specific time and without repeating. TriggerUtils has many handy methods for creating these kind of things.
Yes, it's possible!
JobKey jobKey = new JobKey("testJob");
JobDetail job = newJob(TestJob.class)
.withIdentity(jobKey)
.storeDurably()
.build();
scheduler.addJob(job, true);
scheduler.triggerJob(jobKey); //trigger a job inmediately
In quartz > 2.0, you can get the scheduler to unschedule any job after work is done:
#Override
protected void execute(JobExecutionContext context)
throws JobExecutionException {
...
// process execution
...
context.getScheduler().unscheduleJob(triggerKey);
...
}
where triggerKey is the ID of the job to run only once. After this, the job wouldn't be called anymore.
Here is an example of how to run a TestJob class immediately with Quartz 2.x:
public JobKey runJob(String jobName)
{
// if you don't call startAt() then the current time (immediately) is assumed.
Trigger runOnceTrigger = TriggerBuilder.newTrigger().build();
JobKey jobKey = new JobKey(jobName);
JobDetail job = JobBuilder.newJob(TestJob.class).withIdentity(jobKey).build();
scheduler.scheduleJob(job, runOnceTrigger);
return jobKey;
}
see also Quartz Enterprise Job Scheduler Tutorials → SimpleTriggers
I'm not sure how much similar is Quartz in Mono and Java but this seems working in .Net
TriggerBuilder.Create ()
.StartNow ()
.Build ();
I had to ask myself if it made sense to try to configure a job and add checks if it had been run already as suggested in Marko Lahma's answer (since scheduling a job to run once results in it being run once, every time we start the app). I found examples of CommandLineRunner apps which didn't quite work for me, mostly because we already had an ApplicationRunner which was used for other jobs which use Quartz scheduling / cron. I wasn't happy with having Quartz initialize this job using a SimpleTrigger, so I had to find something else.
Using some ideas from the following articles:
Multiple Spring boot CommandLineRunner based on command line argument
Run Spring Batch Job programmatically?
Firing Quartz jobs manually
Is there any way to get job keys in Quartz by job name
How to list all Jobs in the Quartz Scheduler
Spring Boot CommandLineRunner and ApplicationRunner
I was able to piece together a working implementation which allows me to do the following:
run existing jobs via Quartz, on a timer
run new job, one time programmatically (single use Quartz job using the SimpleTrigger didn't satisfy my requirements, since it would be run once on every application load)
I came up with the following CommandLineRunner class:
public class BatchCommandLineRunner implements CommandLineRunner {
#Autowired
private Scheduler scheduler;
private static final Logger LOGGER = LoggerFactory.getLogger(BatchCommandLineRunner.class);
public void run(final String... args) throws SchedulerException {
LOGGER.info("BatchCommandLineRunner: running with args -> " + Arrays.toString(args));
for (final String jobName : args) {
final JobKey jobKey = findJobKey(jobName);
if (jobKey != null) {
LOGGER.info("Triggering job for: " + jobName);
scheduler.triggerJob(jobKey);
} else {
LOGGER.info("No job found for jobName: " + jobName);
}
}
}
private JobKey findJobKey(final String jobNameToFind) throws SchedulerException {
for (final JobKey jobKey : scheduler.getJobKeys(GroupMatcher.jobGroupEquals("DEFAULT"))) {
final String jobName = jobKey.getName();
if (jobName.equals(jobNameToFind)) {
return jobKey;
}
}
return null;
}
}
In one of my configuration classes I added a CommandLineRunner bean which calls the custom CommandLineRunner I created:
#Configuration
public class BatchConfiguration {
private static final Logger LOGGER = LoggerFactory.getLogger(BatchConfiguration.class);
#Bean
public BatchCommandLineRunner batchCommandLineRunner() {
return new BatchCommandLineRunner();
}
#Bean
public CommandLineRunner runCommandLineArgs(final ApplicationArguments applicationArguments) throws Exception {
final List<String> jobNames = applicationArguments.getOptionValues("jobName");
LOGGER.info("runCommandLineArgs: running the following jobs -> " + ArrayUtils.toString(jobNames));
batchCommandLineRunner().run(jobNames.toArray(ArrayUtils.EMPTY_STRING_ARRAY));
return null;
}
}
Later, I am able to initiate these jobs via the CLI without affecting my current Quartz scheduled jobs, and as long as no one runs the command via CLI multiple times, it will never be run again. I have to do some juggling of types since I accept ApplicationArguments, and then convert them into String[].
Finally, I am able to call it like this:
java -jar <your_application>.jar --jobName=<QuartzRegisteredJobDetailFactoryBean>
The result is that the job is initialized only when I call it, and it is excluded from my CronTriggerFactoryBean triggers which I used for my other jobs.
There are several assumptions being made here, so I'll try to summarize:
the job must be registered as a JobDetailFactoryBean (e.g.: scheduler.setJobDetails(...))
everything is essentially the same as a job with CronTriggerFactoryBean, excepting the lacking scheduler.setTriggers(...) call
Spring knows to execute the CommandLineRunner classes after the application has booted
I hardcoded the parameter being passed into the application to "jobName"
I assumed a group name of "DEFAULT" for all jobs; if you want to use differing groups this would need to be adjusted when fetching JobKey, which is used to actually run the job
there is nothing which prevents this job from being run multiple times via CLI, but it was triggered on every application load using SimpleTrigger approach, so this is better for me; if this is not acceptable, perhaps using StepListener and ExitStatus, etc. can prevent it from being executed twice
Another solution: There is a method .withRepeatCount(0) in SimpleSchedulerBuilder:
public final int TEN_SECONDS = 10;
Trigger trigger = newTrigger()
.withIdentity("myJob", "myJobGroup")
.startAt(new Date(System.currentMillis()+TEN_SECONDS*1000)
.withSchedule(SimpleScheduleBuilder.simpleSchedule()
.withRepeatCount(0)
.withIntervalInMinutes(1))
.build();

Categories