package org.molgenis.file.ingest; import com.google.common.collect.Sets; import org.molgenis.data.DataService; import org.molgenis.data.Entity; import org.molgenis.data.UnknownEntityException; import org.molgenis.data.validation.ConstraintViolation; import org.molgenis.data.validation.MolgenisValidationException; import org.molgenis.file.ingest.execution.FileIngestException; import org.molgenis.file.ingest.meta.FileIngest; import org.molgenis.file.ingest.meta.FileIngestMetaData; import org.quartz.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import static java.util.Objects.requireNonNull; import static org.molgenis.file.ingest.meta.FileIngestMetaData.FILE_INGEST; import static org.quartz.CronScheduleBuilder.cronSchedule; import static org.quartz.JobBuilder.newJob; import static org.quartz.TriggerBuilder.newTrigger; /** * Schedule and unschedule FileIngestJobs */ @Component public class FileIngesterJobScheduler { public static final String TRIGGER_GROUP = "fileingest"; public static final String JOB_GROUP = "fileingest"; private static final Logger LOG = LoggerFactory.getLogger(FileIngesterJobScheduler.class); private final Scheduler scheduler; private final DataService dataService; @Autowired public FileIngesterJobScheduler(Scheduler scheduler, DataService dataService) { this.scheduler = requireNonNull(scheduler); this.dataService = requireNonNull(dataService); } /** * Execute FileIngest job immediately * * @param fileIngestId */ public synchronized void runNow(String fileIngestId) { FileIngest fileIngest = dataService.findOneById(FILE_INGEST, fileIngestId, FileIngest.class); if (fileIngest == null) { throw new UnknownEntityException("Unknown FileIngest entity id '" + fileIngestId + "'"); } try { JobKey jobKey = new JobKey(fileIngestId, JOB_GROUP); if (scheduler.checkExists(jobKey)) { // Run job now scheduler.triggerJob(jobKey); } else { // Schedule with 'now' trigger Trigger trigger = newTrigger().withIdentity(fileIngestId, TRIGGER_GROUP).startNow().build(); schedule(fileIngestId, trigger); } } catch (SchedulerException e) { LOG.error("Error runNow FileIngesterJob", e); throw new FileIngestException("Error job runNow", e); } } /** * Schedule a FileIngest job with a cron expression defined in the entity. * <p> * Reschedules job if the job already exists. * <p> * If active is false, it unschedules the job * * @param fileIngest */ public synchronized void schedule(Entity fileIngest) { String id = fileIngest.getString(FileIngestMetaData.ID); String cronExpression = fileIngest.getString(FileIngestMetaData.CRONEXPRESSION); String name = fileIngest.getString(FileIngestMetaData.NAME); // Validate cron expression if (!CronExpression.isValidExpression(cronExpression)) { throw new MolgenisValidationException( Sets.newHashSet(new ConstraintViolation("Invalid cronexpression '" + cronExpression + "'", null))); } try { // If already scheduled, remove it from the scheduler if (scheduler.checkExists(new JobKey(id, JOB_GROUP))) { unschedule(id); } // If not active, do not schedule it if (!fileIngest.getBoolean(FileIngestMetaData.ACTIVE)) { return; } // Schedule with 'cron' trigger Trigger trigger = newTrigger().withIdentity(id, TRIGGER_GROUP).withSchedule(cronSchedule(cronExpression)) .build(); schedule(fileIngest.getIdValue().toString(), trigger); LOG.info("Scheduled FileIngesterJob '{}' with trigger '{}'", name, trigger); } catch (SchedulerException e) { LOG.error("Error schedule job", e); throw new FileIngestException("Error schedule job", e); } } /** * Remove a job from the scheduler * * @param fileIngestId */ public synchronized void unschedule(String fileIngestId) { try { scheduler.deleteJob(new JobKey(fileIngestId, JOB_GROUP)); } catch (SchedulerException e) { LOG.error("Error unschedule FileIngesterJob '" + fileIngestId + "'", e); throw new FileIngestException("Error unscheduling job", e); } } private void schedule(String id, Trigger trigger) throws SchedulerException { JobDataMap jobDataMap = new JobDataMap(); jobDataMap.put(FileIngesterQuartzJob.ENTITY_KEY, id); JobDetail job = newJob(FileIngesterQuartzJob.class).withIdentity(id, JOB_GROUP).usingJobData(jobDataMap) .build(); scheduler.scheduleJob(job, trigger); } }