PRA03.rst

Pracownia Programowania

Cron - biblioteka Quartz

Potocznie Cronem nazwiemy proces uruchamiający programy cyklicznie lub o określonej porze.

Może być zdefiniowany systemowo, np w systemie linux:

Lub programowo (przez bibliotekę) np :

Quartz

Quartz jest biblioteką Javy do definiowania zadań i ich uruchamiania zgodnie z zadanym terminarzem. Może także być wykorzystywana do tworzenia zdarzeń cyklicznych w typie zadań Crona.

Strona główna podpowiada nam, że by zacząć pracę z biblioteką należy zdefiniować następujące zależności:

<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>

Job

Przez Job będziemy nazywać klasę, którą będziemy mogli uruchamiać wiele razy za pomocą schedulera / crona.

Job jest to klasa która implementuje interfejs org.quarts.Job (czyli innymi słowy posiadającą metodę execute()). Będzie ona uruchamiana przy uruchomieniu zadania. Do obiektu Job przekazywany jest obiekt JobExecutionContext posiadający informacje o zmiennych środowiskowych oraz wywołaniu.

public class MyJob implements org.quartz.Job {

      public MyJob() {
      }

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

Zanim użyjemy Schedulera musimy stworzyć jego instancję, aby to zrobić korzystamy z SchedulerFactory. Scheduler możemy wystartować, przełączyć w tryb stand-by oraz wyłączyć. Zadania nie są uruchamiane dopóki Scheduler nie zostanie uruchamiany jak i nie są uruchamiane gdy jest on zatrzymany.

Elementy Quartz API:

Scheduler - główna klasa Schedulera,
Job - interfejs, który musi implementować zadanie,
JobDetail - definiuje instance zadania i jego własności,
Trigger - definiuje harmonogram uruchamiania zadań,

Podstawowy przykład:

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();
        }
    }
}

Wykonaj

Wejdź na branch Feature/ThirdClass. Uruchom klasę SimpleScheduler z repozytorium, zobacz jak on działa i jak wywoływane są zadania.

SimpleTrigger służy do pojedynczego uruchomienia zadania lub jeżeli chcesz je uruchamiać N razy w odstępach czasu T. CronTrigger jest przydatny w przypadku, gdy zadania są uruchamiane zgodnie z kalendarzem np “w każdy piątek, w południe” or “o 10:15 10-tego dnia miesiąca.”

Za każdym razem gdy zadanie jest wykonywane tworzona jest nowa instancja obiektu Job, w związku z tym nie możemy zapisywać danych między kolejnymi uruchomieniami klasy wewnątrz klasy zadania.

Jeżeli chcemy przekazywać jakieś parametr do zadania możemy to robić poprzez klasę JobDetail.

JobDataMap

JobDataMap jest mapą obiektów związanych z JobDetail.

Przykład:

// 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();

Implementacja zadania:

@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);
    }
  }

Aby JobDataMap był kopiowany między kolejnymi wywołaniami zadania musimy ustawić przed definicją klasy adnotację @PersistJobDataAfterExecution.

Wykonaj

Uruchom klasę JobMapScheduler z repozytorium, zobacz jak zdefiniowana jest klasa JobWithMap, jak zapisywane i odczytywane są wartości z JobDataMap.

Trigger

Elementy trigerów to:

withIdentity - nazwa trigera, ew. nazwa grupy do której należy (możemy grupować trigery),
startAt - czas startu (ew. startNow()),
withSchedule - harmonogram uruchomienia.

Cron

Cron Expressions

Do definicji przetwarzania crona używany tzw. cron expression. Cron-Expressions to napis złożony z siedmiu części oddzielonych spacją, wyznaczających szczegóły harmonogramu, kolejno

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

Np. "0 0 12 ? * WED” - oznacza “w każdą środę 0 12:00:00 pm”.

Kazde pole może zawierać zakres lub listę elementów po przecinku. Np. dzień tygodnia może otrzymać wartość "WED” jak i “MON-FRI”, “MON,WED,FRI”, czy też “MON-WED,SAT”.

Znak ‘*’ oznacza każdą możliwą wartość

Znak ‘?’ można wykorzystać dla day-of-month oraz day-of-week w przypadku gdy jeden z tych elementów przyjmuje specyficzną wartość a drugi chcemy żeby nie przyjmował żadnej wartości konkretnej (jak w przykładzie powyżej).

Więcej informacji można przeczytać tutaj:

Obszerna lista przykładów definicji cron schedulera :

Projekt z zakresu 1-2

Stworzenie programu, który będzie tworzyć listę zapytań SQL i zapisywać je do pliku.

Użytkownik podaje numer zadania i w kolejnej linii zapytanie SQL (takie jak odpowiedź na zadanie z Baz Danych). Zakładamy, ze użytkownik zawsze wpisze poprawny numer jednak zapytanie SQL powinno być w prostu sposób parsowane (poprzez wyszukanie słów kluczowych SELECT, FROM, WHERE, ORDER BY i określenie czy podane były w danej kolejności, czy w złym porządku). W przypadku złego porządku odpowiedź nie jest zapisywana a użytkownik otrzymuje komunikat o błędzie.

Każde uruchomienie tworzy nowy plik odpowiedzi (program nie odczytuje żadnych danych).

W przypadku podania dwa razy tego samego numeru zadania odpowiedź jest nadpisywana.

  • co 30 sekund licząc od pełnych minut wpisane odpowiedzi zostają automatycznie zapisane do pliku odp.txt. Odpowiedzi w pliku mają być posortowane względem numerów zadań. (Uwaga! Użytkownik może w programie wpisywać odpowiedzi w dowolnej kolejności).

  • co pełną minutę od Pn-PT w godzinach 8.15-18.45 będzie pokazywać na ekranie komunikat "X minut do końca zajęć/przeryw". Zamodeluj godziny zajęć i przerw jak na WMI UAM.

Napisz testy do danych klas - co najmniej dla metody sprawdzającej poprawność zapytania SQL, oraz metody sprawdzającej czy dany czas jest przerwą czy zajęciami.

Proponuję kod tworzyć u siebie na repozytorium w gałęzi master.

*

Wykorzystano materiały z:

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