PRA03ENG.rst

Programming Laboratory

Cron - the Quartz library

Colloquially, we call Cron a mechanism of running programs cyclically or at a specific time.

It can be defined in operating system, e.g. in the linux system:

Or using a programming language (through the library), for example in Java we can use library Quartz:

Quartz

Quartz is a Java library for defining tasks and running them according to a given schedule. It can also be used to create recurring events of the Cron type.

As in previous cases, to start working with a library, you must define the following dependencies in the pom.xml:

<dependency>
    <groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz</artifactId>
    <version>2.2.1</version>
</dependency>
<dependency>
    <groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz-jobs</artifactId>
    <version>2.2.1</version>
</dependency>

Quartz API

Elements of the Quartz API:

Scheduler - main Scheduler class - manager for schedules tasks,
Job - interface that must be implemented by the class being the task,
JobDetail - class that defines the instance of the task and its properties,
Trigger - class that defines the schedule of running the tasks.

Job & Scheduler

By Job we will be calling a class that we will be able to run multiple times using a scheduler / cron.

Job is a class that implements the org.quarts.Job interface. In other words class having the execute() method - it will be launched at the start of the task. The object JobExecutionContext containing information about environment variables and the call settings is passed to the Job.

public class MyJob implements org.quartz.Job {

      public MyJob() {
      }

      public void execute(JobExecutionContext context) throws JobExecutionException {
          System.err.println("Hello World!  MyJob is executing.");
      }
}

Before we use Scheduler to call Job we need to create its instance, in order to do this we use a class that generates Scheduler objects, i.e. SchedulerFactory. Scheduler can be started, switched to stand-by and turned off. Tasks will not run until the Scheduler is started and will not be run when it is stopped.

Example:

import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.impl.StdSchedulerFactory;
import static org.quartz.JobBuilder.*;
import static org.quartz.TriggerBuilder.*;
import static org.quartz.SimpleScheduleBuilder.*;

public class QuartzTest {

    public static void main(String[] args) {

        try {
            // Grab the Scheduler instance from the Factory
            Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();

            // and start it off
            scheduler.start();

            // define the job and tie it to our HelloJob class
            JobDetail job = newJob(HelloJob.class)
            .withIdentity("job1", "group1")
            .build();

            // Trigger the job to run now, and then repeat every 1 seconds
            Trigger trigger = newTrigger()
            .withIdentity("trigger1", "group1")
            .startNow()
            .withSchedule(simpleSchedule()
            .withIntervalInSeconds(1)
            .repeatForever())
            .build();

        // Tell quartz to schedule the job using our trigger
            scheduler.scheduleJob(job, trigger);

            scheduler.shutdown();

        } catch (SchedulerException se) {
            se.printStackTrace();
        }
    }
}

Task

Go to branch QuartzAndCollectionsStart. Run the SimpleScheduler class from the repository, see how it works and how the tasks are executed.

Task

Use JobExecutionContext to print on the screen time when the job has been executed.

Trigger

The elements of the triggers are:

withIdentity - name of the trigger, possibly the name of the group to which it belongs (we can group triggers),
startAt - start time (or startNow()),
withSchedule - start up schedule.

SimpleTrigger is used for single task run or if you want to run them N times in time intervals T.

CronTrigger is useful if the tasks are run according to the calendar, e.g. "every Friday, noon" or "at 10:15 on the 10th of the month."

Each time a task is executed a new Job object instance is created, therefore we can not exchange data between subsequent class starts within the task class.

Cron

Cron Expressions

For the definition of cron processing, we use the so-called cron expression. Cron-Expressions is an sequence of seven parts separated by a space, defining the details of the schedule in following order:

Seconds
Minutes
Hours
Day-of-Month
Month
Day-of-Week
Year (optional field)

E.g. "0 0 12 ? * WED" - means "every Wednesday at 12:00:00 pm".

Each field can contain a range or list of elements after the decimal point. For example, the day of the week may receive the value of "WED" as well as "MON-FRI", "MON, WED, FRI" or "MON-WED, SAT".

The ‘*’ character means every possible value

The ‘?’ character can be used for a day-of-month and a day-of-week in case one of these elements takes on a specific value and the other one does not take any specific value (as in the example above).

The ‘/’ character is used in conjunction with ranges to specify step values.

Note that frequencies in general cannot be expressed; only step values which evenly divide their range express accurate frequencies (for minutes and seconds, that's /2, /3, /4, /5, /6, /10, /12, /15, /20 and /30 because 60 is evenly divisible by those numbers; for hours, that's /2, /3, /4, /6, /8 and /12); all other possible "steps" and all other fields yield inconsistent "short" periods at the end of the time-unit before it "resets" to the next minute, second, or day; for example, entering */5 for the day field sometimes executes after 1, 2, or 3 days, depending on the month and leap year; this is because cron is stateless (it does not remember the time of the last execution nor count the difference between it and now, required for accurate frequency counting—instead, cron is a mere pattern-matcher).

More information can be read in the documentation, here:

An extensive list of examples of the cron scheduler definition:

Task

Change SimpleTrigger into CronTrigger.

cronSchedule("0/1 * * * * ?")

If we want to pass a parameter to the task, we can do it through the class JobDetail.

JobDataMap

JobDataMap is a map of objects related to JobDetail.

Example:

// define the job and tie it to our DumbJob class
JobDetail job = newJob(DumbJob.class)
    .withIdentity("myJob", "group1") // name "myJob", group "group1"
    .usingJobData("jobSays", "Hello World!")
    .usingJobData("myFloatValue", 3.141f)
    .build();

Job implementation example:

@PersistJobDataAfterExecution
public class DumbJob implements Job {

    public DumbJob() {
    }

    public void execute(JobExecutionContext context)
          throws JobExecutionException
    {
      JobKey key = context.getJobDetail().getKey();

      JobDataMap dataMap = context.getJobDetail().getJobDataMap();

      String jobSays = dataMap.getString("jobSays");
      float myFloatValue = dataMap.getFloat("myFloatValue");

      System.err.println("Instance " + key + " of DumbJob says: " + jobSays + ", and val is: " + myFloatValue);
    }
  }

In order for JobDataMap to be copied between subsequent job invocations, we need to set an annotation @PersistJobDataAfterExecution before the class definition.

Task

Run the JobMapScheduler class, see how the JobWithMap class is defined, how the values from JobDataMap are written and red.

Task

Remove the annotation @PersistJobDataAfterExecution, see what will happen.

Collections

Collection are used to store elements in specific order that allows to find the elements efficiently, safe memory space or order elements in specific sequence. The examples are Sets, Maps, Queues etc.

Eclipse Collections are collections implementation that provides memory efficient implementation of Sets and Maps as well as primitive collections.

The origin of Eclipse Collections was started off as a collections framework named Caramel at a bank Goldman Sachs in 2004. To maximize the best nature of open source project, GS Collections has been migrated to the Eclipse Foundation, re-branded as Eclipse Collections in 2015. Now the framework is fully open to the community, accepting contributions.

Maven dependencies:

<dependency>
    <groupId>org.eclipse.collections</groupId>
    <artifactId>eclipse-collections-api</artifactId>
    <version>9.2.0</version>
</dependency>

<dependency>
    <groupId>org.eclipse.collections</groupId>
    <artifactId>eclipse-collections</artifactId>
    <version>9.2.0</version>
</dependency>

Structures implemented in Eclipse Collections are usually faster and more memory-efficient that from java.util. You can find in Eclipse collections an additional structures which can not be found in java.util. One of them is multiMap.

MultiMap is a map for which every key can be assigned more than one value. Values in the map are then stored as a list connected with the key.

Example:

FastListMultimap<String, String> citiesToPeople = FastListMultimap.newMultimap();

citiesToPeople.put("Poznan", "Nowak");
citiesToPeople.put("Poznan", "Kowalski");

citiesToPeople.get("Poznan")
    .forEach(name -> System.out.println(name));

More examples:

*

Used materials from:

http://www.quartz-scheduler.org/