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

it.sauronsoftware.cron4j.Scheduler Maven / Gradle / Ivy

The newest version!
/*
 * cron4j - A pure Java cron-like scheduler
 * 
 * Copyright (C) 2007-2010 Carlo Pelliccia (www.sauronsoftware.it)
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version
 * 2.1, as published by the Free Software Foundation.
 *
 * This program 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 2.1 for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License version 2.1 along with this program.
 * If not, see .
 */
package it.sauronsoftware.cron4j;

import java.io.File;
import java.util.ArrayList;
import java.util.TimeZone;

/**
 * 

* The cron4j scheduler. *

* * @author Carlo Pelliccia */ public class Scheduler { /** * A GUID for this scheduler. */ private String guid = GUIDGenerator.generate(); /** * The time zone applied by the scheduler. */ private TimeZone timezone = null; /** * The daemon flag. If true the scheduler and its spawned threads acts like * daemons. */ private boolean daemon = false; /** * The state flag. If true the scheduler is started and running, otherwise * it is paused and no task is launched. */ private boolean started = false; /** * Registered {@link TaskCollector}s list. */ private ArrayList collectors = new ArrayList(); /** * The {@link MemoryTaskCollector} used for memory stored tasks. Represented * here for convenience, it is also the first element in the * {@link Scheduler#collectors} list. */ private MemoryTaskCollector memoryTaskCollector = new MemoryTaskCollector(); /** * The {@link FileTaskCollector} used for reading tasks from files. * Represented here for convenience, it is also the second element in the * {@link Scheduler#collectors} list. */ private FileTaskCollector fileTaskCollector = new FileTaskCollector(); /** * Registered {@link SchedulerListener}s list. */ private ArrayList listeners = new ArrayList(); /** * The thread checking the clock and requesting the spawning of launcher * threads. */ private TimerThread timer = null; /** * Currently running {@link LauncherThread} instances. */ private ArrayList launchers = null; /** * Currently running {@link TaskExecutor} instances. */ private ArrayList executors = null; /** * Internal lock, used to synchronize status-aware operations. */ private Object lock = new Object(); /** * It builds and prepares a brand new Scheduler instance. */ public Scheduler() { collectors.add(memoryTaskCollector); collectors.add(fileTaskCollector); } /** * It returns the GUID for this scheduler. * * @return The GUID for this scheduler. */ public Object getGuid() { return guid; } /** *

* Sets the time zone applied by the scheduler. *

*

* Current system time is adapted to the supplied time zone before comparing * it with registered scheduling patterns. The result is that any supplied * scheduling pattern is treated according to the specified time zone. In * example, suppose: *

*
    *
  • System time: 10:00
  • *
  • System time zone: GMT+1
  • *
  • Scheduler time zone: GMT+3
  • *
*

* The scheduler, before comparing system time with patterns, translates * 10:00 from GMT+1 to GMT+3. It means that 10:00 becomes 12:00. The * resulted time is then used by the scheduler to activate tasks. So, in the * given configuration at the given moment, any task scheduled as * 0 12 * * * will be executed, while any 0 10 * * * will * not. *

* * @param timezone * The time zone applied by the scheduler. */ public void setTimeZone(TimeZone timezone) { this.timezone = timezone; } /** * Returns the time zone applied by the scheduler. * * @return The time zone applied by the scheduler. */ public TimeZone getTimeZone() { return timezone != null ? timezone : TimeZone.getDefault(); } /** * Tests whether this scheduler is a daemon scheduler. * * @return true if this scheduler is a daemon scheduler; false otherwise. */ public boolean isDaemon() { return daemon; } /** * Marks this scheduler daemon flag. When a scheduler is marked as a daemon * scheduler it spawns only daemon threads. The Java Virtual Machine exits * when the only threads running are all daemon threads. * * This method must be called before the scheduler is started. * * @param on * If true, the scheduler will spawn only daemon threads. * @throws IllegalStateException * If the scheduler is started. */ public void setDaemon(boolean on) throws IllegalStateException { synchronized (lock) { if (started) { throw new IllegalStateException("Scheduler already started"); } this.daemon = on; } } /** * Tests if this scheduler is started. * * @return true if the scheduler is started, false if it is stopped. */ public boolean isStarted() { synchronized (lock) { return started; } } /** * Adds a {@link File} instance to the scheduler. Every minute the file will * be parsed. The scheduler will execute any declared task whose scheduling * pattern matches the current system time. * * See {@link CronParser} documentation for informations about the file * contents syntax. * * @param file * The {@link File} instance. */ public void scheduleFile(File file) { fileTaskCollector.addFile(file); } /** * Removes a {@link File} instance previously scheduled with the * {@link Scheduler#scheduleFile(File)} method. * * @param file * The {@link File} instance. */ public void descheduleFile(File file) { fileTaskCollector.removeFile(file); } /** * Returns an array containing any {@link File} previously scheduled with * the {@link Scheduler#scheduleFile(File)} method. * * @return An array containing any {@link File} previously scheduled with * the {@link Scheduler#scheduleFile(File)} method. */ public File[] getScheduledFiles() { return fileTaskCollector.getFiles(); } /** * Adds a custom {@link TaskCollector} instance to the scheduler. The * supplied object, once added to the scheduler, will be query every minute * for its task list. The scheduler will execute any of the returned tasks * whose scheduling pattern matches the current system time. * * @param collector * The custom {@link TaskCollector} instance. */ public void addTaskCollector(TaskCollector collector) { synchronized (collectors) { collectors.add(collector); } } /** * Removes a previously registered custom {@link TaskCollector} instance. * * @param collector * The custom {@link TaskCollector} instance. */ public void removeTaskCollector(TaskCollector collector) { synchronized (collectors) { collectors.remove(collector); } } /** * Returns an array containing any custom {@link TaskCollector} instance * previously registered in the scheduler with the * {@link Scheduler#addTaskCollector(TaskCollector)} method. * * @return An array containing any custom {@link TaskCollector} instance * previously registered in the scheduler with the * {@link Scheduler#addTaskCollector(TaskCollector)} method. */ public TaskCollector[] getTaskCollectors() { synchronized (collectors) { // Discard the first 2 elements in the list. int size = collectors.size() - 2; TaskCollector[] ret = new TaskCollector[size]; for (int i = 0; i < size; i++) { ret[i] = (TaskCollector) collectors.get(i + 2); } return ret; } } /** * Adds a {@link SchedulerListener} to the scheduler. A * {@link SchedulerListener} is notified every time a task is launching, has * succeeded or has failed. * * @param listener * The listener. */ public void addSchedulerListener(SchedulerListener listener) { synchronized (listeners) { listeners.add(listener); } } /** * Removes a {@link SchedulerListener} previously registered with the * {@link Scheduler#addSchedulerListener(SchedulerListener)} method. * * @param listener * The listener. */ public void removeSchedulerListener(SchedulerListener listener) { synchronized (listeners) { listeners.remove(listener); } } /** * Returns an array containing any {@link SchedulerListener} previously * registered with the * {@link Scheduler#addSchedulerListener(SchedulerListener)} method. * * @return An array containing any {@link SchedulerListener} previously * registered with the * {@link Scheduler#addSchedulerListener(SchedulerListener)} method. */ public SchedulerListener[] getSchedulerListeners() { synchronized (listeners) { int size = listeners.size(); SchedulerListener[] ret = new SchedulerListener[size]; for (int i = 0; i < size; i++) { ret[i] = (SchedulerListener) listeners.get(i); } return ret; } } /** * Returns an array containing any currently executing task, in the form of * {@link TaskExecutor} objects. Each running task is executed by a * different thread. A {@link TaskExecutor} object allows the control of the * running task. The inner {@link Task} representation could be retrieved, * the status of the task could be detected and the thread could be * interrupted using any standard {@link Thread} method ( * {@link Thread#interrupt()}, {@link Thread#isAlive() etc}. * * @return An array containing any currently executing task, in the form of * {@link TaskExecutor} objects. */ public TaskExecutor[] getExecutingTasks() { synchronized (executors) { int size = executors.size(); TaskExecutor[] ret = new TaskExecutor[size]; for (int i = 0; i < size; i++) { ret[i] = (TaskExecutor) executors.get(i); } return ret; } } /** * This method schedules a task execution. * * @param schedulingPattern * The scheduling pattern for the task. * @param task * The task, as a plain Runnable object. * @return The task auto-generated ID assigned by the scheduler. This ID can * be used later to reschedule and deschedule the task, and also to * retrieve informations about it. * @throws InvalidPatternException * If the supplied pattern is not valid. */ public String schedule(String schedulingPattern, Runnable task) throws InvalidPatternException { return schedule(schedulingPattern, new RunnableTask(task)); } /** * This method schedules a task execution. * * @param schedulingPattern * The scheduling pattern for the task. * @param task * The task, as a plain Runnable object. * @return The task auto-generated ID assigned by the scheduler. This ID can * be used later to reschedule and deschedule the task, and also to * retrieve informations about it. * @throws InvalidPatternException * If the supplied pattern is not valid. * @since 2.0 */ public String schedule(String schedulingPattern, Task task) throws InvalidPatternException { return schedule(new SchedulingPattern(schedulingPattern), task); } /** * This method schedules a task execution. * * @param schedulingPattern * The scheduling pattern for the task. * @param task * The task, as a plain Runnable object. * @return The task auto-generated ID assigned by the scheduler. This ID can * be used later to reschedule and deschedule the task, and also to * retrieve informations about it. * @since 2.0 */ public String schedule(SchedulingPattern schedulingPattern, Task task) { return memoryTaskCollector.add(schedulingPattern, task); } /** * This method changes the scheduling pattern of a task. * * @param id * The ID assigned to the previously scheduled task. * @param schedulingPattern * The new scheduling pattern for the task. * @throws InvalidPatternException * If the supplied pattern is not valid. * @deprecated Use {@link Scheduler#reschedule(String, String)}. */ public void reschedule(Object id, String schedulingPattern) throws InvalidPatternException { reschedule((String) id, new SchedulingPattern(schedulingPattern)); } /** * This method changes the scheduling pattern of a task. * * @param id * The ID assigned to the previously scheduled task. * @param schedulingPattern * The new scheduling pattern for the task. * @throws InvalidPatternException * If the supplied pattern is not valid. */ public void reschedule(String id, String schedulingPattern) throws InvalidPatternException { reschedule(id, new SchedulingPattern(schedulingPattern)); } /** * This method changes the scheduling pattern of a task. * * @param id * The ID assigned to the previously scheduled task. * @param schedulingPattern * The new scheduling pattern for the task. * @since 2.0 */ public void reschedule(String id, SchedulingPattern schedulingPattern) { memoryTaskCollector.update(id, schedulingPattern); } /** * This methods cancels the scheduling of a task. * * @param id * The ID of the task. * @deprecated Use {@link Scheduler#deschedule(String)}. */ public void deschedule(Object id) { deschedule((String) id); } /** * This methods cancels the scheduling of a task. * * @param id * The ID of the task. */ public void deschedule(String id) { memoryTaskCollector.remove(id); } /** * This method retrieves a previously scheduled task. * * @param id * The task ID. * @return The requested task, or null if the task was not found. * @since 2.0 */ public Task getTask(String id) { return memoryTaskCollector.getTask(id); } /** * This method retrieves a previously scheduled task scheduling pattern. * * @param id * The task ID. * @return The requested scheduling pattern, or null if the task was not * found. * @since 2.0 */ public SchedulingPattern getSchedulingPattern(String id) { return memoryTaskCollector.getSchedulingPattern(id); } /** * This method retrieves the Runnable object of a previously scheduled task. * * @param id * The task ID. * @return The Runnable object of the task, or null if the task was not * found. * @deprecated Use {@link Scheduler#getTask(String)}. */ public Runnable getTaskRunnable(Object id) { Task task = getTask((String) id); if (task instanceof RunnableTask) { RunnableTask rt = (RunnableTask) task; return rt.getRunnable(); } else { return null; } } /** * This method retrieves the scheduling pattern of a previously scheduled * task. * * @param id * The task ID. * @return The scheduling pattern of the task, or null if the task was not * found. * @deprecated Use {@link Scheduler#getSchedulingPattern(String)}. */ public String getTaskSchedulingPattern(Object id) { return getSchedulingPattern((String) id).toString(); } /** * Executes immediately a task, without scheduling it. * * @param task * The task. * @return The {@link TaskExecutor} executing the given task. * @throws IllegalStateException * If the scheduler is not started. */ public TaskExecutor launch(Task task) { synchronized (lock) { if (!started) { throw new IllegalStateException("Scheduler not started"); } return spawnExecutor(task); } } /** * This method starts the scheduler. When the scheduled is started the * supplied tasks are executed at the given moment. * * @throws IllegalStateException * Thrown if this scheduler is already started. */ public void start() throws IllegalStateException { synchronized (lock) { if (started) { throw new IllegalStateException("Scheduler already started"); } // Initializes required lists. launchers = new ArrayList(); executors = new ArrayList(); // Starts the timer thread. timer = new TimerThread(this); timer.setDaemon(daemon); timer.start(); // Change the state of the scheduler. started = true; } } /** * This method stops the scheduler execution. Before returning, it waits the * end of all the running tasks previously launched. Once the scheduler has * been stopped it can be started again with a start() call. * * @throws IllegalStateException * Thrown if this scheduler is not started. */ public void stop() throws IllegalStateException { synchronized (lock) { if (!started) { throw new IllegalStateException("Scheduler not started"); } // Interrupts the timer and waits for its death. timer.interrupt(); tillThreadDies(timer); timer = null; // Interrupts any running launcher and waits for its death. for (;;) { LauncherThread launcher = null; synchronized (launchers) { if (launchers.size() == 0) { break; } launcher = (LauncherThread) launchers.remove(0); } launcher.interrupt(); tillThreadDies(launcher); } launchers = null; // Interrupts any running executor and waits for its death. // Before exiting wait for all the active tasks end. for (;;) { TaskExecutor executor = null; synchronized (executors) { if (executors.size() == 0) { break; } executor = (TaskExecutor) executors.remove(0); } if (executor.canBeStopped()) { executor.stop(); } tillExecutorDies(executor); } executors = null; // Change the state of the object. started = false; } } // -- PACKAGE RESERVED METHODS -------------------------------------------- /** * Starts a launcher thread. * * @param referenceTimeInMillis * Reference time in millis for the launcher. * @return The spawned launcher. */ LauncherThread spawnLauncher(long referenceTimeInMillis) { TaskCollector[] nowCollectors; synchronized (collectors) { int size = collectors.size(); nowCollectors = new TaskCollector[size]; for (int i = 0; i < size; i++) { nowCollectors[i] = (TaskCollector) collectors.get(i); } } LauncherThread l = new LauncherThread(this, nowCollectors, referenceTimeInMillis); synchronized (launchers) { launchers.add(l); } l.setDaemon(daemon); l.start(); return l; } /** * Starts the given task within a task executor. * * @param task * The task. * @return The spawned task executor. */ TaskExecutor spawnExecutor(Task task) { TaskExecutor e = new TaskExecutor(this, task); synchronized (executors) { executors.add(e); } e.start(daemon); return e; } /** * This method is called by a launcher thread to notify that the execution * is completed. * * @param launcher * The launcher which has completed its task. */ void notifyLauncherCompleted(LauncherThread launcher) { synchronized (launchers) { launchers.remove(launcher); } } /** * This method is called by a task executor to notify that the execution is * completed. * * @param executor * The executor which has completed its task. */ void notifyExecutorCompleted(TaskExecutor executor) { synchronized (executors) { executors.remove(executor); } } /** * Notifies every registered listener that a task is going to be launched. * * @param executor * The task executor. */ void notifyTaskLaunching(TaskExecutor executor) { synchronized (listeners) { int size = listeners.size(); for (int i = 0; i < size; i++) { SchedulerListener l = (SchedulerListener) listeners.get(i); l.taskLaunching(executor); } } } /** * Notifies every registered listener that a task execution has successfully * completed. * * @param executor * The task executor. */ void notifyTaskSucceeded(TaskExecutor executor) { synchronized (listeners) { int size = listeners.size(); for (int i = 0; i < size; i++) { SchedulerListener l = (SchedulerListener) listeners.get(i); l.taskSucceeded(executor); } } } /** * Notifies every registered listener that a task execution has failed due * to an uncaught exception. * * @param executor * The task executor. * @param exception * The exception. */ void notifyTaskFailed(TaskExecutor executor, Throwable exception) { synchronized (listeners) { int size = listeners.size(); if (size > 0) { for (int i = 0; i < size; i++) { SchedulerListener l = (SchedulerListener) listeners.get(i); l.taskFailed(executor, exception); } } else { // Logs on console if no one has been notified about it. exception.printStackTrace(); } } } // -- PRIVATE METHODS ----------------------------------------------------- /** * It waits until the given thread is dead. It is similar to * {@link Thread#join()}, but this one avoids {@link InterruptedException} * instances. * * @param thread * The thread. */ private void tillThreadDies(Thread thread) { boolean dead = false; do { try { thread.join(); dead = true; } catch (InterruptedException e) { ; } } while (!dead); } /** * It waits until the given task executor is dead. It is similar to * {@link TaskExecutor#join()}, but this one avoids * {@link InterruptedException} instances. * * @param executor * The task executor. */ private void tillExecutorDies(TaskExecutor executor) { boolean dead = false; do { try { executor.join(); dead = true; } catch (InterruptedException e) { ; } } while (!dead); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy