Timing quartz task execution duration - java

Is there a standard way of logging the time Quartz takes to execute a task? I'm open to Spring based solutions as well, as I'm using both.

You can use a general purpose timing library, such as ERMA. It integrates very nicely with spring.

You could implement a JobListener and add it as a global job listener to the scheduler.
This will give you some hooks on when a job is about to be executed and when it has finished executing. You should be able to do something with these hooks to record the start and end times of the jobs, and then log the run time.

You can use a JobListener for that. The execution time of a job is even provided by the context. The Listener need to be added to the used Scheduler:
public class GlobalJobRuntimeListener implements JobListener {
//... other methods to overwrite
#Override
public void jobWasExecuted(JobExecutionContext context, JobExecutionException jobException) {
System.out.println("Job with key " + context.getJobDetail().getKey().toString()
+ " finished in " + context.getJobRunTime() + " ms.");
}
}
-
Scheduler scheduler = schedulerFactory.getScheduler();
scheduler.getListenerManager().addJobListener(new GlobalJobRuntimeListener());
scheduler.start();

Related

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

how to suspend job in quartz scheduler?

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

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

Guice + Quartz + iBatis

I'm trying to wire together Guice (Java), Quartz scheduler and iBatis (iBaGuice) to do the following:
Start command line utility-scanner using main()
Periodically scan directory (provided as argument) for files containing formatted output (XML or YAML)
When file is detected, parse and output result to the database
The problems:
I used this example to wire Guice and Quartz. However I'm missing some important details which I'm asking in the comments but the post is somewhat dated so I'm quoting it here also:
It's not obvious how to set-up the scheduler. Where and how would I wire the Trigger (I can use Trigger#makeMinutelyTrigger)?
I really have just one type of job I will be executing, I understand that details in the JobFactory#newJob are coming from the TriggerFiredBundle parameter but where/how do I wire that? And where/how do I create or wire concrete Job?
P.S. I got a little bit further by creating and wiring ScheduleProvider. Now I'm stuck with how to actually schedule the Job in this following snippet. It seams that my JobFactory#newJob method is never called
public class CollectorServiceImpl implements CollectorService {
Scheduler scheduler;
/**
* #throws SchedulerException
*/
#Inject
public CollectorServiceImpl(final SchedulerFactory factory, final GuiceJobFactory jobFactory)
throws SchedulerException {
scheduler = factory.getScheduler();
scheduler.setJobFactory(jobFactory);
}
/**
* #throws SchedulerException
* #see teradata.quantum.reporting.collector.service.CollectorService#start()
*/
#Override
public void start() throws SchedulerException {
Trigger trigger = TriggerUtils.makeMinutelyTrigger("MIN_TRIGGER");
scheduler.scheduleJob(trigger); // this fails trigger validation since no job name is provided
scheduler.start();
}
}
core to your problem is, you don´t actually schedule a job class:
getScheduler().scheduleJob(new JobDetail("myFooJob", null, FooJob.class),
TriggerUtils.makeMinutelyTrigger("MIN_TRIGGER"));
full answer & demo code on http://www.codesmell.org/blog/2009/01/quartz-fits/
Do you really need scheduling, or just execution of repeating tasks at fixed intervals?
If the later, have a look at java build in ExecutorService, especially the ScheduledThreadPoolExecutor. Saves a whole framework for something quite simple :)

Categories