26 grudnia 2006

Kolejny rozdział specyfikacji EJB3 za mną - Chapter 3: Enterprise Bean Class and Business Interface

Przyznaję, że czym więcej testuję nowości specyfikacji EJB3 tym bardziej zastanawia mnie stopień skomplikowania jej w poprzednich wersjach. Współczuję tym, którzy musieli spędzać dłuuugie godziny nad rozwiązywaniem problemów związanych z niezrozumiałymi konstrukcjami EJB 2.1. Nie sądziłem, że kiedykolwiek doczekam się dnia, kiedy specyfikacja EJB stanie się tak przyjemna. Aż nie chce się wierzyć, że są tacy, którzy chcą, abyśmy....ekhm...chyba się zapomniałem, bo to chyba był tekst pewnego utworu z dawien dawna...więc wracając do tematu, są tacy, którzy twierdzą, że wiele uproszczeń nie weszło do Java EE 5, a które mogły jeszcze bardziej uatrakcyjnić specyfikację. Mimo wszystko dla mnie uproszczeń jest wystarczająca ilość. Poznanie ich z pewnością podniesie mój warsztat programistyczny, nawet jeśli nie przyjdzie mi tych rozwiązań wykorzystywać w projektach komercyjnych jeszcze długo (dostępność produkcyjna serwerów aplikacyjnych Java EE 5 jest jeszcze skromna).

Co wniosła do mojej wiedzy lektura rozdziału 3. Enterprise Bean Class and Business Interface specyfikacji EJB 3.0 Simplified API?

Rozdział 3 omawia te elementy specyfikacji EJB3, które są wspólne dla komponentów sesyjnych (ang. SB - session beans) i sterowanych komunikatami (ang. MDB - message-driven beans). Pojawia się również notka, która stwierdza, bardzo istotną z punktu widzenia osób tworzących rozwiązania oparte o poprzednie wersje specyfikacji EJB 2.1 i poprzednich, że komponenty encyjne nie są już w zasadzie komponentami EJB jak to miało miejsce poprzednio i stąd też osobny dokument na ich temat - Java Persistence API oraz brak omówienia w tym rozdziale.

Część informacji była już omówiona bardzo pobieżnie w poprzednich rozdziałach, stąd niektóre informacje wydają się być znajome (nawet dla nie-praktyków EJB3, którzy jednak śledzili poprzednie moje relacje).

Tworzenie modułu EJB rozpoczyna się od stworzenia interfejsu biznesowego, np.:

package pl.jaceklaskowski.exam.scheduler;

import java.util.List;

import pl.jaceklaskowski.exam.beans.Exam;

public interface ExamScheduler {
List<Exam> getExams();
}

, a następnie realizacji jego przez klasę komponentu EJB (ang. enterprise bean class),np.

package pl.jaceklaskowski.exam.scheduler;

import java.util.ArrayList;
import java.util.List;

import javax.ejb.Stateless;

import pl.jaceklaskowski.exam.beans.Exam;

@Stateless
public class ExamSchedulerBean implements ExamScheduler {

private final List<Exam> exams = new ArrayList<Exam>();

public List<Exam> getExams() {
return exams;
}

}

W przeciwieństwie do poprzednich wersji specyfikacji EJB, rolę aktywującą funkcjonalność modułu EJB stanowią przede wszystkim annotacje jako dodatkowy mechanizm konfiguracji poza osobno utrzymywanym, zewnętrznym deskryptorem instalacji - ejb-jar.xml. Annotacje, jako mechanizm dostarczany przez Java SE, służy do opisu modułu, jego wymagań i zależności od środowiska uruchomieniowego bezpośrednio w klasie komponentu EJB. Jedyną wymaganą annotacją klasy komponentu EJB jest deklaracja typu (jeśli takowa informacja nie została zawarta w deskryptorze instalacji).

Wyróżniamy następujące annotacje typów komponentu:
  • @Stateless - bezstanowy komponent sesyjny
  • @Stateful - stanowy komponent sesyjny
  • @MessageDriven - komponent sterowny komunikatami

Specyfikacja EJB3 określa, że interfejs biznesowy komponentu EJB to dowolny interfejs w Javie poza:
  • java.io.Serializable
  • java.io.Externalizable
  • dowolnym interfejsem z pakietu javax.ejb

które nie biorą udziału w wyznaczaniu interfejsów lokalnych i/lub zdalnych.

Nie ma konieczności implementacji interfejsów javax.ejb.EJBObject czy javax.ejb.EJBLocalObject, jak to miało miejsce w poprzedniej wersji specyfikacji (choć taka możliwość istnieje ze względu na wsteczną zgodność). I komponenty sesyjne i sterowane komunikatami wymagają co najmniej jednego interfejsu biznesowego (gdzie interfejs komponentu sterowanego komunikatami to zazwyczaj wskazanie na rodzaj obsługiwanej usługi, np. javax.jms.MessageListener dla JMS).

Ciekawym stwierdzeniem jest, że klasa główna komponentu nie musi implementować własnego interfejsu biznesowego. Jak dalej napisano, można tak udekorować klasę (bądź skorzystać z konfiguracji poprzez deskryptor instalacji), że interfejs biznesowy będzie realizowany przez inną klasę i będzie on nadal jej interfejsem biznesowym. Trochę to pogmatwane i nie jestem w stanie sobie tego wyobrazić, więc spodziewam się wyjaśnienia w kolejnych rozdziałach.

Skoro klasa komponentu EJB musi posiadać co najmniej jeden interfejs biznesowy (lokalny lub zdalny), to:
  • pojedyńczy interfejs realizowany przez nią zakłada się, że jest interfejsem biznesowym lokalnym chyba, że zadeklarowano inaczej poprzez annotację @Remote na klasie lub interfejsie (lub alternatywnie przez deskryptor instalacji - ejb-jar.xml). Domyślnie zakłada się, że występuje annotacja @Local.
  • Posiadanie więcej niż jednego interfejsu lokalnego czy zdalnego wymaga ich oznaczenia za pomocą annotacji @Local lub @Remote na klasie komponentu lub interfejsie bądź poprzez deskryptor instalacji.
  • Zabronione jest oznaczanie interfejsu biznesowego jednocześnie jako lokalnego i zdalnego, w tym również sytuacja niespójnej deklaracji @Local czy @Remote na klasie komponentu i interfejsie, tj. jeśli annotacja widoczności występuje na interfejsie biznesowym, to jeśli występuje ona również na klasie komponentu to ich wartości muszą być identyczne.
  • Zabronione jest deklarowanie interfejsu biznesowego, który rozszerza javax.ejb.EJBObject lub javax.ejb.EJBLocalObject.

Poniższy przykład ilustruje kilka z wymienionych wyżej wymagań.

package pl.jaceklaskowski.exam.manager;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import javax.ejb.Local;
import javax.ejb.Remote;
import javax.ejb.Stateless;

import pl.jaceklaskowski.exam.beans.Exam;
import pl.jaceklaskowski.exam.beans.Note;

@Stateless
@Local(ExamScheduler.class)
@Remote(ExamVerifier.class)
public class ExamControllerBean implements ExamScheduler, ExamVerifier {

private final List<Exam> exams = new ArrayList<Exam>();

public List<Exam> getExams() {
return exams;
}

public Note verify(Exam exam, Set<String> answers) {
return exam.verify(answers);
}
}

Kolejne uproszczenie specyfikacji EJB3 dotyczy wyjątków. Metody interfejsu biznesowego mogą deklarować (poprzez słowo kluczowe throws) dowolne wyjątki aplikacyjne za wyjątkiem java.rmi.RemoteException, który jest opakowywany przez javax.ejb.EJBException przez serwer aplikacyjny, w przypadku problemów niskopoziomowych.

Specyfikacja przechodzi do omówienia interceptorów. Temat zajmuje aż 3 strony specyfikacji, co w porównaniu z poprzednimi tematami w tym rozdziale stanowi solidną dawkę informacji do przyswojenia. Postanowiłem pozostawić sobie przyjemność opisania go w kolejnej relacji po próbie zrozumienia tematu na przykładach.

Rozdział kończy się bardzo krótką wzmianką dotyczącą interfejsów domowych, które...znikają na dobre. Grały one kluczową rolę w poprzedniej wersji specyfikacji EJB 2.1 i poprzednich podczas, gdy teraz mamy do dyspozycji mechanizm wstrzeliwania zależności (realizowanego poprzez annotacje) w przypadku komponentów sesyjnych, a komponenty encyjne tworzy się za pomocą mechanizmu powoływania instancji klasy w Javie, tj. słowa kluczowego new.

Dla przypomnienia, komponenty sterowane komunikatami (MDB), ze względu na swoją specyfikę działania, nie miały interfejsu domowego już w poprzednich wersjach specyfikacji.