TimeService i JNDI

Transkrypt

TimeService i JNDI
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 @javax.ejb.Timeout dla
wybranej metody:
@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

Podobne dokumenty