package io.mangoo.scheduler; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Objects; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.quartz.JobDetail; import org.quartz.JobKey; import org.quartz.SchedulerException; import org.quartz.Trigger; import org.quartz.Trigger.TriggerState; import org.quartz.impl.StdSchedulerFactory; import org.quartz.impl.matchers.GroupMatcher; import com.google.inject.Inject; import com.google.inject.Singleton; import io.mangoo.configuration.Config; import io.mangoo.core.Application; import io.mangoo.enums.Default; import io.mangoo.enums.Required; import io.mangoo.exceptions.MangooSchedulerException; /** * Convenient class for interacting with the quartz scheduler * * @author svenkubiak * */ @Singleton public class Scheduler { private static final Logger LOG = LogManager.getLogger(Scheduler.class); private org.quartz.Scheduler quartzScheduler; @Inject public Scheduler(Config config) { config.getAllConfigurations().entrySet().forEach((Map.Entry<String, String> entry) -> { if (entry.getKey().startsWith(Default.SCHEDULER_PREFIX.toString())) { System.setProperty(entry.getKey(), entry.getValue()); } }); } /*** * Initializes the scheduler by booting up Quartz * */ public void initialize() { try { this.quartzScheduler = new StdSchedulerFactory().getScheduler(); this.quartzScheduler.setJobFactory(Application.getInstance(SchedulerFactory.class)); } catch (final SchedulerException e) { LOG.error("Failed to initialize scheduler", e); } } /** * Checks if the scheduler has been initialized * * @return True if the scheduler has been initializes, false otherwise */ public boolean isInitialize() { return this.quartzScheduler != null; } /** * Checks if the scheduler is initialized and started * * @return true if the scheduler is started, false otherwise * @throws MangooSchedulerException if an error occurred accessing the scheduler */ public boolean isStarted() throws MangooSchedulerException { boolean started; try { started = this.quartzScheduler != null && this.quartzScheduler.isStarted(); } catch (SchedulerException e) { throw new MangooSchedulerException(e); } return started; } /** * Returns the current quartz scheduler instance * * @return Scheduler instance, null if scheduler is not initialize or started */ public org.quartz.Scheduler getQuartzScheduler() { return this.quartzScheduler; } public void start() throws MangooSchedulerException { Objects.requireNonNull(this.quartzScheduler, Required.SCHEDULER.toString()); try { this.quartzScheduler.start(); if (this.quartzScheduler.isStarted()) { LOG.info("Successfully started quartz scheduler"); } else { LOG.error("Scheduler is not started"); } } catch (final SchedulerException e) { throw new MangooSchedulerException(e); } } public void shutdown() throws MangooSchedulerException { Objects.requireNonNull(this.quartzScheduler, Required.SCHEDULER.toString()); try { if (isStarted()) { this.quartzScheduler.shutdown(); if (this.quartzScheduler.isShutdown()) { LOG.info("Successfully shutdown quartz scheduler"); } else { LOG.error("Failed to shutdown scheduler"); } } } catch (final SchedulerException e) { throw new MangooSchedulerException(e); } } public void standby() throws MangooSchedulerException { Objects.requireNonNull(this.quartzScheduler, Required.SCHEDULER.toString()); try { this.quartzScheduler.standby(); if (this.quartzScheduler.isInStandbyMode()) { LOG.info("Scheduler is now in standby"); } else { LOG.error("Failed to put scheduler in standby"); } } catch (final SchedulerException e) { throw new MangooSchedulerException(e); } } /** * Adds a new job with a given JobDetail and Trigger to the scheduler * * @param jobDetail The JobDetail for the Job * @param trigger The Trigger for the job * @throws MangooSchedulerException when accessing the scheduler results in an error */ public void schedule(JobDetail jobDetail, Trigger trigger) throws MangooSchedulerException { Objects.requireNonNull(jobDetail, Required.JOB_DETAIL.toString()); Objects.requireNonNull(trigger, Required.TRIGGER.toString()); Objects.requireNonNull(this.quartzScheduler, Required.SCHEDULER.toString()); try { this.quartzScheduler.scheduleJob(jobDetail, trigger); } catch (final SchedulerException e) { throw new MangooSchedulerException(e); } } /** * Retrieves a list of all jobs and their current status * * @return List of io.mangoo.models.Job objects * @throws MangooSchedulerException if an error occurs during access to the Quartz Scheduler */ @SuppressWarnings("unchecked") public List<io.mangoo.models.Job> getAllJobs() throws MangooSchedulerException { Objects.requireNonNull(this.quartzScheduler, Required.SCHEDULER.toString()); List<io.mangoo.models.Job> jobs = new ArrayList<>(); try { for (JobKey jobKey : getAllJobKeys()) { List<Trigger> triggers = (List<Trigger>) this.quartzScheduler.getTriggersOfJob(jobKey); Trigger trigger = triggers.get(0); TriggerState triggerState = quartzScheduler.getTriggerState(trigger.getKey()); jobs.add(new io.mangoo.models.Job(TriggerState.PAUSED == triggerState ? false : true, jobKey.getName(), trigger.getDescription(), trigger.getNextFireTime(), trigger.getPreviousFireTime())); } } catch (SchedulerException e) { throw new MangooSchedulerException(e); } return jobs; } /** * Pauses a job by a given name * @param name The name of the job to pause * * @throws MangooSchedulerException when pausing fails */ public void pauseJob(String name) throws MangooSchedulerException { Objects.requireNonNull(name, Required.NAME.toString()); try { JobKey jobKey = getJobKey(name); this.quartzScheduler.pauseJob(jobKey); } catch (SchedulerException | MangooSchedulerException e) { throw new MangooSchedulerException(e); } } /** * Resume a job by a given name * @param name The name of the job to resume * * @throws MangooSchedulerException if resuming fails */ public void resumeJob(String name) throws MangooSchedulerException { Objects.requireNonNull(name, Required.NAME.toString()); try { JobKey jobKey = getJobKey(name); this.quartzScheduler.resumeJob(jobKey); } catch (SchedulerException | MangooSchedulerException e) { throw new MangooSchedulerException(e); } } /** * Delete a job by a given name * @param name The name of the job to delete * * @throws MangooSchedulerException if deletion fails */ public void deleteJob(String name) throws MangooSchedulerException { Objects.requireNonNull(name, Required.NAME.toString()); try { JobKey jobKey = getJobKey(name); this.quartzScheduler.deleteJob(jobKey); } catch (SchedulerException | MangooSchedulerException e) { throw new MangooSchedulerException(e); } } /** * Retrieves a JobKey by it given name * * @param name The name of the Job in the Scheduler * @return Optional of JobKey * * @throws MangooSchedulerException if retrieving the job fails */ public JobKey getJobKey(String name) throws MangooSchedulerException { Objects.requireNonNull(name, Required.NAME.toString()); List<JobKey> allJobKeys = getAllJobKeys(); for (JobKey jobKey : allJobKeys) { if (jobKey.getName().equalsIgnoreCase(name)) { return jobKey; } } return null; } /** * Executes a single Quartz Scheduler job right away only once * * @param jobName The name of the job to execute * @throws MangooSchedulerException if an error occurs during execution of the job */ public void executeJob(String jobName) throws MangooSchedulerException { Objects.requireNonNull(this.quartzScheduler, Required.SCHEDULER.toString()); try { for (JobKey jobKey : getAllJobKeys()) { if (jobKey.getName().equalsIgnoreCase(jobName)) { this.quartzScheduler.triggerJob(jobKey); } } } catch (SchedulerException | MangooSchedulerException e) { throw new MangooSchedulerException(e); } } /** * Retrieves a list of all JobKeys from the Quartz Scheduler * * @return List of all JobKey objects * @throws MangooSchedulerException if an errors occurs during access to the scheduler */ public List<JobKey> getAllJobKeys() throws MangooSchedulerException { Objects.requireNonNull(this.quartzScheduler, Required.SCHEDULER.toString()); List<JobKey> jobKeys = new ArrayList<>(); try { for (String groupName : this.quartzScheduler.getJobGroupNames()) { jobKeys.addAll(this.quartzScheduler.getJobKeys(GroupMatcher.jobGroupEquals(groupName))); } } catch (SchedulerException e) { throw new MangooSchedulerException(e); } return jobKeys; } /** * Changes the state of a normally running job from pause to resume or resume to pause * * @param jobName The name of the job * @throws MangooSchedulerException if an error occurs during access to the quartz scheduler */ public void changeState(String jobName) throws MangooSchedulerException { Objects.requireNonNull(this.quartzScheduler, Required.SCHEDULER.toString()); try { for (JobKey jobKey : getAllJobKeys()) { if (jobKey.getName().equalsIgnoreCase(jobName)) { TriggerState triggerState = getTriggerState(jobKey); if (TriggerState.NORMAL == triggerState) { this.quartzScheduler.pauseJob(jobKey); } else { this.quartzScheduler.resumeJob(jobKey); } } } } catch (SchedulerException | MangooSchedulerException e) { throw new MangooSchedulerException(e); } } @SuppressWarnings("unchecked") private TriggerState getTriggerState(JobKey jobKey) throws SchedulerException { Objects.requireNonNull(this.quartzScheduler, Required.SCHEDULER.toString()); List<Trigger> triggers = (List<Trigger>) this.quartzScheduler.getTriggersOfJob(jobKey); Trigger trigger = triggers.get(0); return this.quartzScheduler.getTriggerState(trigger.getKey()); } }