All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.qwazr.scheduler.SchedulerManager Maven / Gradle / Ivy

There is a newer version: 1.5.0
Show newest version
/*
 * Copyright 2015-2017 Emmanuel Keller / QWAZR
 * 

* 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 *

* http://www.apache.org/licenses/LICENSE-2.0 *

* 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 com.qwazr.scheduler; import com.qwazr.cluster.ClusterManager; import com.qwazr.scripts.ScriptManager; import com.qwazr.scripts.ScriptRunStatus; import com.qwazr.scripts.ScriptServiceBuilder; import com.qwazr.scripts.ScriptServiceInterface; import com.qwazr.server.ApplicationBuilder; import com.qwazr.server.GenericServerBuilder; import com.qwazr.server.ServerException; import com.qwazr.server.configuration.ServerConfiguration; import com.qwazr.utils.LoggerUtils; import com.qwazr.utils.ObjectMappers; import com.qwazr.utils.concurrent.ReadWriteLock; import org.apache.commons.lang3.StringUtils; import org.quartz.CronScheduleBuilder; import org.quartz.CronTrigger; import org.quartz.JobBuilder; import org.quartz.JobDetail; import org.quartz.Scheduler; import org.quartz.SchedulerException; import org.quartz.TriggerBuilder; import org.quartz.impl.DirectSchedulerFactory; import javax.ws.rs.core.Response.Status; import java.io.Closeable; import java.io.File; import java.io.IOException; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.TimeZone; import java.util.TreeMap; import java.util.concurrent.ExecutorService; import java.util.logging.Level; import java.util.logging.Logger; public class SchedulerManager implements Closeable { private static final Logger LOGGER = LoggerUtils.getLogger(SchedulerManager.class); public static final String QWAZR_SCHEDULER_MAX_THREADS = "QWAZR_SCHEDULER_MAX_THREADS"; public static final int DEFAULT_MAX_THREADS = 200; private final String myAddress; private final ScriptServiceBuilder scriptServiceBuilder; private final ClusterManager clusterManager; private final Scheduler globalScheduler; private final Map> schedulerStatusMap; private final ReadWriteLock statusMapLock; private final ReadWriteLock mapLock; private final Map> schedulerFileMap; private volatile Map schedulerMap; private final SchedulerServiceInterface service; public SchedulerManager(final ExecutorService executorService, final ClusterManager clusterManager, final ScriptManager scriptManager, final Integer maxThreads, final Collection etcFiles) throws IOException, SchedulerException, ServerException { scriptServiceBuilder = new ScriptServiceBuilder(executorService, clusterManager, scriptManager); this.clusterManager = clusterManager; this.myAddress = clusterManager == null ? null : clusterManager.getService().getStatus().me; statusMapLock = ReadWriteLock.stamped(); mapLock = ReadWriteLock.stamped(); schedulerMap = null; schedulerFileMap = new HashMap<>(); schedulerStatusMap = new HashMap<>(); final DirectSchedulerFactory schedulerFactory = DirectSchedulerFactory.getInstance(); schedulerFactory.createVolatileScheduler(maxThreads == null ? DEFAULT_MAX_THREADS : maxThreads); globalScheduler = schedulerFactory.getScheduler(); globalScheduler.getContext().put(SchedulerManager.class.getName(), this); globalScheduler.start(); Runtime.getRuntime().addShutdownHook(new Thread(this::close)); if (etcFiles != null) etcFiles.forEach(this::loadSchedulerConf); service = new SchedulerServiceImpl(this); } @Override public synchronized void close() { try { if (!globalScheduler.isShutdown()) globalScheduler.shutdown(); } catch (SchedulerException e) { LOGGER.log(Level.SEVERE, e, e::getMessage); } } public static Integer getMaxThreadConfiguration(ServerConfiguration configuration) { return configuration.getIntegerProperty(QWAZR_SCHEDULER_MAX_THREADS, DEFAULT_MAX_THREADS); } public SchedulerManager registerContextAttribute(final GenericServerBuilder builder) { builder.contextAttribute(this); return this; } public SchedulerManager registerWebService(final ApplicationBuilder builder) { builder.singletons(service); return this; } public SchedulerServiceInterface getService() { return service; } TreeMap getSchedulers() { final TreeMap map = new TreeMap<>(); final Map scMap = schedulerMap; if (scMap == null) return map; scMap.forEach((name, schedulerDef) -> map.put(name, myAddress + "/schedulers/" + name)); return map; } SchedulerDefinition getScheduler(final String schedulerName) throws IOException { final Map scMap = schedulerMap; final SchedulerDefinition schedulerDefinition = scMap == null ? null : scMap.get(schedulerName); if (schedulerDefinition == null) throw new ServerException(Status.NOT_FOUND, "Scheduler not found: " + schedulerName); return schedulerDefinition; } List getStatusList(final String schedulerName) throws IOException { return statusMapLock.readEx(() -> schedulerStatusMap.get(schedulerName)); } private void checkSchedulerCron(final String schedulerName, final SchedulerDefinition scheduler) throws SchedulerException { final JobDetail job = JobBuilder.newJob(SchedulerJob.class).withIdentity(schedulerName).build(); if (scheduler.enabled != null && scheduler.enabled) { final CronScheduleBuilder cronBuilder = CronScheduleBuilder.cronSchedule(scheduler.cron); if (!StringUtils.isEmpty(scheduler.time_zone)) cronBuilder.inTimeZone(TimeZone.getTimeZone(scheduler.time_zone)); final TriggerBuilder triggerBuilder = TriggerBuilder.newTrigger().withIdentity(schedulerName).withSchedule(cronBuilder).forJob(job); final CronTrigger trigger = triggerBuilder.build(); synchronized (globalScheduler) { globalScheduler.scheduleJob(job, trigger); } } else { synchronized (globalScheduler) { globalScheduler.deleteJob(job.getKey()); } } } List executeScheduler(final String schedulerName, final SchedulerDefinition scheduler) throws IOException, ServerException, URISyntaxException { if (!clusterManager.isLeader(SchedulerServiceInterface.SERVICE_NAME, scheduler.group)) return Collections.emptyList(); LOGGER.info(() -> "execute " + schedulerName + " / " + scheduler.script_path); final long startTime = System.currentTimeMillis(); final ScriptServiceInterface scriptService = scriptServiceBuilder.getActive(scheduler.group); if (scriptService == null) return null; final List statusList = scriptService.runScriptVariables(scheduler.script_path, scheduler.group, scheduler.rule, scheduler.variables); if (statusList == null) return null; final List statusList2 = ScriptRunStatus.cloneSchedulerResultList(statusList, startTime); statusMapLock.write(() -> schedulerStatusMap.put(schedulerName, statusList2)); return statusList2; } List executeScheduler(final String schedulerName) throws IOException, ServerException, URISyntaxException { return executeScheduler(schedulerName, getScheduler(schedulerName)); } private void loadSchedulerConf(final File jsonFile) { try { final SchedulerConfiguration schedulerConfiguration = ObjectMappers.JSON.readValue(jsonFile, SchedulerConfiguration.class); if (schedulerConfiguration == null || schedulerConfiguration.schedulers == null) { unloadSchedulerConf(jsonFile); return; } LOGGER.info(() -> "Load Scheduler configuration file: " + jsonFile.getAbsolutePath()); mapLock.writeEx(() -> { schedulerFileMap.put(jsonFile, schedulerConfiguration.schedulers); buildSchedulerMap(); }); } catch (IOException | SchedulerException e) { LOGGER.log(Level.SEVERE, e, e::getMessage); } } private void unloadSchedulerConf(File jsonFile) { try { mapLock.writeEx(() -> { final Map schedulerDefMap = schedulerFileMap.remove(jsonFile); if (schedulerDefMap == null) return; LOGGER.info(() -> "Unload Scheduler configuration file: " + jsonFile.getAbsolutePath()); buildSchedulerMap(); }); } catch (SchedulerException e) { LOGGER.log(Level.SEVERE, e, e::getMessage); } } private void buildSchedulerMap() throws SchedulerException { synchronized (globalScheduler) { globalScheduler.clear(); } final Map map = new HashMap<>(); schedulerFileMap.forEach((file, schedulerDefMap) -> map.putAll(schedulerDefMap)); final List removeKeys = new ArrayList<>(); // Remove the no more existing jobs status statusMapLock.read(() -> { schedulerStatusMap.forEach((name, scriptRunStatuses) -> { if (!map.containsKey(name)) removeKeys.add(name); }); removeKeys.forEach(schedulerStatusMap::remove); }); // Set the volatile map schedulerMap = map; // Reschedule the jobs schedulerMap.forEach((name, schedulerDefinition) -> { try { checkSchedulerCron(name, schedulerDefinition); } catch (SchedulerException e) { LOGGER.log(Level.SEVERE, e, () -> "Error on scheduler " + name + ": " + e.getMessage()); } }); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy