/* * Copyright 2012-2014 Nikolay A. Viguro * <p/> * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * <p/> * http://www.apache.org/licenses/LICENSE-2.0 * <p/> * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package ru.iris.scheduler; import com.avaje.ebean.Ebean; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.quartz.*; import org.quartz.impl.JobDetailImpl; import org.quartz.impl.StdSchedulerFactory; import org.quartz.impl.matchers.GroupMatcher; import org.quartz.impl.triggers.SimpleTriggerImpl; import ru.iris.common.database.model.DataSource; import ru.iris.common.database.model.Task; import ru.iris.common.messaging.JsonEnvelope; import ru.iris.common.messaging.JsonMessaging; import ru.iris.common.messaging.JsonNotification; import ru.iris.common.messaging.model.tasks.TaskChangesAdvertisement; import ru.iris.common.messaging.model.tasks.TaskSourcesChangesAdvertisement; import ru.iris.common.messaging.model.tasks.TasksStartAdvertisement; import ru.iris.common.messaging.model.tasks.TasksStopAdvertisement; import ru.iris.common.modulestatus.Status; import ru.iris.common.source.googlecal.GoogleCalendarSource; import ru.iris.common.source.vk.VKSource; import java.util.Calendar; import java.util.Date; import java.util.List; import java.util.UUID; public class ScheduleService { private final Logger LOGGER = LogManager.getLogger(ScheduleService.class); private List<Task> events = null; private List<Task> cronevents = null; private List<DataSource> sources = null; private Scheduler scheduler = null; private JsonMessaging jsonMessaging; public ScheduleService() { Status status = new Status("Scheduler"); if (status.checkExist()) { status.running(); } else { status.addIntoDB("Scheduler", "Service that schedule and execute jobs"); } try { SchedulerFactory factory = new StdSchedulerFactory(); scheduler = factory.getScheduler(); // run readSources(); readAndScheduleTasks(); jsonMessaging = new JsonMessaging(UUID.randomUUID(), "events"); jsonMessaging.subscribe("event.scheduler.reload.tasks"); jsonMessaging.subscribe("event.scheduler.reload.sources"); jsonMessaging.subscribe("event.scheduler.stop"); jsonMessaging.subscribe("event.scheduler.start"); jsonMessaging.subscribe("event.scheduler.restart"); jsonMessaging.setNotification(new JsonNotification() { @Override public void onNotification(JsonEnvelope envelope) throws RuntimeException, SchedulerException, InterruptedException, ClassNotFoundException { if (envelope.getObject() instanceof TasksStartAdvertisement) { LOGGER.info("Start/restart scheduler service!"); readAndScheduleTasks(); } else if (envelope.getObject() instanceof TasksStopAdvertisement) { LOGGER.info("Stop scheduler service"); // reload events scheduler.shutdown(); events = null; cronevents = null; } else if (envelope.getObject() instanceof TaskSourcesChangesAdvertisement) { LOGGER.info("Reload sources list"); // reload sources sources = null; sources = Ebean.find(DataSource.class).where().eq("enabled", true).findList(); // take pause to save/remove new entity Thread.sleep(500); readSources(); LOGGER.info("Loaded " + sources.size() + " sources."); } else if (envelope.getObject() instanceof TaskChangesAdvertisement) { LOGGER.info("Reload tasks list"); readAndScheduleTasks(); } } }); jsonMessaging.start(); } catch (final Throwable t) { LOGGER.error("Error in Scheduler!"); status.crashed(); t.printStackTrace(); } } private void readSources() throws RuntimeException { sources = Ebean.find(DataSource.class).where().eq("enabled", true).findList(); for (DataSource source : sources) { switch (source.getType()) { // google calendar case "gcal": GoogleCalendarSource.getInstance().populateCalendar(source.getObj()); break; // VK.com case "vk": VKSource.getInstance().populateBirthDayCalendar(source.getObj()); break; default: LOGGER.info("Unknown data source: " + source.getType() + "!"); break; } } } private void readAndScheduleTasks() throws RuntimeException, SchedulerException, InterruptedException, ClassNotFoundException { Calendar cal = Calendar.getInstance(); cal.add(Calendar.YEAR, 1); events = null; cronevents = null; events = Ebean.find(Task.class) .where() .eq("enabled", true) .between("startdate", new Date(), cal.getTime()) .eq("showInCalendar", true) .findList(); cronevents = Ebean.find(Task.class) .where() .eq("enabled", true) .eq("showInCalendar", false) .findList(); // take pause to save/remove new entity Thread.sleep(500); //Start scheduler if (!scheduler.isStarted()) { LOGGER.info("Scheduling tasks!"); scheduler.start(); } else { LOGGER.info("Rescheduling tasks!"); // cancel calendar jobs for (JobKey job : scheduler.getJobKeys(GroupMatcher.jobGroupEquals("scheduler"))) { LOGGER.debug("Interrupt and delete task: " + job.getName()); scheduler.interrupt(job); scheduler.deleteJob(job); } // cancel cron jobs for (JobKey job : scheduler.getJobKeys(GroupMatcher.jobGroupEquals("scheduler-cron"))) { LOGGER.debug("Interrupt and delete cron task: " + job.getName()); scheduler.interrupt(job); scheduler.deleteJob(job); } } for (Task event : events) { JobDetailImpl jobDetail = new JobDetailImpl(); jobDetail.setName(event.getTitle()); jobDetail.setJobClass((Class<? extends Job>) Class.forName(event.getClazz())); jobDetail.getJobDataMap().put("subject", event.getSubject()); jobDetail.getJobDataMap().put("obj", event.getObj()); jobDetail.setGroup("scheduler"); //Creating schedule time with trigger SimpleTriggerImpl trigger = new SimpleTriggerImpl(); trigger.setStartTime(event.getStartdate()); trigger.setEndTime(event.getEnddate()); if (event.getPeriod() != null && !event.getPeriod().isEmpty()) trigger.setRepeatInterval(Long.valueOf(event.getPeriod())); trigger.setName("trigger-" + event.getTitle()); LOGGER.debug("Schedule job: " + jobDetail.getName()); scheduler.scheduleJob(jobDetail, trigger); } LOGGER.info("Scheduled " + scheduler.getJobKeys(GroupMatcher.jobGroupEquals("scheduler")).size() + " jobs!"); for (Task event : cronevents) { JobDetailImpl jobDetail = new JobDetailImpl(); jobDetail.setName(event.getTitle()); jobDetail.setJobClass((Class<? extends Job>) Class.forName(event.getClazz())); jobDetail.getJobDataMap().put("subject", event.getSubject()); jobDetail.getJobDataMap().put("obj", event.getObj()); jobDetail.setGroup("scheduler-cron"); CronTrigger trigger = TriggerBuilder.newTrigger() .withIdentity("trigger-cron-" + event.getTitle()) .withSchedule(CronScheduleBuilder.cronSchedule(event.getPeriod())) .startNow() .build(); LOGGER.debug("Schedule cron job: " + jobDetail.getName()); scheduler.scheduleJob(jobDetail, trigger); } LOGGER.info("Scheduled " + scheduler.getJobKeys(GroupMatcher.jobGroupEquals("scheduler-cron")).size() + " cron jobs!"); } public void stop() { jsonMessaging.close(); try { scheduler.clear(); scheduler.shutdown(); } catch (SchedulerException e) { e.printStackTrace(); } } }