Quartz - Shutdown Scheduler based on Job Variable - java

I am using Quartz to schedule a job in Java.
The scheduler repeats indefinitely.
If a certain exception occurs in the Job.class, I need to shutdown the scheduler.
How can I get the Job to notify the scheduling class to shutdown()?
Thanks,
RayK

Quartz's jobs are run by calling their execute(JobExecutionContext) method. This JobExecutionContext object has a getScheduler() method to access the Scheduler that is running your job.
So you could just do:
class YourJob implements Job {
//...
#Override
public void execute(final JobExecutionContext context) throws JobExecutionException {
//...
if (someCondition) {
try {
context.getScheduler().shutdown();
} catch (SchedulerException e) {
// log or rethrow!
}
}
//...
However, I'm not really sure if shutting the Scheduler down in the middle of running a job is a good idea. Why do you want to do that? If all you're trying to do is to stop some job from running again, it may be better to just unschedule it:
Unscheduling a Particular Trigger of Job:
// Unschedule a particular trigger from the job (a job may have more than one trigger)
scheduler.unscheduleJob(triggerKey("trigger1", "group1"));
Deleting a Job and Unscheduling All of Its Triggers
scheduler.deleteJob(jobKey("job1", "group1"));

Related

Asynchronous task within scheduler in Java Spring

I currently have a scheduled task within my Spring application.
However a two parts of this logic is severely time-consuming and I am wondering if there would be a way to make these two parts asynchronous so that it does not interfere with the time of the logic being executed.
The logic that I need to execute as follows.
#Scheduled(fixedDelay = 10000)
public void startAuction() throws Exception {
List<SchGoodsAuctionStartListRes> list = schedulerService.schGoodsAuctionStartList();
for (SchGoodsAuctionStartListRes item : list) {
schedulerService.schGoodsAuctionStart(item);
// 1st time consuming block that needs async
PushInfo pushInfo = pushMapper.pushGoodsSeller(item.getGoodsIdx());
pushInfo.setTitle("Start");
pushInfo.setBody("[" + pushInfo.getBrand() + "] started.");
pushInfo.setPushGrp("001");
pushInfo.setPushCode("003");
fcmPushUtil.sendPush(pushInfo);
// 2nd time consuming block that needs async
List<PushInfo> pushInfos = pushMapper.pushGoodsAuctionAll(item.getIdx());
for (PushInfo pushInfoItem : pushInfos) {
pushInfoItem.setTitle("\uD83D\uDD14 open");
pushInfoItem.setBody("[" + pushInfo.getBrand() + "] started. \uD83D\uDC5C");
pushInfoItem.setPushGrp("002");
pushInfoItem.setPushCode("008");
fcmPushUtil.sendPush(pushInfoItem);
}
}
}
From my understanding, a scheduler already is executing logic asynchronously, and I wonder if there would be any way of making those two blocks asynchronous so that it does not cause delays when executing this logic.
Any sort of advice or feedback would be deeply appreciated!
Thank you in advance!
There are several approaches that you could take here.
Configuring Thread Pool executor for Spring's scheduled tasks
By default Spring uses single thread executor to execute scheduled tasks, which means that even if you have multiple #Scheduled tasks or another execution for a task triggers before the previous one is completed, they will all have to wait in the queue.
You can configure your own executor to be used by Spring Scheduling. Take a look at the documentation of #EnableScheduling, it is pretty exhaustive on the subject.
To configure ExecutorService to be used for scheduled tasks it is enough to define a bean:
#Bean
public TaskScheduler taskScheduler() {
ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
threadPoolTaskScheduler.setPoolSize(8);
threadPoolTaskScheduler.setThreadNamePrefix("task-scheduler");
return threadPoolTaskScheduler;
}
Additionally, if you use Spring Boot, you can use properties file:
spring.task.scheduling.pool.size=8
Executing scheduled tasks asynchronously
To execute scheduled tasks asynchronously you can use Spring's #Async annotation (and make sure to #EnableAsync somewhere in your configuration. That will make your tasks to be executed on a background thread, freeing the scheduling thread.
#EnableAsync
public class ScheduledAsyncTask {
#Async
#Scheduled(fixedRate = 10000)
public void scheduleFixedRateTaskAsync() throws InterruptedException {
// your task logic ...
}
}
Offload expensive parts of your tasks to a different executor
Finally, you could use a separate ExecutorService and run expensive parts of your tasks using that executor instead of the one used for task scheduling. This will keep the time needed to complete the execution on the thread used by Spring to schedule tasks to a minimum, allowing it to start next executions.
public class ScheduledAsyncTask implements DisposableBean {
private final ExecutorService executorService = Executors.newFixedThreadPool(4);
#Scheduled(fixedRate = 10000)
public void scheduleFixedRateTaskAsync() throws InterruptedException {
executorService.submit(() -> {
// Expensive calculations ...
});
}
#Override
public void destroy() {
executorService.shutdown();
}
}

Use Quartz Scheduler in IBM Domino Application

I'm very new to Quartz, but know 3 simple things which you have to have in order to make it work.
These are jobs, triggers and scheduler.
Now, in our domino application we have to use it for refreshing a token.
I've created 3 basic classes for it.
The job:
public class RefreshEGRZTokenJob implements Job
{
public void execute(JobExecutionContext arg0) throws JobExecutionException
{
System.out.println("stub for refreshing a token");
}
}
The trigger and something like main:
public class RefreshEGRZTokenExecutor
{
private static String REFRESH_TOKEN_JOB = "refreshTokenJob";
public static void executeAndScheduleRefreshToken(int timeInSeconds) throws SchedulerException
{
JobDetail job = JobBuilder.newJob(RefreshEGRZTokenJob.class)
.withIdentity(REFRESH_TOKEN_JOB).build();
Trigger trigger = TriggerBuilder
.newTrigger()
.withIdentity(REFRESH_TOKEN_JOB)
.withSchedule(
SimpleScheduleBuilder.simpleSchedule()
.withIntervalInSeconds(timeInSeconds).repeatForever())
.build();
QuartzScheduler.getInstance().scheduleJob(job, trigger);
}
public static void pauseScheduler() throws SchedulerException
{
QuartzScheduler.getInstance().standby();
}
}
And the scheduler:
public final class QuartzScheduler
{
private static Scheduler quartzSchedulerInstance;
public static Scheduler getInstance() throws SchedulerException
{
if (quartzSchedulerInstance == null)
{
Scheduler scheduler = new StdSchedulerFactory().getScheduler();
scheduler.start();
quartzSchedulerInstance = scheduler;
}
return quartzSchedulerInstance;
}
}
The call I make is from a button (in production it'll execute shortly after the user authorized)
<xp:eventHandler event="onclick" submit="true"
refreshMode="complete">
<xp:this.action><![CDATA[#{javascript:
ru.lanit.egrz.scheduler.RefreshEGRZTokenExecutor.executeAndScheduleRefreshToken(30);
}]]>
</xp:this.action>
</xp:eventHandler>
Well, quartz scheduler is initilized and the job is set but doesn't execute the job (I know this because if I press the same button twice, it'll give me an exeption that the job already exists).
I guess Domino's JVM doesn't let the scheduler run indefinitely.
The reason why I don't use standard IBM's agent is simple - it doesn't allow to use Java code in Code section. You have to either import and duplicate everything you have so far or to compile it into jar and import. But if you decide to change anything in your sources you'll have to recompile the entire jar (with new source code) and re-import that.
Has anybody integrated Domino JVM and Quartz?
If so, please tell me the best practices and how to make it work.
Thanks in advance.
I have created a plugin, you can find it here: https://github.com/hasselbach/domino-quartz
The feature project and the updatesite project are missing.
You have to install the plugin on the server and in the DDE and activate it in your XPages application.
You have a set of hurdles to overcome here:
Your scheduler needs to run "forever", so you need to run it from where it doesn't die. That place is an OSGi plugin. The place for your scheduler would be the activator class. something along those lines:
import java.util.logging.Logger;
import org.eclipse.core.runtime.Plugin;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
public class Activator extends Plugin implements BundleActivator {
#Override
public void start(final BundleContext context) throws Exception {
// Here you start your scheduler
}
}
You need to call it from somewhere (and that shouldn't be SSJS). So you would create a managed bean that can access the plugins activator. Put the bean code into the plug-in. Once you have defined it you can call the bean method in your button
Your scheduler runs a token refresh. What is supposed to happen with the refreshed token?
Having said all this. You probably can get away with a much simpler solution (unless your token needs millisecond precision):
Create a managed bean for the session context (Each bean is individually instantiated per user).
In the bean have a method you will call from your button (or elsewhere) that kicks of a new thread. That thread sleeps for a while before you execute again. Check for a property of being shut down, so you can terminate gracefully.
Hope that helps

How to avoid relaunching task while it's status is still running on Spring Cloud Data Flow

I have deployed a Spring Batch project as a task on Spring Cloud Data Flow.
I have launched the task for the first time (It was going to run about 5 minutes which made the status of task is "running"). However, I have relaunched this task for the second time when it's status is running and it worked again.
1) How can I configure that the task could not be relaunched until it's completed status for the last time.
2) Besides, I found that when stopping the job, it will execute until it finishes the current step(My job has 2 steps and the second step was not executed ). However, are there any ways to shut down it (step 1) immediately when I stop the job?
How to avoid relaunching task while it's status is still running on Spring Cloud Data Flow
Spring Cloud Task 2.0.0 added a feature that allows you to prevent tasks from running concurrently. See https://spring.io/blog/2018/05/07/spring-cloud-task-2-0-0-release-is-now-available#restricting-concurrent-task-execution
You need to activate the spring.cloud.task.singleInstanceEnabled=true property.
You can find more details in the Restricting Spring Cloud Task Instances section.
You can use JobExplorer class for checking running jobs, see jobExplorer.findRunningJobExecutions(jobName) method.
In your JobLauncher class you can something like that:
#Component
public class MyJobLauncher {
private static final Logger LOG = LoggerFactory.getLogger(MyJobLauncher.class);
private final Job job;
private final JobLauncher jobLauncher;
private final JobExplorer jobExplorer;
#Autowired
public MyJobLauncher(#Qualifier("myJob") Job job, JobLauncher jobLauncher, JobExplorer jobExplorer) {
this.job = job;
this.jobLauncher = jobLauncher;
this.jobExplorer = jobExplorer;
}
public void launchJob() throws JobParametersInvalidException, JobExecutionAlreadyRunningException,
JobRestartException, JobInstanceAlreadyCompleteException, IOException {
if (jobExplorer.findRunningJobExecutions("myJob").isEmpty()) {
LOG.info("Starting myJob");
jobLauncher.run(job, new JobParameters());
LOG.info("Stopping myJob");
}
}
}
For shutting down the job, check JobExecution.stop() method which can be fetched from jobExplorer.getJobExecution() method.

Time limit for Spring Batch Job [duplicate]

Is there a way to limit the lifetime of a running spring-batch job to e.g. 23 hours?
We start a batch job daily by a cron job and he job takes about 9 hours. It happened under some circumstances that the DB connection was so slow that the job took over 60 hours to complete. The problem is that the next job instance gets started by the cronjob the next day - and then anotherone the day after - and anotherone...
If this job is not finished within e.g. 23 hours, I want to terminate it and return an error. Is there a way to do that out-of-the-box with spring-batch?
Using a StepListener you can stop a job by calling StepExecution#setTerminateOnly.
stepBuilderFactory
...
.writer(writer)
.listener(timeoutListener)
...
.build()
And the TimeoutListener could look like this
#Component
public class TimeoutListener implements StepListener {
private StepExecution stepExecution;
#BeforeStep
public void beforeStep(StepExecution stepExecution) {
this.stepExecution = stepExecution;
}
#BeforeRead
public void beforeRead() {
if (jobShouldStop()) {
stepExecution.setTerminateOnly();
}
}
private boolean jobShouldStop() {
// ...
}
}
This will gracefully stop the job, without forcefully terminate any running steps.
Spring Batch specifically avoids the issue of job orchestration which this falls into. That being said, you could add a listener to your job that checks for other instances running and calls stop on them before beginning that one. Not knowing what each job does, I'm not sure how effective that would be, but it should be a start.
If you write your own job class to launch the process you can make your class implement StatefulJob interface, which prevents concurrent launches of the same job. Apart from that you can write your own monitoring and stop the job programatically after some period, but it will require some custom coding, I dont know if there is anything build-in for such use case.

Spring schedule graceful shutdown not working when using a cron scheduled

I have a small standalone application that configures the scheduler to terminate gracefully. With the following configuration:
#Bean
public TaskScheduler taskScheduler() {
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setWaitForTasksToCompleteOnShutdown(true);
scheduler.setAwaitTerminationSeconds(60);
return scheduler;
}
I can get it to gracefully terminate the scheduler, but only if I don't have any #Scheduled(cron = ) task. Once I have one of those, no matter what the scheduler will get stuck until timeout. I already tried configuring it also with an executor and do the shutdown/await manually and the effect is exactly the same.
These cron jobs are not even running. They are set to run at a fixed time during the night for example.
Spring version: 4.2.8.RELEASE
This will happen when the timeout reaches the end:
2017.07.28 01:44:56 [Thread-3] WARN Timed out while waiting for executor 'taskScheduler' to terminate
Any thoughts?
Because by default the ScheduledThreadPoolExecutor will wait for all delayed scheduled tasks to finish executing, even if scheduled tasks aren't running at that time.
Try this below:
#Bean
public TaskScheduler taskScheduler() {
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler() {
private static final long serialVersionUID = -1L;
#Override
public void destroy() {
this.getScheduledThreadPoolExecutor().setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
super.destroy();
}
};
scheduler.setWaitForTasksToCompleteOnShutdown(true);
scheduler.setAwaitTerminationSeconds(60);
return scheduler;
}
Then the ScheduledThreadPoolExecutor will only wait for scheduled tasks which are currently running to finish executing.
Possible bug in that version of Spring? Refer to jira.spring.io/browse/SPR-15067

Categories

Resources