What is yacron4j ?


yacron4j is a lightweight easy to use library for scheduling cron jobs.

There exist other cron job schedulers for java such as:
So why create a new library ?

Quartz is comparatively heavy weight. It throws an NPE when a DST conflict occurs.
cron4j is the kind of lightweight library I was looking for. However it does not handle well  shutdown of the scheduler nor does it handle execution conflicts, thread or queue resource management, exceptions. It is LGPL licensed.Allows cron expressions only up to the minute.

The goal of this library:

Project page

The project is hosted on sourceforge. Here you will find the latest downloads,  issue tracker and discussion board.

Please use the help discussion board for support.
Please use the general discussion board for sharing ideas or for general comments.

Quickstart



public class QuickStart
{
    public static void main(String[] args) throws Exception
    {
        Scheduler scheduler = new Scheduler();        (1)
        String cronExpression = "0/1 * * * * ?";       (2)
        Runnable task = new Runnable()                   (3)
        {
            @Override
            public void run()
            {
                System.out.println("" + new Date() + " hello world! ");
            }
        };
        scheduler.schedule(task, cronExpression);     (4)
    }
}


(1) create a scheduler. This is similar to java's  
Executors.newScheduledThreadPool

(2) define a cron expression.
The syntax for cron expressions can be found here.

(3) define a task to execute.
This could be a Runnable or a Callable

(4) schedule the task. This is similar to java's
ScheduledExecutorService.schedule(...)

The above program will print "hello world!" every full second.

Configuring the Scheduler

      
        SchedulerOptions schedulerOptions = new SchedulerOptions();  (1)
        schedulerOptions.setMaxThreads(1);                                              (2)
        ....
        Scheduler scheduler = new Scheduler(schedulerOptions);           (3)

(1)  Create a SchedulerOptions object

(2) Set the options you would like to change.

(3) Create a Scheduler object using these options.

The following options are supported:

Property Default Value Description
threadFactory Executors.defaultThreadFactory() The thread factory used to create new threads
tickDuration 100 ms see netty documentation
ticksPerWheel 512 see netty documentation
maxThreads Integer.MAX_VALUE size of the thread pool for executing scheduled tasks.
if the number of triggered tasks is higher then the number of threads the tasks are added to a queue and will be executed as soon as a thread is freed.
setting this property to 1 will result in all tasks executed in sequence.
configure this property to control the memory requirements of your application.
maxQueueSize Integer.MAX_VALUE maximal size of the tasks which have been triggered and are awaiting execution in the thread pool.
note: configure this property to control the memory requirements of your application.
However setting it too low may cause a RejectedExecutionException.
logger new DefaultLogger("yacron4j-scheduler") The logger used by the Scheduler. Similar to netty's logger concept it must implement a InternalLogger. The netty project includes adatpers to standard logging frameworks and slf4j.
debug false if true prints debug information using logger.info()
persistManager DefaultPersistManager (no persistance) see Persistence


Configuring the task execution

        TaskOptions taskOptions = new TaskOptions();                                                       (1)
        taskOptions.setAllowOverlap(true);                                                                           (2)
         ...
        ScheduledFuture task = scheduler.schedule(task, cronExpression, taskOptions)   (3)

(1) Create a task options object

(2) Set the options you would like to change

(3) Schedule the task using these options

The following options are supported:

Property Default Value Description
allowOverlap false if true, allows multiple concurrent executions of the same task
ignoreOnOverlap true an overlap conflict resolution property
if allowOverlap is false, will cause the execution of the task to be ignored if it is triggered while the task is executing
if this property is false the trigger will execute immediatly on termination of the execution of  the current task. note: if the trigger frequency is higher then the execution duration this may cause an overflow of the thread pool queue.
maxConcurrent 1 if allowOverlap is true, the maximal number of concurrent executions of the same task. Futher triggers will be ignored
dstConflictIgnore false if true, the scheduler will use the standard quartz method to determine the trigger times, which may result in NPE exeptions.
if false the DST conflict resolution is activated
dstConflictResolution DST_CONFLICT_IMMEDIATE Example: within the time zone
America/Vancouver

the cron expression
"0 15 2 8 3 ? 2015
"
will cause a conflict because it does not exist. At 2:00 the clocks are moved to 3:00.

The property controls how the conflict is resolved:

DST_CONFLICT_BACKWARD: will trigger at 01:15
DST_CONFLICT_IMMEDIATE:                       03:00
DST_CONFLICT_FORWARD                          03:15
timeZone TimeZone.getDefault() sets the time zone for which the trigger calculations are done
exceptionResolution EXCEPTION_IGNORE in case the task throws an exception this property controls how this is handeled

EXCEPTION_CANCEL: the task execution is cancelled
EXCEPTION_RETRY: the task is immediatly reexecuted
EXCEPTION_IGNORE: the exception is ignored and the the task is queued for execution on the next cron trigger

Note: InterruptedException may cause the task to be cancelled
logger new DefaultLogger("scheduler") The logger used by the CronTask. Similar to netty's logger concept it must implement a InternalLogger. The netty project includes adatpers to standard logging frameworks and slf4j.
debug false if true prints debug information using logger.info()
persist false see Persistence


Persistence


        SchedulerOptions schedulerOptions = new SchedulerOptions();
        schedulerOptions.setPersistManager(new PrevaylerPersistentManager("data"));       (1)
        Scheduler scheduler = new Scheduler(schedulerOptions);
        String cronExpression = "0/1 * * * * ?";
        Runnable task = new Runnable()
        {
            public void run()
            {
                System.out.println("" + new Date() + " hello world! ");
            }
        };
        TaskOptions taskOptions = new TaskOptions();
        taskOptions.setPersist(true);                                                                                              (2)
        scheduler.schedule("testid", task, cronExpression, taskOptions);                                  (3)


1. Set a PersistManager in the scheduler options
2. Set persist in the task options
3. Schedule the task with a unique id.

If an application is stopped and later restarted, a cron schedule may be missed.
Setting a PersistManager will persist the next execution schedule.
Thus on restart missed tasks are executed immediatly.

Note: we store only the id of the task and the next schedule. Only tasks with an id are persisted.

yacron4j implements persistance using the prevayler project.
PrevaylerPersistentManager is created with a folder name.

One can add custom persisters by implementing the PersistManger interface.

Waiting for execution results



        final ScheduledFuture future = scheduler.schedule(callableTask, cronExpression);  (1)

        new Thread(new Runnable()
        {
            @Override
            public void run()
            {
                Object result = null;
                try
                {
                    while ((result = future.get()) != null)                                                                   (2)
                    {
                        System.out.println("got: " + result);
                    }
                }
                catch (Exception e)
                {
                    e.printStackTrace();
                }
            }
        }).start();


(1) Scheduling a task returns a future object.

(2) Future.get() blocks until an execution of the task has correctly terminated or if no further triggers are available.
It returns the result of the Callable or null in case of a Runnable.
If before invocation of Future.get() multiple results have been calculated these are retrieved by sequentially invoking Future.get().
It will block if no further results are available
It will return null if the task has terminated, eg no triggers are scheduled.

Cancelling a scheduled task



future = scheduler.schedule(task, cronExpression);
future.cancel()

Future.cancel() cancels the scheduling of a task. It does not interrupt current executions.

Shutting down the Scheduler



scheduler.shutdown(mayInterruptIfRunning)

The scheduler can be shut down at any time.
This will cancel all tasks and if mayInterruptIfRunning is true will interrupt all currently executing tasks.
After shutdown a scheduler cannot be reused.

Examples

The library comes with examples for the different configurations. Try these out and play around with the different option settings.

Third party libraries

This library uses the joda time (APL) library for DST calculations.

prevayler (BSD) is used for persistance.

It uses source code from the quartz scheduler (APL) project and from the netty (APL) project. These classes have been modified and their packages renamed to avoid conflicts when integrating into existing applicaitons.

Change History

can be found here