Prawie wszystkie aplikacje zawierają funkcjonalność, która musi być wykonywana o określonych porach lub co zadany interwał, którą nazywamy przetwarzaniem wsadowym (ang. batch processing).
W Grails obsługa przetwarzania wsadowego opiera się na otwartym projekcie Quartz, który dostarczany jest w ramach podstawy infrastrukturalnej Grails - Spring Framework. Quartz jest podobny do uniksowego crona, który uruchamia zadania w przyszłości, z tą różnicą, że może korzystać z komponentów aplikacyjnych udostępnianych w ramach serwera aplikacyjnego.
Pracę z Quartz w Grails rozpoczynamy jego instalacją poleceniem grails install-plugin quartz. Wtyczka dostarcza nowe polecenie grails create-job.
Zadanie (ang. job) jest aplikacją, którą chcemy wykonać o zadanej porze. Określamy co i kiedy ma być wykonywane.
Tworzymy zadanie poleceniem grails create-job <nazwa-zadania>. Powstanie klasa <NazwaZadania>Job w grails/job. Konwencją Grails jest, że polecenia create-* tworzą klasy o zadanej przez użytkownika nazwie dodając przyrostek odpowiadający poleceniu, np. create-job first tworzy klasę FirstJob. Domyślna klasa zbudowana jest z atrybutu timeout, który określa, co ile wykonywane będzie zadanie zdefiniowane przez domknięcie execute().
W rozdziale znajduje się przykład tworzenia funkcjonalności tworzenia i rozsyłania raportów w trybie wsadowym oparte na klasach i usługach stworzonych w rozdziałach wcześniejszych.
Klasa zadania może zawierać opcjonalne atrybuty name oraz group. Definiują one, odpowiednio, nazwę zadania oraz jego grupę.
Domyślnie zadanie związane jest z sesją Hibernate, więc pobieranie danych z bazy nie wymaga specjalnych czynności. Wyłączenie wiązania zadania z sesją Hibernate jest możliwe, jeśli ustawimy atrybut sessionRequired na false.
Mamy dwie możliwości definiowania pory wykonania zadania - atrybuty startDelay z timeout lub cronExpression. Atrybut startDelay (domyślnie 0) określa, kiedy wykonane zostanie zadanie, licząc od uruchomienia aplikacji. Atrybut timeout (domyślnie 60000 ms = 1 min) określa interwał (w milisekundach) między kolejnymi wykonaniami zadania. Atrybut cronExpression to możliwość wykorzystania własnej wiedzy uniksowej dotyczącej demona cron. Deklarujemy wykonanie zadanie w formacie crontab.
Może się zdarzyć, że wykonanie zadania nie zakończy się, a kolejne zostanie uruchomione. Kontrola równoległego wykonywania zadań jest możliwa przez logiczny atrybut concurrent (false/true).
Format cronExpression składa się z 6 pól oddzielonych białym znakiem (spacja/tabulacja). Poszczególne pola odpowiadają sekundom, minutom, godzinom, dniom, miesiącom, dniom tygodnia i opcjonalnie, w 7. polu, latom. Specjalne znaki to gwiazdka (*), znak zapytania (?), myślnik (-), przecinek (,) oraz ukośnik (/).
Zadanie ma dostęp do automatycznego wiązania zależności bez specjalnej konfiguracji, więc dostęp do usługi grailsowej to po prostu zadeklarowanie pola o odpowiednim typie bądź nazwie. Tworzymy usługę poleceniem grails create-service Batch, a następnie w klasie zadania def batchService (alternatywnie BatchService batchService). Spring Framework zajmie się resztą i zagwarantuje, że pole nie będzie niezainicjowane.
Usługa BatchService pobiera wszystkich użytkowników z niepustym polem email (Listing 11-4):
def users = User.withCriteria {a następnie, wyłącznie jeśli users jest niepuste, pobiera listę zadań dla każdego z nich (Listing 11-4):
isNotNull('email')
}
users?.each { user ->Niesamowita "skromność" kodu w Grails! Przypomina mi to dawne konkursy w C, w którym zawodnicy tworzyli zaawansowane programy w formie jednolinijkowców i z bardzo wyrafinowanymi konstrukcjami. Trzeba było nie lada umysłu i doświadczenia, aby docenić "zalet" takiego programowania. W Grails to "przykrycie" funkcjonalności Hibernate oraz Spring Framework wraz z dynamizmem Groovy (chociażby przez "doklejenie" metod bazodanowych) nasuwa mi takie skojarzenia. Kod nie jest jednak tak zawiły, jak to miało miejsce w tych jednolinijkowcach w C.
def inputCollection = Todo.findAllByOwner(user)
}
W poprzedniej relacji z rozdziału 10. "Reporting" (patrz Raporty w Grails - rozdział 10 z "Beginning Groovy and Grails") mieliśmy okazję poznać sposób na dotarcie do ziaren springowych za pomocą atrybutu sesyjnego GrailsApplicationAttributes.APPLICATION_CONTEXT. Tym razem autorzy zademonstrowali inny, alternatywny sposób - użycie interfejsu ApplicationContextAware. Interfejs dostarcza metodę void setApplicationContext(ApplicationContext applicationContext), która przekazuje kontekst springowy, który z kolei możemy wykorzystać do pobrania dowolnego ziarna springowego:
EMailProperties eMailProperties = applicationContext.getBean("eMailProperties")Tak też można, skoro Grails to "nakładka" na Spring Framework, a Groovy to "nakładka" na Javę. Nie zapomnieliśmy o tym, prawda?
Kiedyś używałem Quartza w połączeniu ze Springiem, nie było to trudne, ale w przypadku Grooviego zostało maksymalnie uproszczone, fajnie!
OdpowiedzUsuńA co do ziaren springowych, to taki sam mechanizm istnieje w Struts2, a dokładnie we wtyczce struts2-spring-plugin, też mamy interfejs ApplicationContextAware i wstrzykiwanie kontekstu Springa do akcji przez interceptor. Ciekawa kto od kogo podpatrzył? ;-)
podejrzewam, że wszyscy od sprnga:
OdpowiedzUsuńhttp://static.springframework.org/spring/docs/2.0.x/api/org/springframework/context/ApplicationContextAware.html