I'm sending post request to run() method class name is ScheduleGame. This class the creates the Quartz RAM based job and triggers.
#Component
public class ScheduleGame {
Logger log = LoggerFactory.getLogger(ScheduleGame.class);
public String run(String jobName, String jobGroup, Class jobClass, String jobTrigger, String cronExp) throws Exception {
try {
isJobExist(jobGroup, jobName);
log.info("------- Initializing -------------------");
// First we must get a reference to a scheduler
SchedulerFactory sf = new StdSchedulerFactory();
Scheduler sched = sf.getScheduler();
log.info("------- Initialization Complete --------");
log.info("------- Scheduling Jobs ----------------");
// job1 will only fire once at date/time "ts"
JobDetail job = newJob(jobClass).withIdentity(jobName, jobGroup).build();
CronTrigger trigger = newTrigger().withIdentity(jobTrigger, jobGroup).withSchedule(
cronSchedule(cronExp).withMisfireHandlingInstructionFireAndProceed()).build();
// schedule it to run!
Date ft = sched.scheduleJob(job, trigger);
log.info(job.getKey() + " will run at: " + ft + " and repeat: " + trigger.getNextFireTime() + " times, every "
+ trigger.getExpressionSummary() + " seconds");
sched.start();
log.info(sched.getSchedulerName());
return "Game Created and Scheduled, it will run at: " + ft;
} catch (CustomException e) {
throw new Exception(e.getMessage());
}
}
}
my job class is
#Component
public class Job implements InterruptableJob {
private static Logger _log = LoggerFactory.getLogger(Job.class);
private JobKey _jobKey = null;
#Autowired
private Results results;
/*public void setResults(Results results) {
this.results = results;
}*/
/**
* Empty constructor for job initialization
*/
public Job() {
}
/**
* <p>
* Called by the <code>{#link org.quartz.Scheduler}</code> when a
* <code>{#link org.quartz.Trigger}</code> fires that is associated with
* the <code>Job</code>.
* </p>
*
* #throws JobExecutionException if there is an exception while executing the job.
*/
public void execute(JobExecutionContext context)
throws JobExecutionException {
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
// This job simply prints out its job name and the
// date and time that it is running
_jobKey = context.getJobDetail().getKey();
_log.info("Job Name: "+ _jobKey.getName() +" Job Group: "+ _jobKey.getGroup()+" SimpleJob says: " + _jobKey + " executing at " + new Date());
results.lotteryResults(_jobKey.getName(),_jobKey.getGroup());
}
#Override
public void interrupt() throws UnableToInterruptJobException {
_log.info("---" + _jobKey + " -- INTERRUPTING --");
}
}
Exception is:
java.lang.NullPointerException: null
at agaming.casino.lottery.handler.Job.execute(Job.java:54)
at org.quartz.core.JobRunShell.run(JobRunShell.java:202)
at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:573)
.19:24:00.001 [DefaultQuartzScheduler_Worker-10] ERROR org.quartz.core.ErrorLogger - Job (SampleJob.Sample Job threw an exception.
org.quartz.SchedulerException: Job threw an unhandled exception.
at org.quartz.core.JobRunShell.run(JobRunShell.java:213)
at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:573)
Caused by: java.lang.NullPointerException: null
at agaming.casino.lottery.handler.Job.execute(Job.java:54)
at org.quartz.core.JobRunShell.run(JobRunShell.java:202)
... 1 common frames omitted
When trigger fire on scheduled time the job is running fine if there is no #Autowired followed by my service class. But I've t #Autowired my services to do other business functions on job fires.
How to #Autowired my services in Quartz job class
I'm using Spring Boot
Related
I have a spring boot java service I have to schedule to run on a particular time. I have enabled the #Enablescheduling and #Scheduled annotation and given the cron expression.
It's working fine. The scheduler is running at the expected time. But my concern is I should control the cron expression somewhere from outside my jar file. I have tried using it in property file but when packaging my property file also getting included in that.
Sample code:
#PostMapping(path = "getoktatodynamodb")
#Scheduled(cron = "0 0/5 0 * * ?")
#ApiOperation("Sync data to DynamoDB")
public FinalResponse getdatatodynamodb() {
FinalResponse finalResponse = new FinalResponse();
try {
LOGGER.info("Sync data to DynamoDB starts - " + new Date());
finalResponse = dynamodbuserService.dynamoDbSync();
} catch (MyRestTemplateException ex) {
LOGGER.error(ex.getMessage());
finalResponse.setResponseMessage(ex.getMessage());
finalResponse.setStatusCode(ex.getStatusCode().value());
} catch (Exception execption) {
LOGGER.error(execption.getMessage());
finalResponse.setResponseMessage(execption.getMessage());
finalResponse.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR.value());
} finally {
LOGGER.info("Sync data DynamoDB Ends - " + new Date());
}
return finalResponse;
}
The main intention is scheduler should be in our control whenever we need to change the time it should be configurable. No code change and restarting the scheduler for minor changes.
How should we achieve this also we would like to schedule this in linux ec2 instance? in case if we have better suggestion to achieve this kindly share it.
You can implement SchedulingConfigurer:
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/scheduling/annotation/SchedulingConfigurer.html
This DZone article shows a really good example: https://dzone.com/articles/schedulers-in-java-and-spring which I'm showing here in case the article doesn't stay permanent.
#Configuration
#EnableScheduling
public class ScheduledConfiguration implements SchedulingConfigurer {
TaskScheduler taskScheduler;
private ScheduledFuture<?> job1;
private ScheduledFuture<?> job2;
#Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
ThreadPoolTaskScheduler threadPoolTaskScheduler =new ThreadPoolTaskScheduler();
threadPoolTaskScheduler.setPoolSize(10);// Set the pool of threads
threadPoolTaskScheduler.setThreadNamePrefix("scheduler-thread");
threadPoolTaskScheduler.initialize();
job1(threadPoolTaskScheduler);// Assign the job1 to the scheduler
// Assign the job1 to the scheduler
this.taskScheduler=threadPoolTaskScheduler;// this will be used in later part of the article during refreshing the cron expression dynamically
taskRegistrar.setTaskScheduler(threadPoolTaskScheduler);
}
private void job1(TaskScheduler scheduler) {
job1 = scheduler.schedule(new Runnable() {
#Override
public void run() {
System.out.println(Thread.currentThread().getName() + " The Task1 executed at " + new Date());
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}, new Trigger() {
#Override
public Date nextExecutionTime(TriggerContext triggerContext) {
String cronExp = "0/5 * * * * ?";// Can be pulled from a db .
return new CronTrigger(cronExp).nextExecutionTime(triggerContext);
}
});
}
private void job2(TaskScheduler scheduler){
job2=scheduler.schedule(new Runnable(){
#Override
public void run() {
System.out.println(Thread.currentThread().getName()+" The Task2 executed at "+ new Date());
}
}, new Trigger(){
#Override
public Date nextExecutionTime(TriggerContext triggerContext) {
String cronExp="0/1 * * * * ?";//Can be pulled from a db . This will run every minute
return new CronTrigger(cronExp).nextExecutionTime(triggerContext);
}
});
}
}
I'm writing this question because we faced an scenario in one of the qa environments where it seems like the semaphore failed.
We have only one semaphore:
private Semaphore lock = new Semaphore(1);
It happened that one thread (Quartz job) was running, holding the lock acquired, and then, another job was triggered and got in the middle of the execution.
Both jobs acquire and then release the lock, so if the first one gets delayed, the latter has to wait for the lock to be released by the first one.
The weird part is that the latter didn't wait, it just passed through the lock.acquire()
Scenario it's not complex at all, and the code has been working since the very beginning. We weren't able to recreate it so far, I'm clueless. A glitch maybe?
I'm wondering if someone knows if there is a kind of known incompatibility between Quartz and Java Semaphores, or if Java semaphores could fail under certain scenarios
EDIT
One more detail, it's an app built on Deltaspike CDI framework
This is the Singleton for handling the lock:
import java.util.concurrent.Semaphore;
public class Lock {
private Lock() {}
private static class SingletonHolder {
public static final Lock INSTANCE = new Lock();
}
/**
* Use this method to get a reference to the singleton instance of
* {#link Lock}
*
* #return the singleton instance
*/
public static Lock getInstance() {
return SingletonHolder.INSTANCE;
}
/**
* we allow only one thread at at time
*/
private Semaphore lock = new Semaphore(1);
public void getLock() throws InterruptedException {
lock.acquire();
}
public void releaseLock() {
lock.release();
}
}
This is the first job:
#Scheduled(cronExpression = "{cronExp1}")
public class Job1 implements Job {
#Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
method1();
}
public void method1(){
long threadId = Thread.currentThread().getId();
try{
logger.debug("Thread # " + threadId + "Requesting lock...");
Lock.getInstance().getLock();
logger.debug("Thread # " + threadId + "Lock acquired.");
//...some logic
}catch (PersistenceException e) {
//.. handling exception
}catch (Exception e) {
//.. handling exception
}finally {
Lock.getInstance().releaseLock();
logger.debug("Thread # " + threadId + "Lock released.");
}
}
}
This is the second job:
#Scheduled(cronExpression = "{cronExp2}")
public class Job2 implements Job {
#Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
long threadId = Thread.currentThread().getId();
try{
logger.debug("Thread # " + threadId + "Requesting lock...");
Lock.getInstance().getLock();
logger.debug("Thread # " + threadId + "Lock acquired.");
//...some logic
}catch (PersistenceException e) {
//.. handling exception
}catch (Exception e) {
//.. handling exception
}finally {
Lock.getInstance().releaseLock();
logger.debug("Thread # " + threadId + "Lock released.");
}
}
}
As you can see, the only difference between the jobs (other than the logic), is that Job1 enters the critical zone inside method1, while Job2 does in inside the execute method
I have a scenario where I am reading data from database for a scheduled interval (for every 5 mins) and then execute them before the next run is started.
Below is the piece of code which does that. ThreadPoolTaskScheduler is injected as a bean from Spring.
How can I ensure all workflowSchedules executions are complete before I add the next set of records to the ThreadPoolTaskScheduler. The method queueAndExecuteWorkflowRuns gets called after every database read.
private void queueAndExecuteWorklowRuns(Seq<WorkflowSchedule> workflowSchedules) {
for (WorkflowSchedule workflowSchedule : workflowSchedules) {
try {
long minutes = ChronoUnit.MINUTES.between(OffsetDateTime.now(), workflowSchedule.getNextRunDate());
threadPoolTaskScheduler.scheduleWithFixedDelay(new Runnable() {
#Override
public void run() {
startPendingWorkflow(workflowSchedule);
}
},
TimeUnit.MINUTES.toMillis(minutes + 1));
} catch (Exception e) {
log.error("Exception while adding workflow to scheduled pool with id " + workflowSchedule.getId()
+ " and run date " +
workflowSchedule.getNextRunDate());
}
}
}
#Bean(name = "workflowSchedulerExecutor")
public ThreadPoolTaskScheduler threadPoolTaskScheduler() {
ThreadPoolTaskScheduler threadPoolTaskScheduler
= new ThreadPoolTaskScheduler();
threadPoolTaskScheduler.setPoolSize(1);
return threadPoolTaskScheduler;
}
I am using Quartz CronScheduler to execute a job every 15 minutes. Every time the job executes it checks the DB for any change in the cron expression and updates the job scheduler for the next run. I have implemented the above in the following way:
#Service
public class QuartzSchedulerService {
private static final Logger LOG = LoggerFactory.getLogger(QuartzSchedulerService.class);
private Scheduler scheduler;
#PostConstruct
private void init() {
try {
scheduler = StdSchedulerFactory.getDefaultScheduler();
scheduler.start();
} catch (Exception e) {
LOG.error("Unable to start quartz scheduler", e);
}
}
#PreDestroy
private void destroy() {
try {
scheduler.shutdown();
} catch (Exception e) {
LOG.error("Unable to shutdown quartz scheduler", e);
}
}
public void registerCronJob(Class<? extends Job> jobClass, String cronExpression) {
try {
String jobName = jobClass.getSimpleName();
CronScheduleBuilder cronBuilder = CronScheduleBuilder.cronSchedule(cronExpression);
JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobName).build();
CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity(jobName).withSchedule(cronBuilder).build();
scheduler.scheduleJob(jobDetail, cronTrigger);
LOG.info("Registered Cron Job:" + jobName + " " + jobDetail.getKey());
} catch (Exception e) {
LOG.error("Unable to register cron job", e);
}
}
public void updateCronSchedule(Class<? extends Job> jobClass, String cronExpression) {
try {
String jobName = jobClass.getSimpleName();
CronScheduleBuilder cronBuilder = CronScheduleBuilder.cronSchedule(cronExpression);
CronTrigger newCronTrigger = TriggerBuilder.newTrigger().withIdentity(jobName).withSchedule(cronBuilder).build();
scheduler.rescheduleJob(TriggerKey.triggerKey(jobName), newCronTrigger);
LOG.info("Updated Cron Job:" + jobName + " " + newCronTrigger.getJobKey());
LOG.info("Jobs executed: " + scheduler.getMetaData().getNumberOfJobsExecuted());
} catch (Exception e) {
LOG.error("Unable to reschedule cron job", e);
}
}
}
The class which implements the Job interface is as belows:
#Component
#DisallowConcurrentExecution
public class PropertiesReloadJob implements Job {
private static final Logger LOG = LoggerFactory.getLogger(PropertiesReloadJob.class);
#Autowired
private QuartzSchedulerService schedulerService;
#Autowired
private PropertiesService propertiesService;
public void loadAtStartup() {
load();
LOG.info("--- Registerting PropertiesReload Cron Job ---");
schedulerService.registerCronJob(PropertiesReloadJob.class, propertiesService.getCacheReloadCronExpression());
}
#Override
public void execute(JobExecutionContext context) throws JobExecutionException {
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
load();
LOG.info("--- Updating Pro Cron Job ---");
schedulerService.updateCronSchedule(PropertiesReloadJob.class, propertiesService.getCacheReloadCronExpression());
}
public void load(){
// Load properties from DB
}
The loadAtStartup() method is called during context initializtion and then after every 15 minutes the execute() method is called.
The cron expression used is: 0 0/15 * 1/1 * ? *
Now, the problem is as follows:
Lets say that the job starts at 3:00:00, it will execute as many times it can till 3:00:01, rather than executing only once.
Next the job will start at 3:15:00 and again will run as many times it can till 3:15:01.
The number of times the job executes is different every time.
I am not sure what is causing this behaviour. I have tested the cron expression with cronmaker.
Can somebody point out the error here ?
I am using JBoss5.1.x AS, EJB3.0. I am trying to add a job (using Quartz) to my deployment. I am registering a new Service, so it will init the scheduler on application deploy.
My problem is that the service never gets registered when I deploy my app.
My code:
Interface:
public interface ComponentMonitoringService
{
void create() throws Exception;
void start() throws Exception;
void stop();
void destroy();
}
Service:
#Service(objectName = "com.mirs.ecms.timer:service=ServerStartupManager")
#Management(ComponentMonitoringService.class)
public class ServerStartupManager implements ComponentMonitoringService
{
private SchedulerFactory schedulerFactory = null;
private Scheduler scheduler = null;
Logger logger = Logger.getLogger("ecms.log");
public void create() throws Exception
{
}
public void start() throws Exception
{
// Write your startup code
initScheduler();
}
private void initScheduler() throws ParseException, SchedulerException
{
schedulerFactory = new StdSchedulerFactory();
scheduler = schedulerFactory.getScheduler();
JobDetail startECMSJob = new JobDetail("startECMSJob", "group1", StartECMSJob.class);
CronTrigger trigger1 = new CronTrigger("cronTrigger", "TriggersGroup1", "0 0/5 * * * ?");
scheduler.scheduleJob(startECMSJob, trigger1);
scheduler.start();
}
public void stop()
{
try
{
scheduler.shutdown();
}
catch (Exception e)
{
logger.error("ServerStartupManager Failure occured during Manager stop", e);
}
}
public void destroy()
{
}
}
I found a solution.
I was not using the right annotation. I have to use EJB3 annotations.