Alright fellas, put your mask on ! we are about to do a dissection of a scala library called Akka Quartz Scheduler. (Eww!@#$* does it stink?) Well not literally, lets find out what is it first, then according to your nose you will get to know how does it smell 😀 :D.
Akka Quartz Scheduler is a library which gets used for scheduling jobs. Now if you’re thinking that you already have a scheduler in Akka which could does all the scheduling part for you, then you might be wrong, as Viktor Klang
points out about the akka scheduler – ‘Perhaps the name “Scheduler” was unfortunate, “Deferer” is probably more indicative of what it does.’. Well as you have probably guess by now your Akka.system.scheduler.schedule()
or Akka.system.scheduler.scheduleOnce()
won’t work when you have to do a job like schedule a job at 5.30 pm on Friday every week. You probably could do all the calculation on Tuesday and set the exact time on it, it will work fine until and unless your application goes down for some reason. Then you have to recalculate again and might need to add some more logic into it. Most of the time if we don’t find a library in scala we try to find a similar library in java because they are interoperable. Quartz is what we are talking about here, for this kind of scheduling we use Quartz in java hence we can use it in scala too. Akka-Quartz-Scheduler is also doing the same, it takes care of some of the typical stuffs for you, so that you don’t have handle all that JobDataMap
and that sort of stuffs. It wraps all those stuffs for you and what you need to interact with is the Actors. Now if you want to know more about it you can visit their github link. I am going to let you know how you can use this library in your application in the next section and if you are more interested in “How does it work?” rather than “How to use it?” you can skip to the dissection part or checkout the source code yourself. Now lets see –
How to use it
In order to know how to use it lets checkout a repo that I have created in github. Click here to go to the repo, and check out into your directory. So here is the directory structure of the example app
Here I have created two actors as FirstActor and SecondActor and they are in the actors directory. And then there is the scheduler in the ‘scheduler’ directory. Now if you look into the code you will see that I just have a function is called scheduled and it is doing the scheduling part. Firstly the ActorSystem on which the actors should run is being fetched and then ActorRef for the actors are being created. The two lines i.e.
QuartzSchedulerExtension(_system).schedule(CRON_FIRST_EXPRESSION, firstReceiver, Message1) QuartzSchedulerExtension(_system).schedule(CRON_SECOND_EXPRESSION, secondReceiver, Message2)
are doing the scheduling part using the Akka-Quartz-Scheduler. The QuartzSchedulerExtension is taking the Akka System on which it will run the scheduling. It’s function schedule
takes three parameters, which are – the cron expression on what the schedule job will be occurred, the ActorRef which will be sent a message when the trigger occurred and the message which needs to be passed to the actor.
So the next question would be “how can we test them?”, well for that just go to the test section in the repo and you will find a test case for the scheduler. In this test case we are loading a different configuration apart from the normal configuration files. So for the first actor it is taking a cron expression to fire in every 5 seconds and for the second actor in every 10 seconds. so if we run the test for 10 seconds should get a sequence of Tock which size would be 3.
Code Dissection
Okay, so finally let’s go through the dissection part now. The Akka-Quartz-Scheduler library consist of four main scala files and package object. Which are QuartzCalenders
, QuartzJob
, QuartzSchedulerExtension
and QuartzSchedules
.
QuartzSchedulerExtension
is what I have used for scheduling in the code.So lets first pick that file only. QuartzSchedulerExtension
extends Akka’s Extension
therefore it gets instantiated once per ActorSystem
. When we use this we pass an ActorSystem
to it which is received by the ExtendedActorSystem
. It loads the configuration from the section akka.quartz
. Then loads all the configuration from there, like the threadCount
, threadPriority
, daemonThreads configuration
, defaultTimezone
etc. These configurations are then used for different initializations. After this configurations it initializes the schedules and runningJobs, where running Job is a simple mutable map with the Quartz’s(java library) JobKey. On the other hand schedules
is a Map of String, QuartzSchedule
, which we will dissect next when we will be done with QuartzSchedulerExtension
, but just to go with the flow you should know that QuartzSchedule
is used to create the Jobs and Triggers.It starts scheduler and initializes the calendar. Wow Where is this scheduler
all of a sudden come from? Well it’s a lazy protected val in the QuyartzSchedulerExtension
class and its task is to initializing the Quartz Scheduler with the help of the threadPool setting (with the help of threadCount, threadPriority and daemonThreads settings from the configuration) that we have loaded previously and jobStore (which is just using the Ram for the time being). It gives scheduler a name with the system’s name, that we have passed to QuartzScheduler
. Then it creates the QuartzScheduler
and register it on the termination hook of the system, which will shutdown the scheduler if the actors are ending. and finally it returns the scheduler object that has been created.
So what does the initialiseCanlenders do? Well it initializes the QuartzCalenders
which is an another object of the library we have already seen. So in order to continue we just have to know that QuartzCalendars
is returning a Map of name(String) and Calendar (Quartz Calendar of java library), where all the calendars are being set in the scheduler along with names.
Now let’s move to the function schedule that we have used in our example. This function takes the name of the Job (that needs to be schedule), the actor ref (to handle the job) and the message (to pass to the actor to start the job). It creates a jobDataMap
of type String and AnyRef, which is later on gets converted into a java map and creates a jobData
object. This jobData
object is then used to create an actual job(java library JobDetail
). This job is then added to the runningJobs
variable(which we have initialised earlier). A trigger is being created through QuartzSchedule
object and then scheduler
schedules a job with this two item job and the trigger. Now you might wonder where is this QuartzSchedule
comes from, well we get this object through the name that is being passed to the function, which is being used to fetch the object from the schedules
(This is being initialised at the beginning with the help of QuartzSchedules
).
There are other functions there as well such as resumeAll, cancelJob, resumeJob, suspendJob
which are works on the runningJobs
to do the job according to their name. We can check if the scheduler has started, we can make as well as check the scheduler standby. One more important function here is the createSchedule
which is used to create a schedule programmatically i.e. without using the configuration. For that you have to send the name of the job, the cron expression for it and additionally you can send the calendars
, timezone
etc. and finally you have to call the schedule by passing all the parameters that it needs. See the below code as an example
QuartzSchedulerExtension(_system).createSchedule("schedule_name", Some("description"), "*/10 * * ? * *") QuartzSchedulerExtension(_system).schedule("schedule_name", secondReceiver, Message2)
So in summary of this object what we can say is that,
1. It gives us two ways of scheduling by loading from the configuration or by scheduling jobs programmatically through the createSchedule function.
2. As it extends the Akka’s Extension it is going to be singleton for that particular ActorSystem.
3. We can do other operations as resumeAll, cancelJob etc.
(To be continued….)
>>>>>Next>>>>>
Reblogged this on pranjut.