Below the code which is executed through the cron and from this class, I'm calling my configuration class for manipulating the messages. while manipulating the message I want to interrupt this job through the rest API's.
public class CronJob extends QuartzJobBean implements InterruptableJob
{
private volatile boolean toStopFlag = true;
#Autowired
#Lazy
SchedulerFactoryBean schedulerFactoryBean;
#Autowired
MessageConfiguration messageConfiguration;
#Override
public void executeInternal(JobExecutionContext jobExecutionContext)
throws JobExecutionException
{
Scheduler scheduler = schedulerFactoryBean.getScheduler();
messageConfiguration.excuteMessageConfiguration(jobExecutionContext,
scheduler);
}
#Override
public void interrupt() throws UnableToInterruptJobException
{
System.out.println("Stopping thread... ");
toStopFlag = false;
}
}
************** Trying to interrupt the job*******
Here I'm trying to interrupt the job through the Rest API. Passing a job name statically.
public void interruptJob() {
List<JobExecutionContext> currentlyExecuting = scheduler.getCurrentlyExecutingJobs();
for (JobExecutionContext jobExecutionContext : currentlyExecuting) {
if(jobExecutionContext.getJobDetail().getKey().getName().equals("**JobName**")){
Object result = scheduler.interrupt(jobExecutionContext.getJobDetail().getKey());
System.out.println("stoppeed");
}
}
}
The issue is interrupt method is executed but the job is running continuously.
Helping hand will be appreciated.
Related
I am writing a java/spring library to include in other projects that are using quartz.
I need it to log some information about the task/calling class everytime a job is executed.
For example, if a quartz job looks like this:
#Bean
public JobDetail jobADetail() {
return JobBuilder.newJob(QuartzTaskA.class)
.withIdentity("sampleJobA")
.storeDurably().build();
}
#Bean
public Trigger jobATrigger(JobDetail jobADetail) {
return TriggerBuilder.newTrigger()
.forJob(jobADetail)
.withIdentity("sampleTriggerA")
.withSchedule(CronScheduleBuilder.cronSchedule("0/10 * * ? * * *"))
.build();
}
public class QuartzTaskA implements Job {
#Override
public void execute(JobExecutionContext jobExecutionContext) {
log.info("QuartzA - the time is now {}", dateFormat.format(new Date()));
}
}
I want it to log something like:
Job [QuartzTaskA] with details [sampleJobA] and trigger [sampleTriggerA] is starting
QuartzA - the time is now 12:07:39
I customize the SchedulerFactoryBean with a custom TaskExecutor that does a log before the task.run().
This works and I am able print the additional first line, but I can't figure out how to get the details/context to pass into the log.
#Configuration
public class SchedulerFactoryCustomizer implements SchedulerFactoryBeanCustomizer {
private static final Logger log = LogManager.getLogger(SchedulerFactoryCustomizer.class);
#Override
public void customize(SchedulerFactoryBean schedulerFactoryBean) {
Executor executor = SchedulerFactoryBean.getConfigTimeTaskExecutor();
schedulerFactoryBean.setTaskExecutor(new CustomExecutor(executor);
}
private static class CustomExecutor implements Executor {
final Executor executor;
private CustomExecutor(Executor executor) {
this.executor = executor;
}
#Override
public void execute(Runnable task) {
// This line here. How can I get the details/context to pass in?
//log.info("Job {} with details {} and trigger {} is starting");
task.run();
}
}
}
how can I get the details/context to pass into the log?
You can implement a JobListener/TriggerListener
public class LoggingTriggerListener implements TriggerListener {
#Override
public String getName() {
return null;
}
#Override
public void triggerFired(final Trigger trigger, final JobExecutionContext context) {
}
#Override
public boolean vetoJobExecution(final Trigger trigger, final JobExecutionContext context) {
return false;
}
#Override
public void triggerMisfired(final Trigger trigger) {
}
#Override
public void triggerComplete(final Trigger trigger, final JobExecutionContext context, final Trigger.CompletedExecutionInstruction triggerInstructionCode) {
}
}
It injects jobExecutionContext also.
I have two APIs: one starts the thread, and another stops the thread. I'm successfully able to start a thread by calling /start API, but I'm unable to stop already running thread by calling /stop API. Seems like Executor#stop() does nothing.
My RestController:
#Autowired
private Executor executor;
#RequestMapping(path = "/start", method = GET)
public ResponseEntity<HttpStatus> startLongTask() {
executor.start();
return ResponseEntity.ok(HttpStatus.OK);
}
#RequestMapping(path = "/stop", method = GET)
public ResponseEntity<HttpStatus> stopLongTask() {
executor.stop();
return ResponseEntity.ok(HttpStatus.OK);
}
My Executor:
#Component
public class Executor {
#Value("${threads.number}")
private int threadsNumber;
private ExecutorService executorService;
#Autowired
private OtherService otherService;
#PostConstruct
private void init() {
executorService = Executors.newFixedThreadPool(threadsNumber);
executorService = Executors.newScheduledThreadPool(threadsNumber);
}
/**
* Start.
*/
public void start() {
executorService.submit(() -> otherService.methodImExecuting());
}
/**
* Stop.
*/
#PreDestroy
publicvoid stop() {
executorService.shutdownNow();
try {
if (!executorService.awaitTermination(800, TimeUnit.MILLISECONDS)) {
executorService.shutdownNow();
}
} catch (InterruptedException e) {
executorService.shutdownNow();
}
}
}
Here's the methodImExecuting:
#Component
public class OtherService {
public void methodImExecuting() {
List<SomeObject> dataList = repository.getDataThatNeedsToBeFilled();
for (SomeObject someObject : dataList) {
gatewayService.sendDataToOtherResourceViaHttp(someObject);
}
}
}
Short answer: You can not stop a running thread which does not cooperate. There's a deprecated destroy() method for threads, but this will lead to a "bad" state of your VM.
The only possibility to end the Thread clean is to interrupt it. But to check for interruption is the task of the thread itself.
So your methodImExcecuting sould look like:
void methodImExecuting() throws InterruptedException {
// it depends on your implementation, I assume here that you iterate
// over a collection for example
int loopCount = 0;
for (Foo foo : foos) {
++loopCount;
if (loopCount % 100 == 0) {
if (Thread.interrupted())
throw new InterruptedException();
}
...
}
It depends on your implementation how often you have to look if your thread was interrupted. But it's a fact that the call of executorService.shutdownNow(); will only set the interrupted flag of all threads currently running in the executorService. To really interrupt the thread, the thread must itself check if the interrupted flag is set and then throw an InterruptedException
Your running threads have to react to the interrupt signal
Thread.currentThread().isInterrupted()
Otherwise the sending of the interrupt signal has no effect.
Here you can find a good explanation:
Difference between shutdown and shutdownNow of Executor Service
Quartz is creating new instance of the class through the JobBuilder each time
JobBuilder.newJob(MyJob.class)
However, I only want one MyJob instance, and only trigger testFunction from execute function, how can I make it work?
I find through QuartzGuiceLib I can use some annotations to make it happen, through Spring I can change something in configuration file. But how can I implement it by pure Java without any framwork?
Below is the code snapshot:
public class MyJob implements Job {
public MyJob() {
testFunction();
try {
final Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
if (!scheduler.checkExists(JOB_KEY)) {
triggerScheduler(scheduler);
} else {
log.info("Job with key {{}} already exist", JOB_KEY);
}
} catch (SchedulerException e) {
log.error("Fail to start scheduler", e);
}
}
public void testFunction() {
}
private void triggerScheduler(final Scheduler scheduler) throws SchedulerException {
final JobDetail jobDetail = JobBuilder.newJob(MyJob.class)
.withIdentity(JOB_KEY)
.build();
final Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("myjob")
.withSchedule(
simpleSchedule()
.withIntervalInSeconds(60)
.repeatForever())
.build();
scheduler.start();
log.info("Scheduling job with key {{}}", jobDetail.getKey());
scheduler.scheduleJob(jobDetail, trigger);
}
#Override
public void execute(JobExecutionContext context) throws JobExecutionException {
testFunction();
}
}
It might be easier to keep the job and scheduler in two separate classes as below:
public class MyQuartzScheduler {
public static void main( String[] args ) throws Exception {
JobDetail job = JobBuilder.newJob(MyJob.class)
.withIdentity("dummyJobName", "group1").build();
Trigger trigger = TriggerBuilder
.newTrigger()
.withIdentity("MyJobTrigger", "group1")
.withSchedule(
CronScheduleBuilder.cronSchedule("0 * * * * ?"))
.build();
Scheduler scheduler = new StdSchedulerFactory().getScheduler();
scheduler.start();
scheduler.scheduleJob(job, trigger);
}
}
And then your Job Class:
public class MyJob implements Job {
public void testFunction() {
System.out.println("Running Test!");
}
#Override
public void execute(JobExecutionContext context) throws JobExecutionException {
testFunction();
}
}
This is an adaptation taken from an mkyong tutorial article found at:
https://www.mkyong.com/java/quartz-2-scheduler-tutorial/
For the answer to your question though, Quartz does create a new instance per run:
https://stackoverflow.com/a/10463309/1410671
You could make another static class or Factory that your Job class would call which would use the same instance every call.
public class MyJob implements Job {
public void testFunction() {
MyClassWithStaticCounter.increaseCounter(1);
System.out.println(MyClassWithStaticCounter.getCounter());
}
#Override
public void execute(JobExecutionContext context) throws JobExecutionException {
testFunction();
}
}
And your class that has the static stuff:
public class MyClassWithStaticCounter {
private static int counter = 0;
public static void increaseCounter(int i){
counter += i;
}
public static int getCounter(){
return counter;
}
}
I'm using the below snippet in Spring to schedule job executions. A job is found by querying the database. Once a new job is found (which has not yet been scheduled), it is scheduled programmatically.
My question is, is this the way to create Runnables for task execution? Is it accessing taskScheduler in the right way or should it access it by fetching an instance from application context?
#Service
public class TimeOfDayScheduler {
#Autowired
private JobExecutor jobExecutor;
#Autowired
private TaskScheduler taskScheduler;
#Scheduled(cron = "${scheduler.interval}")
#PostConstruct
public void findNewJobs() {
// Checks the database if any new jobs should be scheduled
// ...
// If found:
scheduleJob(somejob);
// ...
}
public void scheduleJob(final Job job) {
Runnable task = new Runnable() {
#Override
public void run() {
try {
jobExecutor.execute(job);
} catch (Exception e) {
logger.error("Scheduler error", e);
}
}
};
taskScheduler.schedule(task, new CronTrigger(job.getCronPattern())
}
}
I have a Java application that makes use of a Quartz Scheduler in the guise of a SchedulerFactoryBean. The main() method gets the application context, retrieves the root bean, and commences scheduling jobs.
The issue is that the Scheduler runs in its own thread, so when the main thread is done submitting jobs, it returns and the Scheduler goes on without it. When the Scheduler is finally done (or even if you explicitly call shutdown() on it), the application just hangs there for all eternity.
I have two solutions:
Keep track of the job/trigger count, incrementing it whenever you add a job to the Scheduler. Attach a simple SchedulerListener to the Scheduler that decrements this count with every call to triggerFinalized(), and set up a while loop with a Thread.sleep() inside it that constantly checks to see if the count has hit 0. When it does, it will return up to the main() method and the application will exit normally.
Take the custom SchedulerListener from option 1, and keep track of the job count inside of it. Increment for every call to jobAdded(), and decrement for every call to triggerFinalized(). When the count hits 0, call shutdown() on the Scheduler (or not, it doesn't actually matter) and then call System.exit(0).
I have implemented both of these independently in turn, so I know they both actually function. The problem is that they are both terrible. An infinite while loop polling a value? System.exit(0)? Bleargh.
Does someone have a better way, or are these seriously my only options here?
Edit: While thinking about this on the way home, I came to the conclusion that this may be caused by the fact that I'm using SchedulerFactoryBean. This auto-starts when Spring initializes the application context - that seems to put it outside the scope of the main thread. If I went with a slightly different Scheduler that I manually initialized and called start() on in the code, would this run the Scheduler in the main thread, thus blocking it until the Scheduler completes running all jobs? Or would I still have this problem?
Edit: Son of a...http://quartz-scheduler.org/documentation/quartz-2.x/examples/Example1
To let the program have an opportunity to run the job, we then sleep for 90 seconds. The scheduler is running in the background and should fire off the job during those 90 seconds.
Apparently, that will not work, because the scheduler seems to always run in the background.
In your SchedulerListener add an object solely for synchronization and locking. Call it exitLock or something. You main thread retrieves the scheduler, sets up the listener, submits all the jobs and then just before returning executes
Object exitLock = listener.getExitLock();
synchronized (exitLock) {
exitLock.wait(); // wait unless notified to terminate
}
On every triggerFinalized() call your listener decrements the counter for pending jobs. Once all the jobs have finished executing your listener shuts the scheduler down.
if (--pendingJobs == 0)
scheduler.shutdown(); // notice, we don't notify exit from here
Once the scheduler shuts down it invokes one last callback on the listener where we notify the main thread to terminate and hence the program exits gracefully.
void schedulerShutdown() {
// scheduler has stopped
synchronized (exitLock) {
exitLock.notify(); // notify the main thread to terminate
}
}
The reason we didn't notify in triggerFinalized() when all the pending jobs were finished is that in case the scheduler was shutdown prematurely and not all the jobs were finished we would have left our main thread hanging. By notifying in response to the shutdown event we make sure our program exits successfully.
I think here can be another solution.
Key points:
When task was executed the last time context.getNextFireTime() returns null.
Scheduler.getCurrentlyExecutingJobs == 1 indicate that it is the last executed job.
So when point 1 and 2 is true we can shutdown Scheduler and call System.exit(0).
Here is the code:
Listener
public class ShutDownListenet implements JobListener {
#Override
public String getName () { return "someName"; }
#Override
public void jobToBeExecuted (JobExecutionContext context) {}
#Override
public void jobExecutionVetoed (JobExecutionContext context) {}
#Override
public void jobWasExecuted (JobExecutionContext context, JobExecutionException jobException) {
try {
if (context.getNextFireTime() == null && context.getScheduler().getCurrentlyExecutingJobs().size() == 1) {
context.getScheduler().shutdown();
System.exit(0);
}
} catch (SchedulerException e) {
e.printStackTrace();
}
}
}
Code in the main function
public static void main (String[] args) {
Trigger trigger = ...
Job job = ...
JobListener listener = new ShutDownListenet();
scheduler.getListenerManager().addJobListener(listener);
scheduler.scheduleJob(job, trigger);
}
NOTE
I do not write synchronized blocks, but I tested this code with 100 concurent jobs, it works.
Did not tested in "complex" enviroment: clusters or RMI. (behavior can be differ).
Any comments are wellcome.
If your Quartz schedules/triggers are based on the database then you program needs to be alive till you would want to stop it. This can be doable like below. The idea is hook SchedulerListener and wait in the main thread. You need to hook your own way to terminate the program gracefully which completely a different topic itself.
public static void main(String[] args) {
AnnotationConfigApplicationContext appContext = // initialize the your spring app Context
// register the shutdown hook for JVM
appContext.registerShutdownHook();
SchedulerFactoryBean schedulerFactory = appContext.getBean(SchedulerFactoryBean.class);
scheduler = schedulerFactory.getScheduler();
final Lock lock = new ReentrantLock();
final Condition waitCond = lock.newCondition();
try {
scheduler.getListenerManager().addSchedulerListener(new SchedulerListener() {
#Override
public void jobAdded(JobDetail arg0) {
}
#Override
public void jobDeleted(JobKey arg0) {
}
#Override
public void jobPaused(JobKey arg0) {
}
#Override
public void jobResumed(JobKey arg0) {
}
#Override
public void jobScheduled(Trigger arg0) {
}
#Override
public void jobUnscheduled(TriggerKey arg0) {
}
#Override
public void jobsPaused(String arg0) {
}
#Override
public void jobsResumed(String arg0) {
}
#Override
public void schedulerError(String arg0, SchedulerException arg1) {
}
#Override
public void schedulerInStandbyMode() {
}
#Override
public void schedulerShutdown() {
lock.lock();
try {
waitCond.signal();
}
finally {
lock.unlock();
}
}
#Override
public void schedulerShuttingdown() {
}
#Override
public void schedulerStarted() {
}
#Override
public void schedulerStarting() {
}
#Override
public void schedulingDataCleared() {
}
#Override
public void triggerFinalized(Trigger arg0) {
}
#Override
public void triggerPaused(TriggerKey arg0) {
}
#Override
public void triggerResumed(TriggerKey arg0) {
}
#Override
public void triggersPaused(String arg0) {
}
#Override
public void triggersResumed(String arg0) {
}
});
// start the scheduler. I set the SchedulerFactoryBean.setAutoStartup(false)
scheduler.start();
lock.lock();
try {
waitCond.await();
}
finally {
lock.unlock();
}
} finally {
scheduler.shutdown(true);
}
}
If it helps someone else. I solved this by adding a shutdown-hook that triggers on Ctrl-C or normal kill (15) from script. A new Thread is spawned and polls the getCurrentlyExecutingJobs().size() every 3 seconds and exits when jobs counter has reached zero meaning all jobs finished.
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
try {
while (jobScheduler.getScheduler().getCurrentlyExecutingJobs().size() > 0) {
Thread.sleep(3000);
}
jobScheduler.getScheduler().clear();
} catch (Exception e) {
e.printStackTrace();
}
}));
while (!scheduler.isShutdown())
{
Thread.sleep(2L * 1000L);//Choose reasonable sleep time
}