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.

Podobnie jak w poprzednich przypadkach żeby 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>

Quartz API

Elementy Quartz API:

Scheduler - główna klasa Schedulera, manager zarządzający zadaniami,
Job - interfejs, który musi implementować zadanie,
JobDetail - definiuje instancje zadania i jego własności,
Trigger - definiuje harmonogram uruchamiania zadań.

Job & Scheduler

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 do wywołania Job musimy stworzyć jego instancję, aby to zrobić korzystamy z klasy generującej obiekty typu Scheduler, czyli 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.

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 QuartzAndCollectionsStart. Uruchom klasę SimpleScheduler z repozytorium, zobacz jak on działa i jak wywoływane są zadania.

Wykonaj

Wykorzystaj obiekt JobExecutionContext do wypisania na ekran czasu wykonania zadania.

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.

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.

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

Znak ‘/’ wykorzystywany razem z zakresem do oznaczenia kroku wykonania (co X sekund/dni/minut)

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

Więcej informacji można przeczytać w dokumentacji, tutaj:

Obszerna lista przykładów definicji cron schedulera :

Wykonaj

Zamień SimpleTrigger na CronTrigger.

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

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.

Wykonaj

Usuń adnotację @PersistJobDataAfterExecution, zobacz co się stanie.

Kolekcje

Kolekcje służą do przechowywania elementów w określonej kolejności, która pozwala na sprawne znalezienie elementów, wydajne wykorzystanie pamięci lub zapisywania elementów w określonej kolejności. Przykładami są Zbiory (Set), mapy (Map), kolejki (Queue) itp.

Implementacje kolekcji z biblioteki Eclipse Collections zapewniają efektywną pamięciowo implementację zbiorów i map, a także kolekcji podstawowych kolekcje.

Eclipse Collections powstało jako struktura kolekcji o nazwie Caramel wykorzystywana wewnętrznie przez bank Goldman Sachs w 2004 roku. po paru latach projekt został przeniesiony do open source pod nazwą GS Collections, a następnie do Eclipse Foundation, pod ostateczną nazwą Eclipse Collections w 2015 roku. Projekt jest w pełni otwarty i darmowy.

Zależność Mavena:

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

Struktury zaimplementowane w kolekcjach Eclipse są zwykle szybsze i zajmują mniej pamięci niż te z java.util. W kolekcjach Eclipse można ponadto znaleźć dodatkowe struktury, których nie można znaleźć w java.util. Jednym z nich jest MultiMapa.

MultiMapa to mapa, do której każdemu kluczowi można przypisać więcej niż jedną wartość. Wartości na mapie są następnie zapisywane jako lista wartości połączona z kluczem.

Przykład:

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

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

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

Więcej przykładów:

*

Wykorzystano materiały z:

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