TimerService i JNDI 1. Usługa TimerServiece, interfejsy TimedObject, TimerService, Timer, TimerHandle transakcje, zastosowanie usługi w komponentach EJB, cykl życia EJB, problemy. 2. Zasoby JNDI i wstrzykiwanie, wypełnianie Enterpise Naming Context, odwołania do JNDI, rodzaje wsztrzyknięć. 1
Usługa TimerService Aplikacje biznesowe często wykorzystuj ą systemy harmonogramowania, które s ą odpowiedzialne przykładowo za: generowanie raportów, cykliczne operacje na danych, wykonywanie audytów. Usługa Timer Service kontenera EJB udostępnia interfejs zdarze ń okresowych. Licznik upływającego czasu jest związany z komponentem EJB i po upływie określonego limitu wywołuje na jego rzecz metod ę ejbtimeout() lub metod ę oznaczon ą adnotacj ą @javax.ejb.timeout. 2
Interfejs TimedObject Warunkiem korzystania przez komponent EJB z usługi Timer Service jest implementacja interfejsu: package javax.ejb; public interface TimedObject { public void ejbtimeout(timer timer) ; Przykład: @Stateless public class ShipMaintenanceBean implements ShipMaintenanceRemote, javax.ejb.timedobject { public void ejbtimeout(javax.ejb.timer timer) { // logika biznesowa... 3
Interfejs TimedObject Alternatywnie moż na skorzystać z adnotacji wybranej metody: @javax.ejb.timeout dla @Stateless public class ShipMaintenanceBean implements ShipMaintenanceRemote{ @Timeout public void maintenance(javax.ejb.timer timer) { // logika biznesowa... Metoda oznaczona t ą adnotacj ą musi posiadać sygnatur ę identyczn ą z przykładow ą metod ą maintance(). 4
Interfejs TimedObject Aby skorzystać z systemu harmonogramowania należy zdefiniować kiedy ma nastąpić powiadomienie. Przykład: // Utworzenie obiektu Calendar Calendar time = Calendar.getInstance( ); // aktualny czas time.add(calendar.date, 30); // dodanie 30 dni Date date = time.gettime( ); TimerService timerservice = // pobrany np. z EJBContext timerservice.createtimer( date, null); TimerService jest interfejsem poprzez który zarządzamy harmonogramem. 5
Interfejs TimerService package javax.ejb; public interface TimerService{ Timer createtimer(date initialexpiration, long intervalduration, Serializable info); Timer createtimer(date expiration, Serializable info); Timer createtimer(long initialduration, long intervalduration, Serializable info); Timer createtimer(long duration, Serializable info); Collection gettimers(); intervalduration liczba milisekund pomiędzy kolejnymi wywołaniami zdarzenia, info obiekt przekazywany przez referencje Timer wraz z każdym wywołaniem zdarzenia. 6
Wyjątki IllegalArgumentException brak daty (null) lub liczba ujemna określajaca czas, IllegalStateException metoda wywołana gdy instancja była w niewłaściwym stanie. Ogólnie wywołania mog ą nastąpić zewsząd poza metodami interfejsu EJBContext (setentitycontext(), setsessioncontext(), setmessagedrivencontext() itd.), EJBException błąd systemowy. 7
Odwołanie harmonogramu Aby anulować ustawiony wcześniej licznik czasu należy wywołać metod ę cancel(). Poniższy kod zawarty w metodzie clearschedule(), anuluje wszystkie ustawione wcześniej w ramach komponentu harmonogramy: @Stateless public class ShipMaintenanceBean { implements ShipMaintenanceRemote @Resource javax.ejb.timerservice timerservice; public void clearschedule() { for (Object obj : timerservice.gettimers()) { javax.ejb.timer timer = (javax.ejb.timer)obj; timer.cancel(); 8
Interfejs Timer package javax.ejb; public interface Timer { // Kasuje harmonogram public void cancel(); // Zwraca informacje związaną z harmonogramem w trakcie // jego stworzenia public java.io.serializable getinfo(); // Zwraca moment następnego zdarzenia public java.util.date getnexttimeout(); // Zwraca liczbę milisekund do następnego zdarzenia public long gettimeremaining(); //Get a serializable handle to the timer public TimerHandle gethandle(); 9
Interfejs TimerHandle Metoda gethandle() zwraca referencje do obiektu implementującego interfejs TimerHandle: package javax.ejb; public interface TimerHandle extends java.io.serializable { public Timer gettimer(); Jedyna metoda zwraca instancj ę Timer określając ą aktualny licznik czasowy. Referencja TimerHandle ma charakter lokalny i może być wymieniana pomiędzy komponentami w ramach kontenera EJB. 10
Transakcje Licznik czasowy jest tworzony w ramach bieżącej transakcji. Jeżeli transakcja zostanie wycofana (np. poprzez zgłoszenie wyjątku) licznik nie zostanie utworzony. Z tego powodu metody zwrotne komponentów, wywoływane na skutek wyzerowania odpowiednich liczników czasowych deklaruje si ę z atrybutem transakcji RequiresNew: @Timeout @TransactionAttribute(TransactionAttributeTypes.REQUIRES_NEW) public void timeoutmethod() {... 11
Bezstanowe komponenty sesyjne package com.titan.maintenance; import javax.ejb.remote; import java.util.date; @Remote public interface ShipMaintenanceRemote{ public void schedulemaintenance(string ship, String description, Date dateoftest); 12
Bezstanowe komponenty sesyjne package com.titan.maintenance; import javax.ejb.*; import java.util.date; import javax.annotation.resource; @Stateless public class ShipMaintenanceBean implements ShipMaintenanceRemote{ @Timeout public void maintenance(timer timer) { System.out.println("Wywołana metoda Timeout"); String scheduled = (String)timer.getInfo(); System.out.println(scheduled); 13
Bezstanowe komponenty sesyjne @Resource TimerService timerservice; public void schedulemaintenance(string ship, String description, Date dateof) { String item = ship + " podlega przeglądowi: " + description; for (Object obj : timerservice.gettimers()) { Timer timer = (Timer)obj; String scheduled = (String)timer.getInfo(); if (scheduled.equals(item)) { timer.cancel(); timerservice.createtimer(dateof, item); 14
Bezstanowe komponenty sesyjne Liczniki czasowe bezstanowych komponentów sesyjnych s ą związane z konkretnymi typami komponentów. Po wyzerowaniu licznika, kontener wybierze jeden spośród składowanych w puli komponentów i wywoła jego metod ę zwrotn ą. Każdy egzemplarz komponentu może obsługiwać żądania dowolnego klienta (w tym kontenera EJB). Komponent może uzyskać dostęp do TimerService zarówno w metodach @PostConstruct i @PreDestroy (choć jest to niezalecane) jak równie ż w metodach biznesowych. 15
Problemy z @PostConstruct 1. Nie wiadomo, czy metoda w ogóle zostanie wywołana. 2. Metoda może być wywoływana wiele razy, public class StatelessTimerBean implements TimedObject { static boolean istimerset = false; @Resource TimerService timerservice; @Resource SessionContext ctx; @PostConstruct public void init(){ if( istimerset == false) { long expirationdate = (Long)ctx.lookup("expirationDate"); timerservice.createtimer(expirationdate, null ); istimerset = true;... Powyższe rozwiązanie (statyczne flagi) nie pomoże w sytuacji gdy komponenty działaj ą w ramach kilku JVM. Usuwanie istniejących liczników jest i ustawianie nowego jest nieefektywne. 16
Cykl życia Class.newInstance() @PostConstruct metoda biznesowa Nie istnieje @PreDestroy W puli gotowych komponentów ejbtimeout() 17
Komponenty sterowane komunikatami Liczniki czasowe komponentów sterowanych komunikatami działaj ą podobnie jak w bezstanowych komponentach sesyjnych. @MessageDriven public class JmsTimerBean implements MessageListener { @Resource TimerService timerservice; public void onmessage(message message){ MapMessage mapmessage = (MapMessage)message; long date = 0; try { date = mapmessage.getlong("expirationdate"); catch(jmsexception e) { e.printstacktrace(); this.timerservice.createtimer(date, null ); @Timeout public void timeout( ){...... 18
Problemy TimerService Pomimo, ze liczniki czasowe s ą wartościowym dodatkiem do platformy EJB, posiadaj ą one liczne ograniczenia. Ich funkcjonalność jest znacznie mniejsza ni ż popularnego narzędzia cron. Standard nie przewiduje możliwości skonfigurowania i ustawienia licznika w momencie uruchomienia aplikacji. Ponadto, w trakcie obsługi zdarzenia, nie istnieje możliwość rozróżnienia liczników jednorazowych od cyklicznych. W związku z tym bardzo prawdopodobna jest zmiana specyfikacji usługi w przyszłych wersjach EJB lub JEE. 19
Zasoby JNDI i wstrzykiwanie Każdy kontener EJB dysponuje własnym rejestrem zwanym ENC (Enterprise Naming Context) implementowanym przez JNDI (Java Naming and Directory Interface). W kontekście ENC można umieszczać referencje do interfejsów EJB, kolejki i tematy obsługiwane przez JMS, źródła danych (zasoby JCA) oraz inne obiektowe zasoby jak równie ż typy proste. Zasoby mog ą być umieszczane w kontekście ENC poprzez deklaracje w plikach XML lub adnotacje w kodzie źródłowym. 20
Wypełnianie JNDI ENC <ejb-jar> <enterprise-beans> <session> <ejb-name>travelagentbean</ejb-name> <ejb-local-ref> <ejb-ref-name>ejb/processpayment</ejb-ref-name> <ejb-ref-type>session</ejb-ref-type> <local> com.titan.processpayment.processpaymentlocal </local> <ejb-link>processpaymentbean</ejb-link> </ejb-local-ref> </session> </enterprise-beans> </ejb-jar> Komponent TravelAgentBean będzie dysponował referencj ą do ProcessPaymentBean poprzez interfejs ProcessPaymentLocal. Komponent będzie dostępny poprzez nazw ą ejb/processpayment. 21
Wypełnianie JNDI ENC import javax.ejb.ejb; import javax.ejb.stateful; @Stateful @EJB(name="ejb/ProcessPayment", beaninterface=processpaymentlocal.class, beanname="processpaymentbean") public class TravelAgentBean implements TravelAgentRemote {... 22
Odwołanie do JNDI ENC Dostęp za pomoc ą jndi.lookup(): @Stateful public class TravelAgentBean implements TravelAgentRemote { public TicketDO bookpassage(creditcarddo card, double amount) { ProcessPaymentLocal payment = null; try { InitialContext ctx = new InitialContext(); payment = (ProcessPaymentLocal) ctx.lookup("java:comp/env/ejb/processpayment"); catch(namingexception ne){ throw new EJBException(ne); payment.process(card, customer, amount);... java:comp/env ścieżka do zasobów komponentu ( comp). 23
Odwołanie do JNDI ENC Dostęp za pomoc ą interfejsu EJBContext: @Stateful public class TravelAgentBean implements TravelAgentRemote { @Resource private javax.ejb.sessioncontext ejbcontext; public TicketDO bookpassage(creditcarddo card, double amount) { ProcessPaymentLocal payment = (ProcessPaymentLocal) ejbcontext.lookup("ejb/processpayment"); payment.process(card, customer, amount);... EJBContext może korzystać ze ścieżki względnej ejb/processpayment. 24
Odwołanie do JNDI ENC Dostęp za pomoc ą adnotacji ( com.titan.travelagentbean/payment): @Stateful public class TravelAgentBean implements TravelAgentRemote { @EJB private ProcessPaymentLocal payment;... lub te ż ( com.titan.travelagentbean/processpayment): @Stateful public class TravelAgentBean implements TravelAgentRemote { private ProcessPaymentLocal payment; @EJB public void setprocesspayment(processpaymentlocal payment){ this.payment = payment;... Drugi sposób jest szczególnie użyteczny w trakcie testów. 25
Odwołanie do JNDI ENC <ejb-jar> <enterprise-beans><session> <ejb-name>travelagentbean</ejb-name> <ejb-local-ref> <ejb-ref-name>processpayment</ejb-ref-name> <ejb-ref-type>session</ejb-ref-type> <local> com.titan.processpayment.processpaymentlocal </local> <ejb-link>processpaymentbean</ejb-link> <injection-target> <injection-target-class> com.titan.travelagent.travelagentbean </injection-target-class> <injection-target-name> payment </injection-target-name> </injection-target> </ejb-local-ref> </session></enterprise-beans> </ejb-jar> 26
Rodzaje wstrzyknięć komponenty EJB: @Target({TYPE, METHOD, FIELD) @Retention(RUNTIME) public @interface EJB { String name( ) default ""; // nazwa względem java:comp/env Class beaninterface( ) default Object.class; String beanname( ) default ""; String mappedname( ) default ""; // nazwa w globalnym // drzewie JNDI obiekt EntityManager: @Target({TYPE, METHOD, FIELD) @Retention(RUNTIME) public @interface PersistenceContext { String name( ) default ""; String unitname( ) default ""; // nazwa jednostki utrwalania // zdefiniowana w pliku persistance.xml PersistenceContextType type( ) default TRANSACTION; PersistenceProperty[] properties( ) default {; 27
Rodzaje wstrzyknięć obiekt EntityManagerFactory (uzyskiwany za poprzez JNDI): @Target({TYPE, METHOD, FIELD) @Retention(RUNTIME) public @interface PersistenceUnit { String name( ) default ""; String unitname( ) default ""; // nazwa jednostki utrwalania // zdefiniowana w pliku // persistance.xml przykład (JNDI): InitialContext jndicontext = new InitialContext( ); EntityManagerFactory titan = (EntityManagerFactory) jndicontext.lookup("java:comp/env/persistence/titandb"); 28
Rodzaje wstrzyknięć zasoby: @Target({TYPE, METHOD, FIELD) @Retention(RUNTIME) public @interface Resource { public enum AuthenticationType {CONTAINER, APPLICATION String name( ) default ""; Class type( ) default Object.class; AuthenticationType authenticationtype() default AuthenticationType.CONTAINER; boolean shareable( ) default true; String description( ) default ""; String mappedname( ) default ""; przykład: @Stateful @Resource(name="jdbc/OracleDB", type=javax.sql.datasource, mappedname="java:/defaultds") public class TravelAgentBean implements TravelAgentRemote {... 29
Rodzaje wstrzyknięć referencje do usług WebServices: @Target({TYPE, METHOD, FIELD) @Retention(RUNTIME) public @interface WebServiceRef { String name( ) default ""; String wsdllocation( ) default ""; Class type( ) default Object.class; Class value( ) default Object.class; String mappedname( ) default ""; przykład: @Stateful public class TravelAgentBean implements TravelAgentRemote { @WebServiceRef ProcessorService service; @WebServiceRef(ProcessorService.class) Processor endpoint;... 30
Podsumowanie Przedstawiono dwie usługi (TimerService i JNDI), które pomagaj ą przy tworzeniu aplikacji biznesowych. Pierwsza pozwala określać liczniki czasowe, potrzebne przy realizacji różnego rodzaju harmonogramów, druga udostępnia środowisko aplikacji poprzez które komponenty mog ą wymieniać si ę zasobami oraz uzyskiwać dostęp do różnorodnych funkcji zapewnianych przez kontener aplikacji. 31