24 grudnia 2006

Wigilia i kolejny rozdział specyfikacji EJB3 - Chapter 2: Overview of the EJB 3.0 Simplified API

Święta to idealny czas na spędzenie czasu z rodziną i...dokształcanie się. Nie mogłem oprzeć się pokusie, aby nie zapoznać się z kolejnym rozdziałem specyfikacji EJB3 - Chapter 2: Overview of the EJB 3.0 Simplified API. Wyjątkowo krótki acz treściwy rozdział, który w niektórych miejscach, kiedy chciałem przygotować ilustrujące przykłady, zmusił mnie do lektury kolejnych. W zasadzie to jest on rozwinięciem rozdziału 1. i przybliża pewne terminy, które przewijają się przez całą specyfikację. Prezentuje on inne spojrzenie na uproszczenia nowej wersji specyfikacji EJB3.

Do tych już wspomnianych w rozdziale 1. (które opisałem wczoraj) dodać można:
  • Tworzenie modułów EJB nie wymaga tworzenia dodatkowych, specyficznych dla serwera aplikacyjnego, klas przed właściwą ich instalacją na serwerze. Innymi słowy: nie ma konieczności uruchamiania narzędzia dostarczanego przez serwer aplikacyjny generującego klasy pomocnicze dla modułów EJB. Klasy konieczne do uruchomienia modułów EJB tworzone są dynamicznie podczas uruchamiania przez serwer.

  • Nie ma konieczności rozszerzania klas należących do EJB API czy dostarczania klas wymaganych przez specyfikację EJB, np. rozszerzanie (nie wprost) javax.ejb.EnterpriseBean.

    Poniżej znajduje się przykład kompletnego bezstanowego komponentu sesyjnego implementującego biznesowy interfejs lokalny pl.jaceklaskowski.ejb.ExamScheduler.
    package pl.jaceklaskowski.ejb;

    import java.util.ArrayList;
    import java.util.List;
    import javax.ejb.Stateless;
    import javax.interceptor.Interceptors;
    import pl.jaceklaskowski.ejb.interceptor.ParameterLogger;

    @Stateless
    public class ExamSchedulerBean implements ExamScheduler {
    public List<Exam> getExams() {
    List<Exam> exams = new ArrayList<Exam>();
    exams.add(new Exam("Programowanie obiektowe"));
    return exams;
    }
    }
    gdzie pl.jaceklaskowski.ejb.ExamScheduler to:
    package pl.jaceklaskowski.ejb;

    import java.util.List;

    public interface ExamScheduler {
    List<Exam> getExams();
    }
    Instalacja komponentu ExamSchedulerBean to stworzenie pliku jar zawierającego obie klasy i umieszczenie go na serwerze aplikacyjnym. W poprzednich wersjach specyfikacji EJB 2.1 i wcześniejszych, między nimi występował jeszcze krok tworzenia klas specyficznych dla serwera aplikacyjnego. Krok był zależny od zastosowanego serwera, więc próba uruchomienia komponentu EJB wymagała wykonania go narzędziami dostarczanymi przez serwer (i wymagało zdecydowanie więcej wiedzy na temat środowiska poza znajomością samej specyfikacji EJB).

  • Konfiguracja komponentów EJB opiera się na ustandaryzowanych wartościach domyślnych, które można modyfikować poprzez udekorowanie kodu komponentów przez annotacje, bądź alternatywnie za pomocą pliku ejb-jar.xml. Wprowadzono nowy termin związany z konfiguracją komponentów EJB, tj. konfiguracja przez nadpisywanie (ang. configuration by exception), która określa podejście do konfiguracji EJB, gdzie podane są wyłącznie parametry konfiguracyjne nadpisujące wartości domyślne.

    Ważną kwestią jest wyznaczenie ostatecznej wartości parametru EJB. Mamy do dyspozycji (w kolejności ich rosnącej ważności):

    • możliwość pozostania przy konfiguracji domyślnej
    • nadpisania jej annotacjami
    • skorzystanie z deskryptora konfiguracji - ejb-jar.xml.

    Jest to przydatne w trakcie tworzenia aplikacji, gdzie można nadpisywać parametry za pomocą pliku ejb-jar.xml, a podczas produkcyjnej instalacji zaniechać jego dołączania i pozostania przy wartościach annotacji czy domyślnych.

  • Tworzenie komponentów EJB sprowadziło się do stworzenia metod ważnych dla programisty, a nie serwera aplikacyjnego, tj. nie ma konieczności dostarczania metod typu ejbCreate tylko i wyłącznie dla spełnienia wymagań serwera. Jeśli metody nie istnieją, kontener EJB dostarczy własne implementacje w trakcie działania.

  • Dowiązanie do zdalnego interfejsu biznesowego (ang. remote business interface) nie wymaga odszukania interfejsu domowego komponentu EJB, a następnie utworzenia instancji. W EJB3 uproszczono mechanizm dowiązania do pobrania interfejsu zdalnego bezpośrednio.

  • Dowiązania do innych komponentów i zasobów środowiska odbywa się poprzez annotacje (lub deskryptor instalacji). Wystarczy zadeklarować zależność za pomocą annotacji zmiennej prywatnej, która będzie reprezentować zależność, np. fabrykę instancji typu EntityManager (EntityManagerFactory), a kontener EJB poprzez mechanizm wstrzeliwania zależności (ang. dependency injection) zainicjuje zmienną do odpowiedniej wartości. Wyłącznie w pełni skonstruowana (zainicjowana) aplikacja z dostępnymi wszystkimi zależnościami będzie uruchomiona. W przypadku błędów inicjalizacji samych komponentów EJB, bądź powiązań między nimi, bądź ostatecznie części aplikacji deklarującej zależność z komponentami EJB serwer aplikacyjny nie uruchomi aplikacji.
    Załóżmy aplikację przemysłową (ang. enterprise application), która składa się z aplikacji internetowej (która z kolei składa się z servletu) oraz modułu EJB (który składa się z bezstanowego komponentu sesyjnego ExamSchedulerBean zaprezentowanego powyżej). Specyfikacja EJB3 uprościła sposób dostępu (dowiązania) servletu do komponentu EJB w następujący sposób:
    package pl.jaceklaskowski.servlet;

    import java.io.IOException;
    import java.util.List;
    import javax.ejb.EJB;
    import javax.servlet.RequestDispatcher;
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;

    import pl.jaceklaskowski.ejb.Exam;
    import pl.jaceklaskowski.ejb.ExamScheduler;

    public class ExecuteEjbServlet extends HttpServlet {

    @EJB
    private ExamScheduler examScheduler;

    protected void service(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
    List<Exam> exams = examScheduler.getExams();
    request.setAttribute("exams", exams);
    RequestDispatcher rd = getServletContext().getRequestDispatcher("/index.jsp");
    rd.forward(request, response);
    }
    }

    Na uwagę zasługuje udekorowanie prywatnej zmiennej instancji examScheduler annotacją @EJB, która za pomocą wykorzystanego interfejsu ExamScheduler wskazuje serwerowi aplikacyjnemu, który komponent EJB jest potrzebny.

  • Wprowadzenie interceptorów (ang. interceptors), które wprowadzają możliwości aspektów (ang. aspects) z Aspect-Oriented Programming (AOP) do świata komponentów EJB, gdzie można zadeklarować wykonanie metody interceptora wraz z wykonaniem metody komponentu EJB.

    Poniżej znajduje się klasa z metodą oznaczoną annotacją @AroundInvoke, za pomocą której wskazuje się metody biorące udział w mechaniźmie interceptorów.
    package pl.jaceklaskowski.ejb.interceptor;

    import java.lang.reflect.Method;
    import javax.interceptor.AroundInvoke;
    import javax.interceptor.InvocationContext;

    public class ParameterLogger {

    @AroundInvoke
    public Object printArgs(InvocationContext ic) throws Exception {
    Object target = ic.getTarget();
    Method m = ic.getMethod();
    Object[] params = ic.getParameters();
    System.out.printf("Parametry wywołania metody %s.%s %n", target.getClass().getName(), m.getName());
    if (params != null) {
    for (Object param: params) {
    System.out.printf("- %s %n", param);
    }
    } else {
    System.out.println("Brak parametrów wejściowych");
    }
    return ic.proceed();
    }
    }
    Wywołanie metody powyższej klasy w komponencie EJB wiąże się z udekorowaniem klasy bądź metody klasy annotacją @Interceptors z obowiązkowym parametrem wskazującym na klasę interceptora.
    package pl.jaceklaskowski.ejb;

    import java.util.ArrayList;
    import java.util.List;
    import javax.ejb.Stateless;
    import javax.interceptor.Interceptors;
    import pl.jaceklaskowski.ejb.interceptor.ParameterLogger;

    @Stateless
    @Interceptors(ParameterLogger.class)
    public class ExamSchedulerBean implements ExamScheduler {
    public List<Exam> getExams() {
    List<Exam> exams = new ArrayList<Exam>();
    exams.add(new Exam("Programowanie obiektowe"));
    return exams;
    }
    }
  • Udostępnienie komponentu EJB jako Usługi Sieciowej (ang. Web Service) wiąże się jedynie z udekorowaniem metod instancji klasy przez annotację @WebMethod.
    package pl.jaceklaskowski.ejb;

    import java.util.List;
    import javax.jws.WebMethod;

    public interface ExamScheduler {

    @WebMethod
    List<Exam> getExams();

    }
  • Łatwość migracji do nowej wersji specyfikacji EJB3.
    Podobnie jak to ma miejsce z innymi rozszerzeniami do Java SE czy Java EE zazwyczaj gwarantuje się ich wsteczną zgodność (dobrym przykładem jest mechanizmu Java Generics i jego wykorzystanie w Java Collections API). Aplikacja korzystająca z komponentów EJB 2.1 i wcześniejszych ma możliwość korzystania z rozwiązań EJB3 i na odwrót.