31 stycznia 2008

EJB 3.0 - Rozdział 12.3.1 Kolejność wykonywania interceptorów biznesowych

Kolejny podrozdział specyfikacji EJB3 - 12.3.1 Kolejność wykonywania interceptorów biznesowych.

Kolejność wykonania interceptorów biznesowych wyznaczana jest przez adnotacje oraz opcjonalny deskryptor wdrożenia (ejb-jar.xml). Kolejność wykonania interceptorów może zostać zmieniona przez deskryptor wdrożenia.
  1. Najpierw uruchamianie są domyślne interceptory, które mogą być zdefiniowane wyłącznie w deskryptorze rozmieszczenia. Kolejność ich definicji w deskryptorze określa kolejność ich uruchomienia.

  2. Jeśli zdefiniowano klasy interceptorów dla klasy ziarna, ich metody uruchamiane są zanim uruchomione zostaną jakiekolwiek metody przechwytujące definiowane przez samo ziarno.

  3. Metody AroundInvoke zdefiniowane na tych klasach interceptorów są wykonywane w tej samej kolejności jak kolejność odpowiadających im klas interceptorów w adnotacji @Interceptors.

  4. Jeśli klasa interceptora posiada klasy nadrzędne, metody przechwytujące zdefiniowane przez nie są wykonywane przed metodami przechwytującymi zdefiniowanymi przez tą klasę, w kolejności zgodnej z hierarchią dziedziczenia, idąc od java.lang.Object będącym na szczycie hierarchi.

  5. Po wykonaniu metod przechwytujących zdefiniowanych przez klasy interceptorów, następuje wykonanie metod przechwytujących w następującej kolejności:

    1. Jeśli zdefiniowano interceptory na poziomie metody biznesowej, która jest wywoływana, metody AroundInvoke zdefiniowane na tych klasach interceptorów są wykonywane w tej samej kolejności jak ich kolejność w adnotacji @Interceptors przy tej metodzie.

    2. Jeśli klasa ziarna ma klasy nadrzędne, wszystkie metody AroundInvoke są wykonywane najpierw, w kolejności zgodnej z hierarchią dziedziczenia, idąc od java.lang.Object będącym na szczycie hierarchi.

    3. Jeśli istnieje metoda AroundInvoke zdefiniowana w klasie ziarna, jest ona wywołana.

Jeśli metoda AroundInvoke jest przesłonięta przez inną metodę (bez względu na to czy sama jest metodą AroundInvoke), metoda przesłonięta nie będzie wywołana.

Egzemplarz javax.ejb.InvocationContext udostępnia mechanizm kontroli wykonania ciągu interceptorów, wraz z możliwością zatrzymania wykonania kolejnej metody (biznesowej czy interceptora) w łańcuchu kolejnych wywołań oraz ich wartości parametrów i wynik wywołania.

Dla zobrazowania tematu rozszerzyłem przykład z poprzednich relacji z rozdziałów o interceptorach.

Celem nadrzędnym było możliwe minimalne użycie adnotacji związanych z interceptorami w klasach ziarna i interceptorów (z już istniejących zostały zdjęte, a do nowych nie były w ogóle dodawane). Deklaracja adnotacji została wykonana za pomocą deskryptra 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>
<around-invoke>
<method-name>przechwycWywolanieMetodyBiznesowej</method-name>
</around-invoke>
</interceptor>
<interceptor>
<interceptor-class>pl.jaceklaskowski.ejb3.interceptors.InterceptorDomyslny</interceptor-class>
</interceptor>
<interceptor>
<interceptor-class>pl.jaceklaskowski.ejb3.interceptors.InterceptorKlasy</interceptor-class>
</interceptor>
<interceptor>
<interceptor-class>pl.jaceklaskowski.ejb3.interceptors.InterceptorKlasyZWlasnymiInterceptorami</interceptor-class>
<around-invoke>
<method-name>innePrzechwycWywolanieMetodyBiznesowej</method-name>
</around-invoke>
</interceptor>
<interceptor>
<interceptor-class>pl.jaceklaskowski.ejb3.interceptors.InterceptorMetody</interceptor-class>
</interceptor>
</interceptors>
<assembly-descriptor>
<interceptor-binding>
<ejb-name>*</ejb-name>
<interceptor-class>pl.jaceklaskowski.ejb3.interceptors.InterceptorDomyslny</interceptor-class>
</interceptor-binding>
<interceptor-binding>
<ejb-name>bankomat</ejb-name>
<interceptor-class>pl.jaceklaskowski.ejb3.interceptors.InterceptorKlasy</interceptor-class>
</interceptor-binding>
<interceptor-binding>
<ejb-name>bankomat</ejb-name>
<interceptor-class>pl.jaceklaskowski.ejb3.interceptors.InterceptorKlasyZWlasnymiInterceptorami</interceptor-class>
</interceptor-binding>
<interceptor-binding>
<ejb-name>bankomat</ejb-name>
<interceptor-class>pl.jaceklaskowski.ejb3.interceptors.InterceptorMetody</interceptor-class>
<method>
<method-name>wyplac</method-name>
<method-params>
<method-param>int</method-param>
</method-params>
</method>
</interceptor-binding>
</assembly-descriptor>
</ejb-jar>
Zmieniłem nazwę metody interceptora dla jej uatrakcyjnienia. Teraz nazywa się ona przechwycWywolanieMetodyBiznesowej(InvocationContext). Przy okazji zmiany zauważyłem, że poprzednio metoda interceptora nie zwracała obowiązkowego parametru typu Object będącego wynikiem wywołania metody biznesowej (bądź ostatecznie ostatniego interceptora w łańcuchu wykonań, jeśli dany interceptor zdecydował o nie wykonaniu metody biznesowej). W ten sposób utworzyłem klasę bazową dla wszystkich nowych interceptorów - pl.jaceklaskowski.ejb3.interceptors.KlasaBazowaInterceptorow.

package pl.jaceklaskowski.ejb3.interceptors;

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

public abstract class KlasaBazowaInterceptorow {

Logger logger = Logger.getLogger("interceptor");

@AroundInvoke
Object przechwycWywolanieMetodyBiznesowej(InvocationContext ic) throws Exception {
logger.info("Wywolano " + this.getClass().getSimpleName());
return ic.proceed();
}

}
Metoda przechwycWywolanieMetodyBiznesowej jest udekorowana adnotacją @AroundInvoke, aby zademonstrować jej wykonanie przed wykonaniem metody interceptora z podklasy - pl.jaceklaskowski.ejb3.interceptors.InterceptorKlasyZWlasnymiInterceptorami.

package pl.jaceklaskowski.ejb3.interceptors;

import javax.interceptor.InvocationContext;

public class InterceptorKlasyZWlasnymiInterceptorami extends KlasaBazowaInterceptorow {
Object innePrzechwycWywolanieMetodyBiznesowej(InvocationContext ic) throws Exception {
logger.info("Wywolano (inne) " + this.getClass().getSimpleName());
return ic.proceed();
}
}
Dalej mamy samo ziarno bez adnotacji @Interceptors.

package pl.jaceklaskowski.ejb3.interceptors;

import java.util.logging.Logger;
import javax.ejb.Stateless;
import javax.jws.WebService;

@Stateless(name="bankomat")
@WebService(serviceName="bankomat")
public class BankomatBean implements Bankomat {

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

public BankomatBean() {
logger.info("Ziarno EJB utworzone");
}

public void wyplac(int kwota) {
logger.info("Wyplacam kwote " + kwota);
}

}
oraz pozostałe interceptory - InterceptorDomyślny, InterceptorKlasy oraz InterceptorMetody.

package pl.jaceklaskowski.ejb3.interceptors;

public class InterceptorDomyslny extends KlasaBazowaInterceptorow {
}

package pl.jaceklaskowski.ejb3.interceptors;

public class InterceptorKlasy extends KlasaBazowaInterceptorow {
}

package pl.jaceklaskowski.ejb3.interceptors;

public class InterceptorMetody extends KlasaBazowaInterceptorow {
}
Wykonanie przykładu demonstrowane jest w konsoli GlassFish następująco:

Ziarno EJB utworzone
Wywolano _InterceptorDomyslny_Serializable
Wywolano _InterceptorKlasy_Serializable
Wywolano _InterceptorKlasyZWlasnymiInterceptorami_Serializable
Wywolano (inne) _InterceptorKlasyZWlasnymiInterceptorami_Serializable
Wywolano _InterceptorMetody_Serializable
Wyplacam kwote 5
Pytanie: Dlaczego nie został wykonany interceptor OdnotujWyplateInterceptor?

p.s. Ja ponownie o konkursie Blog Roku 2007. Trwa III tura konkursu i nawet jeśli głosowałeś/głosowałaś w poprzedniej turze, teraz możesz ponownie wysłać SMSa na swój ulubiony blog (wierzę, że będzie to ten właściwy). Jestem na 13. miejscu z 12-oma głosami. Gratulacje dla tych 3 odważnych, którzy zechcieli zagłosować od wczoraj. Ty możesz być kolejny!