20 września 2007

Elementy programowania aspektowego w EJB 3.0 - część 1

Zaintrygowany komentarzami na temat (nie)doskonałości EJB 3.0 w dostarczaniu funkcjonalności znanej z programowania aspektowego (ang. AOP - Aspect-Oriented Programming) na ostatnim spotkaniu Warszawa JUG postanowiłem zbadać temat dokładniej, którego celem byłoby przedstawienie tematu na jednym ze spotkań grupy. Rozpocznę od stwierdzenia, że mechanizmy znane z AOP w wykonaniu EJB3 są jedynie namiastką pełnego wachlarza możliwości AOP. Mimo wszystko warto poznać EJB3 z tej perspektywy, aby w pełni docenić zmiany i możliwości nowej wersji specyfikacji EJB3 i nie uciekać się do rozwiązań alternatywnych, jeśli to, czego w danej chwili potrzebujemy jest dostępne pod ręką, w EJB3.

Za Wikipedią - Programowanie aspektowe:

Programowanie aspektowe (aspect-oriented programming, AOP) to paradygmat tworzenia programów komputerowych wspomagający separację zagadnień i rozdzielenie programu na części w jak największym stopniu niezwiązane funkcjonalnie.

Jak można było usłyszeć na ostatniej prezentacji Bartka Szkudlarka podczas XV spotkania Warszawa JUG jest wiele pojęć związanych z AOP i próba ich wytłumaczenia tu i teraz i w dodatku przeze mnie mogłaby nie oddać pełnego kolorytu AOP (niech tracą Ci, którym nie udało się przybyć na spotkanie).

Programowanie aspektowe opiera się na wyodrębnieniu zagadnień, które można określić pobocznymi w stosunku do podstawowych, biznesowych wymagań aplikacji. Takimi zagadnieniami są m.in. mechanizm bezpieczeństwa, śledzenie wykonywania programu, badanie wydajności, itp. Są to zagadnienia istotne dla pełnego działania aplikacji, jednakże ich znaczenie biznesowe jest niewielkie w stosunku do problemu, dla którego powstaje nasza aplikacja.

EJB3 dostarcza rozwiązanie o nazwie interceptor (alternatywnie metoda przechwytująca), który jest uruchamiany podczas wykonania metody biznesowej ziarna EJB - interceptor metod biznesowych (dalej zwany interceptorem biznesowym) - lub zdarzenia związanego z cyklem rozwojowym ziarna - interceptor zdarzenia rozwojowego (dalej zwany interceptorem rozwojowym).

Interceptor może być zdefiniowany w dedykowanej klasie interceptora bądź może być niebiznesową metodą ziarna EJB (dla przypomnienia: metody biznesowe są zdefiniowane przez interfejs lokalny, zdalny, endpoint oraz w przypadku ziarna sterowanego komunikatami metodzie onMessage bądź adekwatnej dla stosowanego systemu komunikatów).

I tu pojawia się kwestia związana z typem ziarna, dla których można zadeklarować interceptor biznesowy. Interceptory biznesowe są jedynie deklarowane dla ziaren sesyjnych oraz sterowanych komunikatami (kolejne miejsce w specyfikacji EJB3, gdzie można zauważyć inne traktowanie ziaren encyjnych, które w następnej specyfikacji Java EE 6 mają być ustandaryzowane przez dedykowaną specyfikację i wyjść z objęć EJB3).

Klasa interceptora musi definiować bezargumentowy konstruktor.

Metoda interceptora biznesowego posiada następującą składnię:

Object <dowolna-nazwa-metody>(InvocationContext) throws Exception
Wykonanie interceptora wiąże się z przekazaniem egzemplarza javax.interceptor.InvocationContext, który dostarcza informacji o środowisku uruchomieniowym metody interceptora, tj. metodzie, parametrów wejściowych i nazwie klasy ziarna, z którym jest związany. Najczęściej wykorzystywaną metodą InvocationContext jest proceed(), której uruchomienie spowoduje wykonanie kolejnego interceptora w łańcuchu interceptorów związanych z daną metodą biznesową lub samą metodę biznesową.

Skoro wspominałem o łańcuchu interceptorów sugeruje to możliwość deklaracji wielu interceptorów dla pojedyńczej metody biznesowej oraz prowokuje do zadania pytania o ich kolejność wykonania. Istnieje możliwość deklaracji wielu interceptorów biznesowych dla pojedyńczej metody czy klasy. Ich kolejność zostanie omówiona w kolejnej odsłonie na ich temat.

I w końcu informacja, której brakuje do popróbowania się z interceptorami biznesowymi - sposób ich deklaracji oraz miejsce ich uruchomienia. EJB3 dostarcza adnotację @AroundInvoke oraz znacznik <around-invoke>, które definiują metodę interceptora, natomiast adnotacja @Interceptors związuje metody biznesowe z klasami interceptorów. Miejsce wystąpienia adnotacji @Interceptors lub wartości znacznika <around-invoke> określają metody biznesowe związane z interceptorami, których wywołanie spowoduje wywołanie interceptora.

Dla zobrazowania materiału przedstawię przykład interceptora TracingInterceptor, którego zadaniem będzie odnotowywanie wykonania metod biznesowych.

Klasa interceptora TracingInterceptor

package pl.jaceklaskowski.ejbws.interceptors;

import java.util.logging.Logger;
import javax.interceptor.AroundInvoke;
import javax.interceptor.InvocationContext;

public class TracingInterceptor {

Logger logger = Logger.getLogger(TracingInterceptor.class.toString());

@AroundInvoke
public Object trace(InvocationContext ctx) throws Exception {
logger.info("Wykonuje metode biznesowa " +
ctx.getTarget().getClass().getSimpleName() +
"." + ctx.getMethod().getName());
return ctx.proceed();
}
}

oraz ziarno MojPierwszyWsEJBBean z poprzedniego wpisu, z zadeklarowanym interceptorem:

package pl.jaceklaskowski.ejbws;

import java.util.logging.Logger;
import javax.ejb.Stateless;
import javax.interceptor.Interceptors;
import pl.jaceklaskowski.ejbws.interceptors.TracingInterceptor;

@Stateless
@Interceptors(TracingInterceptor.class)
public class MojPierwszyWsEJBBean implements MojPierwszyWsEJBLocal {

Logger logger = Logger.getLogger(MojPierwszyWsEJBBean.class.toString());

public void metodaBiznesowa() {
logger.info("Metoda biznesowa metodaBiznesowa wywolana poprawnie");
}
}

Uruchomienie metody metodaBiznesowa zakończy się następującym wpisem w dzienniku zdarzeń serwera aplikacyjnego Korporacyjnej Javy 5 (Java EE 5):

Wykonuje metode biznesowa MojPierwszyWsEJBBean.metodaBiznesowa
Metoda biznesowa metodaBiznesowa wywolana poprawnie