niedziela, 26 sierpnia 2012

EJB 3.1 [1]

"Enterprise JavaBeans is a standard server-side component model for distributed business applications."

Rodzaje komponentów:
1. Stateless session beans [SLSB]
2. Stateful session beans [SFSB]
3. Singleton beans
4. Message-driven beans [MDB]


1. Stateless session beans [SLSB]

Podstawowy komponent. Bezstanowy. Dla każdego SLSB kontener tworzy pulę instancji. Do obługi żądania zostaje przydzielona losowa instancja z puli. Do obsługi kolejnego żądania znów zostaje przydzielona losowa instancja. Być może zupełnie inna. Dlatego nie przechowujemy danych w zmiennych beana. Efekt może wyglądać następująco:
Mike: Hi, my name is Mike.
SLSB: Hello, Mike.
Mike: Do you know my name?
SLSB: Yes, your name is Jason.
Mike: What? That's wrong! I'm going!
SLSB: OK, later, Dave.  
Jak ze zwykłej klasy zrobić SLSB:
- Upewniamy się, że ma bezargumentowy konstruktor.
- Dodajemy adnotację @javax.ejb.Stateless.
- Tworzymy interfejs zawierający metody, które chcemy wystawić i adnotujemy go jako @javax.ejb.Local albo @javax.ejb.Remote. Adnotacje te można też umieścić na klasie, wskazując odpowiedni interfejs, np: @Local(SampleLocalEJBInterface.class). Od EJB 3.1 można w ogóle nie określać interfejsu, adnotując klasę jako @LocalBean.
Można określić 2 interfejsy, Local i Remote (ale nie można utworzyć jednego interfejsu pełniącego obie funkcje).
Interfejs lokalny służy do wywołań w ramach tej samej JVM. Jest wydajnieszy od zdalnego. Argumenty są przekazywane przez referencję (w przypadku zdalnego - przez wartość).
- Zamiast adnotacji można wykorzystać deskryptor wdrożenia: plik ejb-jar.xml w katalogu META-INF tworzonego jara. Większość adnotacji można nadpisać w xml-u (choć nie wszystkie - np. nie można nadpisać typu beana). Plik ejb-jar.xml jest opcjonalny. Nie musi być pełny, tzn. może służyć jako uzupełnienie dla adnotacji. W przypadku, gdy ten sam element jest określony poprzez adnotację i w pliku xml, pierwszeństwo ma konfiguracja w xml'u.

Cykl życia SLSB składa się z 2 stanów: [Does not exist] i [Method-Ready pool]. Kontener tworząc instancję wywołuje Class.newInstance(), wstrzykuje wszystkie zależności, a następnie wywołuje metodę zaadnotowaną jako @PostConstruct. Przed usunięciem instancji wywołuje metodę @PreDestroy. Metody takie [lifecycle callback methods] mogą, ale nie muszą istnieć (maksymalnie jedna dla każdej adnotacji).

Dostęp do kontekstu można otrzymać wstrzykując obiekt SessionContext:

Interfejs ten rozszerza EJBContext, określając dodatkowe metody:
- getBusinessObject() - metoda zwraca referencję do beana, na którym została wywołana, tzn. odpowiednik wskaźnika this.
- getInvokedBusinessInterface() - zwraca interfejs, który został użyty podczas wywołania (local, remote, webservice).
- przestarzałe metody: getEJBLocalObject() i getEJBObject() - przy próbie wywołania wyrzucony zostanie wyjątek.
Sam interfejs javax.ejb.EJBContext ma następujące metody:
- lookup(String name) - wyszukuje obiekty w ENC (Enterprise Naming Context) EJB'ka
- getTimerService( )
- bezpieczeństwo: getCallerPrincipal(); isCallerInRole();
- transakcje: getUserTransaction(); getRollbackOnly();setRollbackOnly();
- przestarzałe metody: getCallerIdentity(); isCallerInRole(); getEnvironment(); getEJBHome(); getEJBLocalHome();

W ejb-jar.xml można określić wpisy środowiskowe:

a następnie wstrzyknąć je poprzez:

lub wyciągnąć z ENC:

Metody asynchroniczne
Zostały wprowadzone w EJB 3.1. Można je stosować w SLSB, SFSB i Singletonach. W przypadku, gdy wywoływana metoda może trochę potrwać, można wpierw zlecić jej wykonanie, a dopiero za jakiś czas pobrać wynik. Przykład:

Wywołanie:

2. Stateful session beans [SFSB]

SFSB przeznaczone są do obsługi konwersjacji (klasyczny przykład: koszyk z zakupami). Z konkretną sesją użytkownika powiązana jest jedna, konkretna instancja. Nie ma puli. Zamiast @Stateless używamy @Stateful. Dochodzi trzeci stan w cyklu życia: [Passive] oraz metody @PrePassivate i @PostActivate. Przejście do stanu [Passive] może nastąpić np. gdy kontener uzna, że użytkownik poszedł na lunch i nie ma co zajmować pamięci przechowując jego beana - wówczas instancja zostaje "zserializowana" i wyrzucona z pamięci. W stanach [Method-ready pool] i [Passive] może wystąpić timeout - instancja przechodzi wówczas do stanu [Does not exist]. Specyfikacja nie określa, czy przy timeoucie wywołana zostanie metoda @PreDestroy (zależy to od dostawcy).

W SFSB jedna z metod, kończąca konwersację, powinna mieć adnotację @Remove - po wywołaniu takiej metody instancja zostaje usunięta.

Z uwagi na pasywację i aktywację, stan beana może składać się jedynie z prymitywów, obiektów serializowalnych oraz następujących typów: [SessionContext, UserTransaction, javax.naming.Context, EntityManager, EntityManagerFactory, referencje do innych EJB, referencje do "managed resource factories" (np. javax.sql.DataSource)]. Referencje do obiektów z tej listy zostaną automatycznie odtworzone podczas aktywacji.

Pasywacja i aktywacja przypominają serializację/deserializację z jednym wyjątkiem: podczas deserializacji obiekty oznaczone jako @Transient otrzymują swoje domyślne wartości (np. 0 dla int, null dla referencji do obiektów); podczas aktywacji wartości te nie są określone (mogą być losowe).

Jeżeli podczas wykonywania metody beana sesyjnego wyrzucony zostanie wyjątek systemowy, bean zostaje usunięty z pamięci, bez wywoływania metody @PreDestroy. Wyjątki systemowe są to wszystkie wyjątki rozszerzające RuntimeException, które nie zostały oznaczone jako @ApplicationException. Wyjątki systemowe zostają opakowane przez kontener w EJBException.


3. Singleton beans

W EJB 3.1 określono kolejny komponent - singleton. Jak nazwa wskazuje, w ramach JVM istnieje maksymalnie jedna instancja takiego beana. Adnotacja: @javax.ejb.Singleton. Dodatkowo istnieje adnotacja @javax.ejb.Startup, pozwalająca wymusić utworzenie instancji beana zaraz po uruchomieniu aplikacji (a kod, który powinien się wówczas wykonać, można umieścić w metodzie @PostConstruct).

Wraz z Singletonem pojawił się problem współbieżności. (W przypadku SLSB i SFSB współbieżnością zajmuje się kontener. Żądania do SLSB dostają kolejne instancje z puli, a żądania do SFSB czekają w kolejce. Można uniemożliwić kolejkowanie żądań do SFSB poprzez adnotację @javax.ejb.ConcurrencyManagement - przy współbieżnym żądaniu zostanie wówczas wyrzucony wyjątek.) Adnotacja @javax.ejb.ConcurrencyManagement(javax.ejb.ConcurrencyManagementType.CONTAINER/BEAN/NOT_SUPPORTED) określa sposób zarządzania współbieżnością. Domyślną wartością jest CONTAINER. Każda metoda ma wówczas przypisany blokadę typu WRITE. Można to zmienić poprzez adnotację @javax.ejb.Lock(javax.ejb.LockType.READ) na poziomie klasy lub poszczególnych metod. Można też określić timeouty, by blokada nie była trzymana w nieskończoność: @javax.ejb.AccessTimeout(timeout=15,unit=java.util.concurrent.TimeUnit.SECONDS). W przypadku ConcurrencyManagementType.BEAN deweloperowi pozostaje własnoręczna zabawa takimi słowami kluczowymi jak synchronized i volatile.


4. Message-driven beans [MDB]

MDB to komponenty bezstanowe. Nie posiadają interfejsu. Nie są wywoływane bezpośrednio przez klienta - pełną rolę listenerów dla przychodzących komunikatów. Adnotacja @MessageDriven posiada atrybut activationConfig, określający konfigurację specyficzną dla systemu komunikatów, któregu używamy, np.:



Specyfikacja EJB określa zestaw stałych właściwości:
- acknowledgeMode - Auto-acknowledge oznacza, żę potwierdzenie otrzymania komunikatu zostanie wysłane natychmiast. Dups-ok-acknowledge pozwala kontenerowi odwlec wysłanie potwierdzenia w czasie. W związku z tym system komunikatów może uznać, że komunikat zaginął po drodze i wysłać go jeszcze raz. Dups-ok-acknowledge istnieje ze względów wydajnościowych, ale w rzeczywistości niewiele daje. AcknowledgeMode jest zazwyczaj ignorowany (chyba że mamy do czynienia z BMT (bean managed transactions) lub gdy transakcje nie są wspierane (NotSupported transaction attribute)).
- messageSelector - Pozwala filtrować otrzymywane komunikaty. Dopuszczalne wyrażenia to pozdbiór wyrażeń warunkowych SQL-92. Warunki odnoszą się do dodatkowych nagłówków komunikatu, ustawianych w następujący sposób: message.setStringProperty("MessageFormat","Version 3.4");
- subscriptionDurability - Durable oznacza, że gdy system padnie na jakiś czas, po ponownym uruchomieniu otrzyma wszystkie komunikaty, które zostały dostarczone w tym czasie. Dla not durable, komunikaty te przepadną. SubscriptionDurability nie ma większego znaczenia w przypadku javax.jms.Queue - z samej natury kolejki wiadomość będzie czekać na odebranie.
- destinationType

Cykl życia MDB nie różni się od cyklu życia SLSB. Posiada dwa stany: [Does Not Exist] i [Method-Ready Pool].

MessageDrivenContext rozszerza interfejs EJBContext, nie dodając nic od siebie. Można go wstrzyknąć:

Metody związane z interfejsami i bezpieczeństwem, wywołane z poziomu MDB wyrzucą wyjątek - w ramach MDB nie mamy zdefiniowanego użytkownika; MDB nie mają też lokalnych ani zdalnych interfejsów.

MDB zazwyczaj implementują interfejs javax.jms.MessageListener z metodą void onMessage(Message message). Dla systemów innych niż JMS interfejs może być inny.

JMS określa 2 rodzaje destynacji:
- Queue (model point-to-point) - Wiadomości zostają umieszczone w kolejce, a następnie z niej pobrane. Co najważniejsze - wiadomość zostanie pobrana dokładnie raz. Jeżeli kolejka ma więcej niż jednego klienta pobierającego wiadomości - nie jest określone, kto konkretną wiadomość dostanie.
- Topic (model publish/subscribe) - Wiadomości zostają wysyłane do konkretnych "grup tematycznych". Mogą zostać pobrane wielokrotnie, przez wielu klientów.

Przykład wysłania wiadomości do kolejki:
- Za pomocą ConnectionFactory tworzymy obiekt Connection.
- W ramach połączenia tworzymy sesję (connection.createSession(true, 0)).
- W ramach sesji tworzymy obiekty MessageProducer i ObjectMessage (session.createProducer(queue); session.createObjectMessage())
- Wypełniamy wiadomość i wysyłamy (producer.send(message))
- Zamykamy połączenie (connection.close())

Argumenty metody createSession(boolean transacted, int acknowledgeMode) są ignorowane - zaleca się jednak wartości true i 0.
Typy komunikatów: TextMessage, MapMessage, ObjectMessage, StreamMessage, BytesMessage.

Poza JMS-based MDB istnieją jeszcze Connector-Based MDB - np. do komunikacji poprzez emaile lub SOAP.