EJB

Autor: Marcin Kasiński
21.01.2011 13:24:00 +0200

Komponenty EJB (Enterprise Java Beans) są programowalnymi komponentami umieszczanymi na serwerze aplikacji i zarządzanymi przez kontener EJB. Komponenty te odpowiadają za logikę aplikacji udostępniając zasadnicze jej funkcjonalności dla innych warstw aplikacji lub zupełnie na zewnątrz zdalnym klientom. W uproszczeniu są to klasy JAVA, w których określiliśmy, że pewne kluczowe ich metody mogą być wywoływane zdalnie. Ważną cechą ze względu na bezpieczeństwo jest tu to, że możliwość wywołania konkretnej metody nie oznacza, że strona wywołująca zna jej implementacje. Dla strony wywołującej znana jest tylko nazwa metody, argumenty metody i wartość zwracana. Cała implementacja znajduje sie na serwerze i jest dla strony wywołującej nieznana. Komponenty EJB można uznać za pewnego rodzaju usługi rezydujące na serwerze. Z punktu widzenia programisty komponenty te upraszczają proces tworzenia aplikacji. Dla przykładu w przypadku aplikacji bankowej takim ewentualnym komponentem EJB może być komponent zwracający informacje o kliencie. W takim przypadku możemy taki komponent napisać raz i umieścić na serwerze aplikacji, a następnie jego funkcjonalność wykorzystywać w innych aplikacjach danego banku. Dodatkowo, przy takim rozwiązaniu, mając dostęp już do istniejącego EJB, programista nie musi się martwić o logikę aplikacji i dostęp do informacji zwracanych przez konkretny komponent EJB (to wykonywane jest w kontenerze EJB), tylko o zaprezentowanie pobranych danych klientowi.

W przypadku aplikacji wykorzystującej tą technologię istnieje łatwość ich skalowania. Kiedy zwiększy się ilość użytkowników korzystających z aplikacji i wystąpią problemy z wydajnością istnieje możliwość zainstalowania komponentów EJB na dodatkowych maszynach co spowoduje rozłożenia obciążenia. Dodatkowo ze względu na przeniesienie implementacji na serwer aplikacje klienckie są "odchudzone" i mogą być wywoływane z urządzeń o ograniczonych zasobach sprzętowych. Kontener EJB dostarcza mechanizmów transakcyjnych. Oznacza to, że w przypadku aplikacji, które wymuszają integracje danych mamy możliwość określenia poziomu izolacji danej metody EJB. Oznacza to, że w ramach jednego komponentu EJB możemy nadawać para użytkownikom do konkretnych jego metod, a nie tylko do samego komponentu. Mając taki przykładowy bankowy komponent EJB możemy jego metodę odpowiadającą za otwarcie kredytu udostępnić zwykłemu pracownikowi, natomiast zabronić mu możliwości wykonania metody odpowiadającej za przyznawanie kredytu tego samego komponentu. Ważne jest tu też to, że jeśli mamy konkretny komponent EJB, nie oznacza to, że mamy zdalnie dostęp do wszystkich jego metod. To, które metody udostępniamy jako zdalne, a które nie określamy na etapie projektowania, a następnie implementacji aplikacji. Komponent do wykonania swoich zdalnych metod może potrzebować wywołać inną pomocniczą metodę tego komponentu, która jest potrzebna do poprawnego jego działania, ale wcale nie musi być udostępniana na zewnątrz.

Deskryptor EJB

Deskryptor EJB, to specjalny plik XML, zawierający informacje o komponentach EJB. W pliku tym zapisywane są odwzorowania pół komponentu EJB na kolumny bazodanowe, relacje pomiędzy nimi. Zawiera on informacje o bazie danych będącej repozytorium dla komponentów. Dodatkowo zawiera on zapytania w języku EJB QL wyszukujące komponenty EJB wg zadanego kryterium. Poniżej znajduje się lista podstawowych typów komponentów EJB:

EJB Session Bean

Komponent ten odpowiada za dostarczanie odpowiednich usług do zdalnego wywołania. Udostępnia on pewne swoje metody dla klienta, a ten z kolei zdalnie wywołuje daną metodę, a zadaniem komponentu session jest wykonanie zadania na potrzeby klienta. Istnieją dwa typy takich komponentów bezstanowy (częściej wykorzystywany) i stanowy.

Stateful Session Beans

W przypadku komponentu stanowego z każdym klientem wywołującym dany komponent związany jest jego stan, który może być wykorzystany do zapisywania w nim danych sesyjnych związanych z konkretnym klientem. Występuje tu analogia do sesji HTTP, gdzie również jest ona związana z konkretnym klientem i przechowuje jego stan. Dużą wadą tych komponentów jest ich wydajność, w związku z tym, jeśli nie ma ku temu poważnych przesłanek w wielu dokumentach zaleca się aby z nich nie korzystać. Powodem problemów z wydajnością jest to, że dla każdego klienta takiego komponentu musi być stworzony odpowiedni obiekt sesyjny przechowujący dane z nim związane. W przypadku małych aplikacji może to nie stanowić większego problemu, natomiast w przypadku dużych aplikacji, gdzie mamy dużą ilość klientów może to stanowić poważny problem wydajnościowy.

Stateless Session Beans

W odróżnieniu od komponentu stanowego, komponent bezstanowy nie przechowuje żadnych informacji stanowych klienta. Kiedy klient wywołuje daną metodę EJB wszystkie zmienne instancyjne mogą być oczywiście wykorzystywane i przechowywać dane, jednak tylko i wyłącznie w trakcie wywoływania danej metody. Po zakończeniu wywołania metody dane te nie są już przechowywane i nie mogą być wykorzystane przy kolejnych wywołaniach. W porównaniu do komponentów stanowych mamy lepszą możliwość skalowania systemu i zdecydowanie lepszą wydajność. Dzieje sie tak właśnie z powodu konieczności przechowywania informacji sesyjnych przez komponenty stanowe. Z tego powodu do obsłużenia takiej samej ilości klientów wystarcza mniejsza ilość komponentów bezstanowych niż stanowych. Dodatkowo, ze względu na to, że komponenty stanowe muszą być zapisywane w pamięciach masowych, a bezstanowe zawsze pozostają tylko i wyłącznie w pamięci mamy lepszą wydajność.

EJB Entity Bean

Komponent Entity Bean pełni trochę inną rolę niż Session Bean. Komponent Entity Bean w odróżnieniu od komponentu session Bean nie udostępnia usług dostępnych zdalnie lecz reprezentuje obiekt biznesowy przechowywany w pamięciach masowych, gdzie najczęściej jest to baza danych. Za obiekt biznesowy możemy uznać zamówienie, produkt, klient, fakturę. Taki komponent reprezentowany jest przez klasę, której pola mapują sie na odpowiednie kolumny tabeli bazodanowej. Na takim komponencie po wyciągnięciu go z serwera można wykonywać dowolne operacje poprzez wywołanie odpowiednich metod zmieniających pola takiej klasy, co z kolei przekłada sie na konkretne operacje bazodanowe typu UPDATE. Poza operacjami modyfikującymi można takie obiekty tworzyć co z kolei spowoduje utworzenie nowego rekordu w tabeli. komponenty Entity za pomocą specjalnych mechanizmów mogą być łatwo wyszukiwane, a jako rezultat programista może otrzymać komponent reprezentujący jeden rekord bazodanowy lub kolekcje takich obiektów. W przypadku zwracania jednego obiektu używany jest identyfikator komponentu (Primaty Key) i jest on wykorzystywany analogicznie jak to ma miejsce w przypadku kluczy podstawowych bazy danych. Można uznać, że identyfikator EJB jest obiektową reprezentacją klucza podstawowego bazy danych. Dostęp do tych komponentów jest dzielony, więc mogą być wykorzystywane jednocześnie przez wielu klientów co poprawia wydajność aplikacji. Oczywiście kontener EJB zapewnia nam transakcyjność operacji zmieniających komponent i jeśli zaznaczymy poziom izolacji w deskryptorze EJB, to taka transakcyjność będzie zapewniona. Dodatkowo tak samo jak ma to miejsce w relacyjnych bazach danych komponent Entity Bean może być w relacji z innym komponentem, pozycje na fakturze mogą być w relacji z fakturą, produkt z kategorią, itp. Taką relację można traktować jak bazodanowy klucz obcy. Rozróżniamy cztery podstawowe typy relacji. Nie różnią sie one od standardowych definicji relacji bazodanowy.

  • one-to-oneJest to najprostsza forma relacji oznaczająca, że dany komponent jest w relacji tylko i wyłącznie z jedną instancją drugiego komponentu. Dla przykładu możemy tu podać obywatela i jego numer PESEL. Jest to typowa relacja one-to-one.
  • one-to-manyJest to relacja oznaczająca, że dany komponent może być w relacji z wieloma instancjami drugiego komponentu. Przykładem takiej relacji jest faktura i pozycje na niej, gdzie na fakturze może być wiele pozycji.
  • many-to-oneJest to relacja będąca odwrotnością poprzedniej relacji i oznacza, że wiele instancji komponentu jest w relacji z jedną instancją drugiego komponentu. Dla przykładu pozycje na fakturze są w relacji tylko do jednej faktury.
  • many-to-manyJest to relacja oznaczająca, że wiele instancji komponentu może być w relacji z wieloma instancjami drugiego komponentu. Dla przykładu studenci uczestniczą na wiele zajęć i na zajęcia przychodzi wielu studentów.

Rodzaje komponentów Entity Bean

Ze względu na zarządzanie komponentami rozróżniamy dwa rodzaje komponentów Entity, CMP (Container-Managed Persistence) oraz BMP (Bean-Managed Persistence). Różnice pomiędzy nimi dotyczą zarządzania ich zapisem do pamięci masowych oraz co za tym idzie ich transakcyjnością.

W przypadku komponentów CMP zarządcą takim jest kontener EJB, na działanie którego programista nie ma wpływu. W takim przypadku to kontener zajmuje sie cache-owaniem danych, zapisem do pamięci masowych oraz transakcyjnością. Programista w takiej sytuacji nie ma możliwości zoptymalizowania operacji bazodanowych, wszystko to się dzieje poza jego kontrolą. Z jednej strony może to być wada lecz z drugiej równie dobrze może to być zaleta. Może to być wada, ponieważ programista jest uzależniony od tego, co zostało już stworzone i nie ma możliwości ingerencji , a przecież wiadomym jest, że właśnie operacje zapisu do pamięci masowych są najbardziej obciążające. Z drugiej strony może to się okazać dużą zaletą. Przy takim rozwiązaniu programista może skupić się tylko na najistotniejszych aspektach aplikacji i pisaniu jej logiki, natomiast samą implementacje niskopoziomowych operacji powalających na przechowywanie komponentów w bazie danych pozostawić innym. Dodatkowo dużą zaletą takiego rozwiązania jest to, że kontenery EJB są standardowym modułem każdego serwera aplikacji, a to oznacza, że nie jest wymagane przy zmianach w aplikacji testowanie właśnie tych operacji, które są dostarczane z pudełka. Drugą bardzo ważna zaletą takiego rozwiązania jest łatwość przeniesienia architektury na inny silnik bazodanowy. Standardowo kontenery EJB są przystosowane do obsługi określonego rodzaju silników bazodanowych. W takim przypadku mamy pewność, że po zmianie silnika aplikacja dalej będzie pracowała poprawnie.

Z kolej komponenty BMP są zarządzane w zupełności przez programistę. To programista poza sama logiką musi oprogramować wszystkie operacje niskopoziomowe związane z przechowywaniem komponentów, pobieraniem ich, uaktualnieniem danych, usuwaniem oraz co najważniejsze musi zarządzać transakcyjnościa tych operacji. Takie stosowanie zalecane jest w zasadzie tylko w wyjątkowych sytuacjach, ponieważ standardowe mechanizmy kontenera EJB bez większych problemów dają sobie radę z takimi operacjami odciążając programistę od programowania niskopoziomowych modułów związanych z komunikacją z pamięcią masową. Istnieją jednak sytuacje, kiedy funkcjonalność dostarczana przez kontener EJB nie wystarcza. Dla przykładu jeśli aplikacje chcemy osadzić na silniku bazodanowym, którego kontener nie obsługuje. Większość znanych silników baz danych przez kontenery EJB jest obsługiwana, natomiast zawsze może się zdarzyć sytuacja, że aplikacja używa "specyficznego" silnika bazy danych i operacje dostępu do niego programista musi sam oprogramować. Kolejnym przykładem, kiedy użycie komponentów BMP będzie zasadne jest sytuacja, kiedy do przechowywania danych trwałych o stanie komponentu Entity nie jest używana baza danych, tylko jakieś inne repozytorium, np. LDAP lub kiedy istnieją specyficzne wymagania co do transakcyjności operacji.

Komponent MDB (Message-Driven Bean)

W przypadku komponentów Entity i Session mamy do czynienia z synchronicznymi operacjami na komponentach, gdzie w komponentach Entity zmieniamy stan komponentu, a w komponentach Session wywołujemy jakąś usługę. Do operacji asynchronicznych, czyli nieblokujących innych operacji stworzono komponenty MDB. Komponent ten jest wywoływany, do obsługi każdego komunikatu JMS znajdującego się w kolejce związanej z tym komponentem. Konfigurując zasoby serwera aplikacji tworzymy w nim zasób opisujący kolejkę JMS i połączenie do tej kolejki oraz korelujemy tą kolejkę za pomocą odpowiednich procesów nasłuchowych z komponentem MDB. Komponent ten, to klasa JAVA, implementująca odpowiedni interfejs z najważniejszą jej metodą onMessage, którą należy zaimplementować. Metoda ta nie zwraca żadnych wartości, a jako argument przyjmuje obiekt Message. Obiekt ten reprezentuje komunikat JMS przeczytany z kolejki. Metoda ta jest uruchamiana dla każdego komunikatu oddzielnie. Aby obsługa tych komunikatów nie odbywała się iteracyjnie, tylko współbieżnie można tak skonfigurować serwer aplikacji aby jednocześnie można było obsłużyć więcej niż jeden komunikat. Przykład implementacji metody onMessage wyświetlający przeczytany komunikat JMS, jego nagłówki i dane może mieć postać:


public void onMessage(Message inMessage) {
	TextMessage msg = null; 
	try { 
		if (inMessage instanceof TextMessage) { 
		msg = (TextMessage) inMessage; 
		System.out.println("MESSAGE BEAN: Message received: "+ 
		msg.getText()); 
		} else { 
			System.out.println("Message of wrong type: "+
			 inMessage.getClass().getName()); 
		} 
	} catch (JMSException e) { 
		e.printStackTrace(); 
		mdc.setRollbackOnly(); 
	} catch (Throwable te) { 
		te.printStackTrace(); 
	} 
}

	

powrót
Zachęcam do przedstawienia swoich uwag i opinii w polu komentarzy.

Komentarze

Dodaj Komentarz