30 stycznia 2008

EJB 3.0 - Rozdział 12.3 Interceptory biznesowe

Na dzisiaj przygotowałem krótkie streszczenie z wprowadzenia do rozdziału 12.3 Interceptory biznesowe (interceptory metod biznesowych).

Metody przechwytujące wywołania metod biznesowych (dalej: interceptory biznesowe) ziarna mogą być zdefiniowane dla metod biznesowych ziaren sesyjnych i sterowanych komunikatami (MDB). Interceptory biznesowe określane są adnotacją @AroundInvoke lub elementem around-invoke w deskryptorze wdrożenia ejb-jar.xml.

Metody AroundInvoke mogą być zdefiniowane w nadklasach klasy ziarna lub interceptora. Jednakże, jedynie pojedyńcza metoda AroundInvoke może być zdefiniowana dla danej klasy. Metoda AroundInvoke nie może być metodą biznesową ziarna.

Metody AroundInvoke mogą być publiczne (public), prywatne (private), chronione (protected) lub o widoczności pakietu (aka package protected). Nie mogą być final lub static.

Metody AroundInvoke mają następującą sygnaturę:

Object <METODA>(InvocationContext) throws Exception

Metoda AroundInvoke może wywołać dowolny komponent lub zasób, który może wywołać metoda biznesowa.

Wykonanie interceptora biznesowego występuje w ramach tej samej transakcji i kontekstu bezpieczeństwa jak metoda biznesowa, której wykonanie spowodowało jej uruchomienie.

Metody interceptora biznesowego mogą być zdefiniowane per metoda zamiast per klasa. Zdefiniowanie interceptora biznesowego na poziomie klasy związuje go ze wszystkimi metodami biznesowymi ziarna.

Rozszerzmy przykład z relacji EJB 3.0 - Rozdział 12.2 Cykl rozwojowy interceptora o kolejny interceptor biznesowy - pl.jaceklaskowski.ejb3.interceptors.OdnotujWyplateInterceptor.

package pl.jaceklaskowski.ejb3.interceptors;

import java.util.logging.Logger;
import javax.annotation.Resource;
import javax.ejb.SessionContext;
import javax.interceptor.AroundInvoke;
import javax.interceptor.InvocationContext;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

public class OdnotujWyplateInterceptor {

Logger logger = Logger.getLogger(OdnotujWyplateInterceptor.class.getName());

@PersistenceContext
EntityManager em;

@Resource
SessionContext beanContext;

@AroundInvoke
void aroundInvokeBusinessMethod( InvocationContext ic) throws Exception {
String kto = beanContext.getCallerPrincipal().getName();
int ile = (Integer) ic.getParameters()[0];
ic.proceed();
// jeśli jesteśmy tutaj to wywołanie zostało zakończone poprawnie
logger.info("Wyplacona kwota: " + ile + " przez " + kto);

// odnotuj wypłatę w bazie danych
em.persist(new Wyplata(kto, ile));
}
}

Jego przypisanie do ziarna bankomat następuje za pomocą następującego deskryptora wdrożenia ejb-jar.xml.

<?xml version="1.0" encoding="UTF-8"?>
<ejb-jar xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/ejb-jar_3_0.xsd"
version="3.0">
<interceptors>
<interceptor>
<interceptor-class>pl.jaceklaskowski.ejb3.interceptors.BankomatInterceptor</interceptor-class>
<env-entry>
<env-entry-name>dozwolonaKwota</env-entry-name>
<env-entry-type>java.lang.Integer</env-entry-type>
<env-entry-value>20</env-entry-value>
</env-entry>
</interceptor>
<interceptor>
<interceptor-class>pl.jaceklaskowski.ejb3.interceptors.OdnotujWyplateInterceptor</interceptor-class>
</interceptor>
</interceptors>
<assembly-descriptor>
<interceptor-binding>
<ejb-name>bankomat</ejb-name>
<interceptor-class>pl.jaceklaskowski.ejb3.interceptors.OdnotujWyplateInterceptor</interceptor-class>
</interceptor-binding>
</assembly-descriptor>
</ejb-jar>

Deklarujemy w nim istnienie interceptora (sekcja interceptors) oraz jego wiązanie z ziarnami (sekcja interceptor-binding). Ważne jest określenie nazwy ziarna (element ejb-name) jak zdefiniowano w adnotacji @Stateless.

Do pełnego obrazu pozostaje zademonstrować encję Wyplata (klasa pl.jaceklaskowski.ejb3.interceptors.Wyplata):

package pl.jaceklaskowski.ejb3.interceptors;

import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.Id;

@Entity
public class Wyplata implements Serializable {

@Id
long id;

String kto;

int ile;

protected Wyplata() {
}

public Wyplata(String kto, int ile) {
}

public long getId() {
return id;
}

protected void setId(long id) {
this.id = id;
}

public String getKto() {
return kto;
}

public void setKto(String kto) {
this.kto = kto;
}

public int getIle() {
return ile;
}

public void setIle(int ile) {
this.ile = ile;
}
}

Uruchomienie przykładu na serwerze GlassFish 2.1 daje następujące wyniki (przy wypłacie 5):

Ziarno EJB utworzone
Interceptor utworzony
em == null ? true
dozwolonaKwota == -1 ? true
Wywolanie @PostConstruct dla BankomatBean
em == null ? false
dozwolonaKwota == -1 ? false
Przed wywolaniem metody biznesowej BankomatBean.wyplac()
Wyplata dozwolona - kwota wyplacana 5 mniejsza niz dozwolona 20
Wyplacam kwote 5
Wyplacona kwota: 5 przez ANONYMOUS
TopLink, version: Oracle TopLink Essentials - 2.1 (Build b17-fcs (01/17/2008))
Server: unknown
file:/C:/temp/EnterpriseApplication1/EnterpriseApplication1-ejb/build/jar/-__default login successful
Po wywolaniu metody biznesowej BankomatBean.wyplac()

Interceptor OdnotujWyplateInterceptor jest wykonany po interceptorze BankomatInterceptor (pytanie dla Ciebie: dlaczego?) z ANONYMOUS jako użytkownikiem wykonującym operację (o bezpieczeństwie w EJB3 będzie niebawem). Dostęp do kontekstu trwałego (@PersistenceContext) oraz kontekstu wykonania ziarna (@Resource dla egzemplarza SessionContext) jest możliwy, podobnie jak wykonanie podobnej metody w samym ziarnie EJB (środowisko wykonania ziarna jest współdzielone z interceptorem).

p.s. Już jest lepiej z głosowaniem i mamy szanse konkurować z innymi blogami w konkursie Blog Roku 2007. Zwyżkowałem na 14. miejsce z 9-ma głosami w III etapie (2 miejsca w górę). Ilość głosów wzrasta! Podziękowania dla tych, którzy zechcieli zagłosować. Niewiele kosztuje, a jaka ogromna radość Jacka ;-)