Chime is time scheduler which works on Vert.x event bus and provides:

  • scheduling with cron-style or interval timers
  • applying time zones available on JVM
  • flexible timers management system:
    • grouping timers
    • defining timer start or end time
    • pausing / resuming
    • fire counting
  • sending messages in JSON
  • publish or send timer fire event to the address of your choice

Running.

Deploy Chime using Verticle.deployVerticle method.

    import io.vertx.ceylon.core {vertx}
    import herd.schedule.chime {Chime}
    Chime c = Chime().deploy(vertx.vertx());

Chime exchanges events with customers via event bus using JSON messages.

Configuration.

Following parameters could be specified in JSON verticle configuration:

  • “address” - address Chime is listen to, String, default is “chime”
  • “max year period limit” - limiting scheduling period in years, Integer, default is 10 years
  • “tolerance” - tolerance in milliseconds used to compare actual and requested times, Integer, default is 10 milliseconds

Scheduling.

Chime operates by two structures: timer and scheduler.
Scheduler is a set or group of timers. At least one scheduler has to be created before creating timers.
Each timer operates within some particular scheduler.
All messages Chime listens are to be sent to Chime address or to scheduler address.

Scheduler.

Scheduler messages.

In order to maintain schedulers send JSON message to Chime address (specified in configuration, “chime” is default) in the following format:

    {
        "operation" -> String // operation code, mandatory  
        "name" -> String // scheduler name, mandatory   
        "state" -> String // state, mandatory only if operation = 'state'   
    }

Chime listens event bus at “scheduler name” address with messages for the given scheduler.

Scheduler operation codes.

  • “create” - create new scheduler with specified name, state and description, if state is not specified, scheduler is put to running state.
  • “delete” - delete scheduler with name name. All timers belong to the scheduler are deleted.
  • “info” - request info on Chime or on a particular scheduler (scheduler name to be provided)
  • “state”:
    • if set to “get” then state has to be returned
    • if set to “running” then scheduler is to be set to running, which leads all non paused timers are running
    • if set to “paused” then scheduler is to be set to paused, which leads all timers are paused
    • otherwise error is returned

Scheduler request examples.

// create new scheduler with name "scheduler name"
JSON message = JSON { 
    "operation" -> "create", 
    "name" -> "scheduler name" 
} 

// change state of scheduler with "scheduler name" to paused
JSON message = JSON { 
    "operation" -> "state", 
    "name" -> "scheduler name",  
    "state" -> "paused"
} 

Scheduler response.

Chime responds on messages in JSON format:

    {
        "name" -> String // scheduler name  
        "state" -> String // scheduler state  
    }

or on “info” request with no or empty “name” field

    {
        "schedulers" -> JSONArray // Schedulers info. Each item contains name, state and a list of timers.  
    }

where each item of the array is in format:

    {
        "name" -> String // scheduler name  
        "state" -> String // scheduler state  
        "timers" -> JSONArray // list of scheduler timers
    } 

Where each item of the 'timers' array contains the same fields as provided with timer 'create' request (see below). Except:

  • 'state' which contains current state
  • 'count' which contains current number of fires.

Error response.

The error response is sent using Message.fail with corresponding code and message, see Chime.errors.

Requesting info for a number of schedulers.

Send message:

    JSON message = JSON { 
        "operation" -> "info", 
        "name" -> JSONArray{"name1", ...} 
    } 

I.e. name field contains JSON array with list of schedulers name. Chime responds with:

    JSON {
        "schedulers" -> JSONArray{...} 
    }

Where returned JSON array contains info for all schedulers the info is requested for.

Deleting all schedulers / timers

Send delete message to the Chime address with empty name or name equal to Chime address:

    eventBus.send (
        chimeAddress,
        JSON {
            Chime.key.operation -> Chime.operation.delete,
            Chime.key.name -> ""
        }
    );

Timer.

Once shceduler is created timers can be run within.

There are two ways to access a given timer:

  • sending message to “scheduler name” address using timer short name “timer name”
  • sending message to Chime address using full timer name which is “scheduler name:timer name”

Timer full name is scheduler name and timer name separated with ':', i.e. “scheduler name:timer name”.

Timer fire message is sent to timer full name address.

Timer request.

Request has to be sent in JSON format to scheduler name address with timer short name or to Chime address with timer full name.
Request format:

{  
    "operation" -> String // operation code, mandatory  
    "name" -> String // timer short or full name, mandatory  
    "state" -> String // state, nonmandatory, except if operation = 'sate'  

    // fields for create operation:
    "maximum count" -> Integer // maximum number of fires, default - unlimited  
    "publish" -> Boolean // if true message to be published and to be sent otherwise, nonmandatory  

    "start time" -> JSON // start time, nonmadatory, if doesn't exist timer will start immediately  
    {  
        "seconds" -> Integer // seconds, mandatory  
        "minutes" -> Integer // minutes, mandatory  
        "hours" -> Integer // hours, mandatory  
        "day of month" -> Integer // days of month, mandatory  
        "month" -> Integer or String // months, mandatory  
        "year" -> Integer // year, mandatory  
    }  

    "end time" -> `JSON` // end time, nonmadatory, default no end time  
    {  
        "seconds" -> Integer // seconds, mandatory  
        "minutes" -> Integer // minutes, mandatory  
        "hours" -> Integer // hours, mandatory  
        "day of month" -> Integer // days of month, mandatory  
        "month" -> Integer or String // months, mandatory  
        "year" -> Integer // year, mandatory  
    }  

    "time zone" -> String // time zone ID, nonmandatory, default server local  

    "description" -> JSON // timer desciption, mandatoty for create operation  
}  

Chime address could be specified in verticle configuration, default is “chime”.

Timer operation codes.

  • “create” - create new timer with specified name, state and description
  • “delete” - delete timer with name name
  • “info” - get information for timer (if timer name is specified) or scheduler (if timer name is not specified)
  • “state”:
    • if set to “get” timer state has to be returned
    • if set to “running” timer state is to be set to running
    • if set to “paused” timer state is to be set to paused
    • otherwise error is returned

Timer fires only if both timer and scheduler states are running.

Unique timer name.

The Chime may generate unique timer name automatically. Just follow next steps:

  1. Set “operation” field to “create”.
  2. Set “name” field to scheduler name (i.e. omit timer name).
  3. Fill “description” field with required timer data.
  4. Send message to Chime or scheduler address.
  5. Take the unique timer name from the response.

Supported timers.

Timer is specified within description field of timer creation request.

  • Cron style timer. Timer which is defined like cron:
    {  
        "type" -> "cron" // timer type, mandatory   
    
        "seconds" -> String // seconds in cron style, mandatory  
        "minutes" -> String // minutes in cron style, mandatory  
        "hours" -> String // hours in cron style, mandatory  
        "days of month" -> String // days of month in cron style, mandatory  
        "months" -> String // months in cron style, mandatory  
        "days of week" -> String // days of week in cron style, L means last, # means nth of month, nonmandatory  
        "years" -> String // year in cron style, nonmandatory           
    }  
    
    Following notations are applicable:
    • FROM-TO/STEP
    • FROM/STEP
    • FROM-TO
    • '*' means any allowed
    • month can be specified using digits (1 is for January) or using names (like 'jan' or 'january', case insensitive)
    • day of week can be specified using digits (1 is for Sunday) or using names (like 'sun' or 'sunday', case insensitive)

Month and day of week are case insensitive.


  • Interval timer. Timer which fires after each given time period (minimum 1 second):
    {  
        "type" -> "interval" // timer type, mandatory   
        "delay" -> Integer // timer delay in seconds, if <= 0 timer fires only once, mandatory
    }
    

Interval timer delay is in seconds

Response on a timer request.

Remember: timer request has to be sent to scheduler name address with timer short name or to Chime address with timer full name.

Chime responds on each request to a scheduler in JSON format:

{  
    "name" -> String //  timer name  
    "state" -> String // state  

    // 'Info' request also returns fields from timer 'create' request
}  

or as response on 'info' request with no or empty 'name' field specified - info for all timers is returned

{
    "timers" -> JSONArray // list of timer infos currently scheduled
}

Where each item of the array contains the same fields as provided with timer 'create' request. Except:

  • 'state' which contains current state
  • 'count' which contains current number of fires.

Error response.

The error response is sent using Message.fail with corresponding code and message, see Chime.errors.

Timer events

Timer sends or publishes to full timer name address two types of events in JSON:

  • fire event
    {  
        "name" -> String, timer name
        "event" -> "fire"
        "count" -> Integer, total number of fire times
        "time" -> String formated time / date
        "seconds" -> Integer, number of seconds since last minute
        "minutes" -> Integer, number of minutes since last hour
        "hours" -> Integer, hour of day
        "day of month" -> Integer, day of month
        "month" -> Integer, month
        "year" -> Integer, year
        "time zone" -> String, time zone ID
    }  
    
  • complete event
    {  
        "name" -> String, timer name
        "event" -> "complete"
        "count" -> Integer, total number of fire times
    }   
    

Complete event is always published in order every listener receives it.
While fire event may be either published or send depending on 'publish' field in timer create request.

The value at the 'event' key indicates the event type (fire or complete).

Timer full name is scheduler name and timer name separated with ':', i.e. “scheduler name:timer name”.

String formatted time / date is per ISO 8601.

Time zones.

Available time zones, actual availability may depend on particular JVM installation.

See also time zones and JRE.

Timer example.

    // creat new Scheduler with name "schedule manager" at first and then the timer
    eventBus.send<JSON> (
        "chime",
        JSON {
            "operation" -> "create",
            "name" -> "schedule manager",
            "state" -> "running"
        },
        (Throwable|Message<JSON> msg) {
            if (is Message<JSON> msg) {
                // create timer
                eventBus.send<JSON>(
                    "chime",
                    JSON {
                        "operation" -> "create",
                        "name" -> "schedule manager:scheduled timer", // full timer name == address to listen timer
                        "state" -> "running",
                        "publish" -> false, // timer will send messages
                        "max count" -> 3,
                        "time zone" -> "Europe/Paris",
                        "descirption" -> JSON {
                            "type" -> "cron", // timer type is 'cron'
                            "seconds" -> "27/30", // 27 with step 30 leads to fire at 27 and 57 seconds
                            "minutes" -> "*", // every minute
                            "hours" -> "0-23", // every hour
                            "days of month" -> "1-31", // every day
                            "months" -> "january-OCTOBER", // from January and up to October
                            "days of week" -> "sat#2,sunday", // at second Saturday and at each Sunday 
                            "years" -> "2015-2019"
                        }
                    },
                    (Throwable|Message<JSON?> msg) {
                        print(msg); // Chime replies if timer successfully created or some error occured
                    }
                );
            }
            else {
                print("time scheduler creation error: ``msg``");
            }
        }
    );

    // listen timer
    eventBus.consumer (
        "schedule manager:scheduled timer",
        (Throwable | Message<JSON?> msg) {
            ...
        }
    );

Scheduler and Timer interfaces.

Scheduler interface provides a convenient way to exchange messages with particular scheduler.
In order to connect to already existed scheduler or to create new one connectToScheduler() function can be used. The function sends create scheduler request to the Chime and wraps the event bus with implementation of Scheduler interface.

Timer interface provides a convenient way to exchange messages with particular scheduler.
To get an instance of the Timer call Scheduler.createIntervalTimer() or Scheduler.createCronTimer().

Example:

    connectToScheduler (
        (Throwable|Scheduler scheduler) {
            if (is Scheduler scheduler) {
                scheduler.createIntervalTimer (
                    (Throwable|Timer timer) {
                        if (is Timer timer) {
                            timer.handler (
                                (TimerEvent event) {...}
                            );
                        }
                        else {
                            // error while creating timer
                        }
                    }
                );
            }
            else {
                // error while creating / connecting to scheduler
            }
        },
        "chime", eventBus, "scheduler name"
    );

Error messages.

The error is sent using Message.fail with corresponding code and message, see Chime.errors.

possible errors (see Chime.errors):

  • “unsupported operation”
  • “operation has to be specified”
  • “scheduler doesn't exist”
  • “scheduler name has to be specified”
  • “scheduler state has to be one of - 'get', 'paused', 'running'”
  • “state has to be specified”
  • “timer already exists”
  • “timer doesn't exist”
  • “timer name has to be specified”
  • “timer type has to be specified”
  • “unsupported timer type”
  • “incorrect start date”
  • “incorrect end date”
  • “end date has to be after start date”
  • “unsupported time zone”
  • “timer description has to be specified”
  • “timer state has to be one of - 'get', 'paused', 'running'”
  • “delay has to be specified”
  • “delay has to be greater than zero”
  • “incorrect cron timer description”

Cron expressions.

Expression fields.

  • seconds, mandatory
    • allowed values: 0-59
    • allowed special characters: , - * /
  • minutes, mandatory
    • allowed values: 0-59
    • allowed special characters: , - * /
  • hours, mandatory
    • allowed values: 0-23
    • allowed special characters: , - * /
  • days of month, mandatory
    • allowed values 1-31
    • allowed special characters: , - * /
  • months, mandatory
    • allowed values 1-12, Jan-Dec, January-December
    • allowed special characters: , - * /
  • days of week, nonmandatory
    • allowed values 1-7, Sun-Sat, Sunday-Saturday
    • allowed special characters: , - * / L #
  • years, nonmandatory
    • allowed values 1970-2099
    • allowed special characters: , - * /

Names of months and days of the week are case insensitive.

Special characters.

  • '*' means all values
  • ',' separates list items
  • '-' specifies range, for example, '10-12' means '10, 11, 12'
  • '/' specifies increments, for example, '0/15' in seconds field means '0,15,30,45', '0-30/15' means '0,15,30'
  • 'L' has to be used after digit and means the last xxx day of the month, for example, '6L' means the last friday of the month
  • '#' has to be used with digits before and after: 'x#y' and means the y'th x day of the month, for example, '6#3' means the third Friday of the month
Platform: Java
By: Lis
License: The MIT License (MIT) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Packages
herd.schedule.chime

Chime.

Dependencies
ceylon.json1.3.0
ceylon.time1.3.2
io.vertx.ceylon.core3.4.1

Chime.

By: Lis
Since 0.1.0
Functions
connectToSchedulershared void connectToScheduler(Anything(Throwable|Scheduler) handler, String shimeAddress, EventBus eventBus, String name, Boolean paused = false)

Connects to scheduler. If scheduler has not been created yet then new one is created.

Parameters:
  • handler

    Handler to receive created scheduler or error if occured.

  • shimeAddress

    Address to call Chime.

  • eventBus

    Event bus to send message to Chime.

  • name

    Name of the scheduler to be connected or created.
    Must not contain ':' symbol, since it separates scheduler and timer names.

    • !name.contains( Chime.configuration.nameSeparator )
  • paused = false

    True if new scheduler is paused and false if running.
    If scheduler has been created early its state is not changed.

Throws
  • AssertionError

    scheduler name contains ':'

By: Lis
See also Scheduler
Since 0.2.0
schedulerInfoshared void schedulerInfo(Anything(Throwable|SchedulerInfo[]) handler, String shimeAddress, EventBus eventBus, String* names)

Returns info on the given schedulers.

Parameters:
  • handler

    Handler to receive scheduler infos or error if occured.

  • shimeAddress

    Address to call Chime.

  • eventBus

    Event bus to send message to Chime.

  • names

    List of scheduler name, the info to be requested for.
    If empty then info on all schedulers are requested.

By: Lis
Since 0.2.0
Interfaces
Schedulershared Scheduler

Wraps event bus to provide exchanging messages with previously created scheduler.
The object implementing interface is returned by connectToScheduler().

Timershared Timer

Wraps event bus to provide exchanging messages with previously created timer.
The object implementing interface is returned by Scheduler.createIntervalTimer() and Scheduler.createCronTimer().

Timer is sent timer fire or complete events with TimerEvent. To set timer event handler call Timer.handler().

Complete event is always published.

Classes
Chimeshared Chime

Chime scheduler verticle. Starts scheduling.

Ensure that the verticle is started just a once!

Static strings contain keys of the JSON messages and some possible values.

SchedulerInfoshared final SchedulerInfo

Info on the scheduler.

Stateshared State

Timer or scheduler state - running, paused or completed.

TimerCompletedshared final TimerCompleted

Represents timer complete event.

TimerEventshared abstract TimerEvent

Represents timer event: fire or complete.
Timer publishes or sends the event in JSON format to timer address when the timer fires or completes. Timer interface converts JSON event to TimerEvent.

Complete event is always published.

TimerFireshared final TimerFire

Timer fire event.

TimerInfoshared final TimerInfo

Info on the timer.