Time scheduling with Chime

Eclipse Vert.x ex­e­cutes pe­ri­odic and de­layed ac­tions with pe­ri­odic and one-​shot timers. This is the base for time sched­ul­ing and reach fea­ture ex­ten­sion must be rather in­ter­est­ing. Be no­ti­fied at cer­tain date / time, take into ac­count hol­i­days, re­peat no­ti­fi­ca­tions until a given date, apply time zone, take into ac­count day­light sav­ing time etc. There are a lot of use­ful fea­tures time sched­uler may in­tro­duce to the Vert.x stack.

Chime

Chime is time sched­uler ver­ti­cle which works on Vert.x event bus and pro­vides:

  • sched­ul­ing with cron-​style, in­ter­val or union timers:
  • at a cer­tain time of day (to the sec­ond);
  • on cer­tain days of the week, month or year;
  • with a given time in­ter­val;
  • with nearly any com­bi­na­tion of all of above;
  • re­peat­ing a given num­ber of times;
  • re­peat­ing until a given time / date;
  • re­peat­ing in­fi­nitely
  • prox­y­ing event bus with con­ven­tional in­ter­faces
  • ap­ply­ing time zones avail­able on JVM with day­light sav­ing time taken into ac­count
  • flex­i­ble timers man­age­ment sys­tem:
  • group­ing timers;
  • defin­ing a timer start or end times
  • paus­ing / re­sum­ing;
  • fire count­ing;
  • lis­ten­ing and send­ing mes­sages via event bus with JSON;
  • pub­lish­ing or send­ing timer fire event to the ad­dress of your choice.

Chime is writ­ten in Cey­lon and is avail­able at Cey­lon Herd.

Running

Ceylon users

De­ploy Chime using Verticle.deployVerticle method.

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

Or with vertx.deployVerticle(\"ceylon:herd.schedule.chime/0.2.1\"); but en­sure that Cey­lon ver­ti­cle fac­tory is avail­able at class path.

Java users

  1. En­sure that Cey­lon ver­ti­cle fac­tory is avail­able at class path.
  2. Put Cey­lon ver­sions to con­sis­tency. For in­stance, Vert.x 3.4.1 de­pends on Cey­lon 1.3.0 while Chime 0.2.1 de­pends on Cey­lon 1.3.2.
  3. De­ploy ver­ti­cle, like:
vertx.deployVerticle("ceylon:herd.schedule.chime/0.2.1")

Ex­am­ple with Maven is avail­able at Github.

Schedulers

Well, Chime ver­ti­cle is de­ployed. Let’s see its struc­ture.
In order to pro­vide flex­i­ble and broad ways to man­age tim­ing two level ar­chi­tec­ture is adopted. It con­sists of sched­ulers and timers. Timer is a unit which fires at a given time. While sched­uler is a set or group of timers and pro­vides fol­low­ing:

  • cre­at­ing and delet­ing timers;
  • paus­ing / re­sum­ing all timers work­ing within the sched­uler;
  • info on the run­ning timers;
  • de­fault time zone;
  • lis­ten­ing event bus at the given sched­uler ad­dress for the re­quests to.

Any timer op­er­ates within some sched­uler. And one or sev­eral sched­ulers have to be cre­ated be­fore start­ing sched­ul­ing.
When Chime ver­ti­cle is de­ployed it starts lis­ten event bus at chime ad­dress (can be con­fig­ured). In order to cre­ate sched­uler send to this ad­dress a JSON mes­sage.

{
  "operation": "create",
  "name": "scheduler name"
}

Once sched­uler is cre­ated it starts lis­ten event bus at sched­uler name ad­dress. Send­ing mes­sages to chime ad­dress or to sched­uler name ad­dress are rather equiv­a­lent, ex­cept­ing that chime ad­dress pro­vides ser­vices for every sched­uler, while sched­uler ad­dress pro­vides ser­vices for this par­tic­u­lar sched­uler only.
The re­quest sent to the Chime has to con­tain op­er­a­tion and name keys. Name key pro­vides sched­uler or timer name. While op­er­a­tion key shows an ac­tion Chime has to per­form. There are only four pos­si­ble op­er­a­tions:

  • cre­ate - cre­ate new sched­uler or timer;
  • delete - delete sched­uler or timer;
  • info - re­quest info on Chime or on a par­tic­u­lar sched­uler or timer;
  • state - set or get sched­uler or timer state (run­ning, paused or com­pleted).

Timers

Now we have sched­uler cre­ated and timers can be run within. There are two ways to ac­cess a given timer:

  1. Send­ing mes­sage to chime ad­dress with ‘name’ field set to sched­uler name:timer name.
  2. Send­ing mes­sage to sched­uler name ad­dress with ‘name’ field set to ei­ther timer name or sched­uler name:timer name.

Timer re­quest is rather com­pli­cated and con­tains a lot of de­tails. In this blog post only basic fea­tures are con­sid­ered:

{
  "operation": "create",
  "name": "scheduler name:timer name",
  "description": {}
};

This is rather sim­i­lar to re­quest sent to cre­ate a sched­uler. The dif­fer­ence is only de­scrip­tion field is added. This de­scrip­tion is an JSON ob­ject which iden­ti­fies par­tic­u­lar timer type and its de­tails.
The other fields not shown here are op­tional and in­cludes:

  • ini­tial timer state (paused or run­ning);
  • start or end date-​time;
  • num­ber of re­peat­ing times;
  • is timer mes­sage to be pub­lished or sent;
  • timer fire mes­sage and de­liv­ery op­tions;
  • time zone.

Timer descriptions

Cur­rently, three types of timers are sup­ported:

  • In­ter­val timer which fires after each given time pe­riod (min­i­mum 1 sec­ond):
{
  "type": "interval",
  "delay": "timer delay in seconds, Integer"
};
  • Cron style timer which is de­fined with cron-​style:
{  
  "type": "cron",  
  "seconds": "seconds in cron style, String",  
  "minutes": "minutes in cron style, String",  
  "hours": "hours in cron style, String",  
  "days of month": "days of month in cron style, String",  
  "months": "months in cron style, String",  
  "days of week": "days of week in cron style, String, optional",  
  "years": "years in cron style, String, optional"  
};

Cron timer is rather pow­er­ful and flex­i­ble. In­ves­ti­gate spec­i­fi­ca­tion for the com­plete list of fea­tures.

  • Union timer which com­bines a num­ber of timers into a one:
{  
  "type": "union",  
  "timers": ["list of the timer descriptions"]  
};

Union timer may be use­ful to fire at a list of spe­cific dates / times.

Timer events

Once timer is started it sends or pub­lishes mes­sages to sched­uler name:timer name ad­dress in JSON for­mat. Two types of events are sent:

  • fire event which oc­curs when time reaches next timer value:
{  
  "name": "scheduler name:timer name, String",  
  "event": "fire",  
  "count": "total number of fire times, Integer",  
  "time": "ISO formated time / date, String",  
  "seconds": "number of seconds since last minute, Integer",  
  "minutes": "number of minutes since last hour, Integer",  
  "hours": "hour of day, Integer",  
  "day of month": "day of month, Integer",  
  "month": "month, Integer",  
  "year": "year, Integer",  
  "time zone": "time zone the timer works in, String"
};
  • com­plete event which oc­curs when timer is ex­hausted by some cri­te­ria given at timer cre­ate re­quest:
{  
  "name": "scheduler name:timer name, String",  
  "event": "complete",  
  "count": "total number of fire times, Integer"  
};

Ba­si­cally, now we know every­thing to be happy with Chime: sched­ulers and re­quests to them, timers and timer events. Will see some ex­am­ples in the next sec­tion.

Examples

Ceylon example

Let’s con­sider a timer which has to fire every month at 16-30 last Sun­day.

// listen the timer events
eventBus.consumer (
  "my scheduler:my timer",
  (Throwable|Message<JsonObject?> msg) {
    if (is Message<JsonObject?> msg) { print(msg.body()); }
    else { print(msg); }  
  }
);
// create scheduler and timer
eventBus.send<JsonObject> (
  "chime",
  JsonObject {
    "operation" -> "create",
    "name" -> "my scheduler:my timer",
    "description" -> JsonObject {
      "type" -> "cron",
      "seconds" -> "0",
      "minutes" -> "30",
      "hours" -> "16",
      "days of month" -> "*",
      "months" -> "*",
      "days of week" -> "SundayL"
    }
  }
);

’*’ means any, ‘Sun­dayL’ means last Sun­day.

If ‘cre­ate’ re­quest is sent to Chime ad­dress with name set to ‘sched­uler name:timer name’ and cor­re­spond­ing sched­uler hasn’t been cre­ated be­fore then Chime cre­ates both new sched­uler and new timer.

Java example

Let’s con­sider a timer which has to fire every Mon­day at 8-30 and every Fri­day at 17-30.

// listen the timer events
MessageConsumer<JsonObject> consumer = eventBus.consumer("my scheduler:my timer");
consumer.handler (
  message -> {
    System.out.println(message.body());
    }
);
// description of timers
JsonObject mondayTimer = (new JsonObject()).put("type", "cron")
  .put("seconds", "0").put("minutes", "30").put("hours", "8")
  .put("days of month", "*").put("months", "*")
  .put("days of week", "Monday");
JsonObject fridayTimer = (new JsonObject()).put("type", "cron")
  .put("seconds", "0").put("minutes", "30").put("hours", "17")
  .put("days of month", "*").put("months", "*")
  .put("days of week", "Friday");
// union timer - combines mondayTimer and fridayTimer
JsonArray combination = (new JsonArray()).add(mondayTimer)
  .add(fridayTimer);
JsonObject timer = (new JsonObject()).put("type", "union")
  .put("timers", combination);
// create scheduler and timer
eventBus.send (
  "chime",
  (new JsonObject()).put("operation", "create")
    .put("name", "my scheduler:my timer")
    .put("description", timer)
);

En­sure that Cey­lon ver­ti­cle fac­tory with right ver­sion is avail­able at class path.

At the end

herd.schedule.chime mod­ule pro­vides some fea­tures not men­tioned here:

  • con­ve­nient builders use­ful to fill in JSON de­scrip­tion of var­i­ous timers;
  • prox­y­ing event bus with con­ven­tional in­ter­faces;
  • read­ing JSON timer event into an ob­ject;
  • at­tach­ing JSON mes­sage to the timer fire event;
  • man­ag­ing time zones.

There are also some ideas for the fu­ture:

  • cus­tom or user-​defined timers;
  • lim­it­ing the timer fire time / date with cal­en­dar;
  • ex­tract­ing timer fire mes­sage from ex­ter­nal source.

This is very quick in­tro­duc­tion to the Chime and if you are in­ter­ested in you may read more in Chime doc­u­men­ta­tion or even con­tribute to.

Thank’s for the read­ing and enjoy with cod­ing!

Next post

Presentation of the Vert.x-Swagger project

This post is an introduction to the Vert.x-Swagger project, and describe how to use the Swagger-Codegen plugin and the SwaggerRouter class.

Read more
Previous post

Building a real-time web app with Angular/Ngrx and Vert.x

There are multiple tech stacks to build a real-time web app. What are the best choices to build Angular client apps, connected to a JVM-based backend?

Read more
Related posts

Real-time bidding with Websockets and Vert.x

The expectations of users for interactivity with web applications have changed over the past few years. Users during bidding in auction no longer want to press the refresh button.

Read more

Building services and APIs with AMQP 1.0

Microservices and APIs are everywhere. Everyone talks about them, presentation slides are full of them ... some people are actually even building them.

Read more

Scala is here

The rise of Scala as one of the most important languages on the JVM caught many (me included) by surprise. This hybrid of functional and imperative paradigms struck a chord with many developers.

Read more