/* * Copyright (C) 2012 eXo Platform SAS. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.crsh.cron; import it.sauronsoftware.cron4j.InvalidPatternException; import it.sauronsoftware.cron4j.Scheduler; import it.sauronsoftware.cron4j.SchedulingPattern; import it.sauronsoftware.cron4j.TaskCollector; import it.sauronsoftware.cron4j.TaskTable; import org.crsh.plugin.CRaSHPlugin; import org.crsh.plugin.PropertyDescriptor; import org.crsh.plugin.ResourceKind; import org.crsh.shell.ShellFactory; import org.crsh.util.Utils; import org.crsh.vfs.Resource; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.lang.reflect.Method; import java.net.MalformedURLException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.CopyOnWriteArrayList; import java.util.logging.Level; import java.util.logging.Logger; /** @author Benjamin Prato */ public class CronPlugin extends CRaSHPlugin<CronPlugin> implements TaskCollector { public static PropertyDescriptor<String> CRON_CONFIG_PATH = new PropertyDescriptor<String>(String.class, "cron.config.path", null, "The contrab file path") { @Override protected String doParse(String s) throws Exception { return s; } }; /** Logger */ protected final Logger log = Logger.getLogger(getClass().getName()); /** . */ private final Scheduler scheduler = new Scheduler(); /** . */ final ConcurrentLinkedQueue<CRaSHTaskProcess> history = new ConcurrentLinkedQueue<CRaSHTaskProcess>(); /** . */ final CopyOnWriteArrayList<CRaSHTaskProcess> processes = new CopyOnWriteArrayList<CRaSHTaskProcess>(); @Override public CronPlugin getImplementation() { return this; } public Scheduler getScheduler() { return scheduler; } @Override protected Iterable<PropertyDescriptor<?>> createConfigurationCapabilities() { return Collections.<PropertyDescriptor<?>>singleton(CRON_CONFIG_PATH); } @Override public void init() { scheduler.addTaskCollector(this); scheduler.start(); } @Override public void destroy() { scheduler.stop(); } /** * Returns the processes currently running. * * @return the running processes */ public List<CRaSHTaskProcess> getProcesses() { return processes; } /** * Returns the processes history. * * @return the running processes */ public Queue<CRaSHTaskProcess> getHistory() { return history; } /** * Spawn the job immediatly. * @return true when the jobs have been spawned successfully */ public boolean spawn() { try { Method spawn = Scheduler.class.getDeclaredMethod("spawnLauncher", long.class); spawn.setAccessible(true); spawn.invoke(scheduler, System.currentTimeMillis()); return true; } catch (Exception e) { e.printStackTrace(); return false; } } /** * This method is protected so it can be overriden for unit tests * * @return read the config file and return it */ protected Resource getConfig() { // Resource config = null; String configPath = getContext().getProperty(CRON_CONFIG_PATH); if (configPath != null) { File configFile = new File(configPath); if (configFile.exists()) { log.log(Level.FINE, "Found crontab file " + configPath); if (configFile.isFile()) { try { config = new Resource("crontab", configFile.toURI().toURL()); } catch (MalformedURLException e) { log.log(Level.SEVERE, "Could not retrieve cron config file from " + configPath, e); } catch (IOException e) { log.log(Level.FINE, "Could not load cron config file from " + configPath, e); } } else { log.log(Level.FINE, "Crontab file " + configPath + " is not a file"); } } else { log.log(Level.FINE, "Crontab file " + configPath + " does not exist"); } } else { // Override from config if any Resource res = getContext().loadResource("crontab", ResourceKind.CONFIG); if (res != null) { config = res; log.log(Level.FINE, "Found crontab config url " + res); } } // if (config == null) { log.log(Level.INFO, "No crontab configuration"); } // return config; } public TaskTable getTasks() { // Resource res = getConfig(); List<String> lines = null; try { lines = new ArrayList<String>(); BufferedReader reader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(res.getContent()), "UTF-8")); while (true) { String cronLine = reader.readLine(); if (cronLine == null) { break; } else { lines.add(cronLine); } } } catch (IOException e) { log.log(Level.FINE, "Could not read cron file", e); } // ShellFactory factory = getContext().getPlugin(ShellFactory.class); TaskTable table = new TaskTable(); for (String cronLine : lines) { CRaSHTaskDef crshTask = validateAndParseCronLine(cronLine); if (crshTask != null) { table.add(crshTask.getSchedullingPattern(), new CRaSHTask(this, factory, crshTask)); } else { log.log(Level.FINE, "Cannot parse cron line " + cronLine); } } return table; } private CRaSHTaskDef validateAndParseCronLine(String cronLine) { // cronLine = cronLine.trim(); if (cronLine.length() == 0 || cronLine.charAt(0) == '#') { return null; } // List<String> cronLineParts = Arrays.asList(cronLine.split("\\s+")); if (cronLineParts.size() < 6) { return null; } // String schedulePart = Utils.join(cronLineParts.subList(0, 5), " "); String commandPart = Utils.join(cronLineParts.subList(5, cronLineParts.size()), " "); try { return new CRaSHTaskDef(new SchedulingPattern(schedulePart), commandPart); } catch (InvalidPatternException e) { // ? return null; } } }