r12-05.doc

(98 KB) Pobierz
Szablon dla tlumaczy

W niniejszym rozdziale:

·         Dystrybucja ładunku

·         Integracja z J2EE

Rozdział 12.

Serwlety korporacyjne i J2EE

Niniejszy rozdział opisuje serwlety korporacyjne. Termin korporacyjny (enterprise) jest w obecnych czasach mocno związany z Javą, co jednak oznacza? Według starszego wydania słownika „American Heritage Dictionary” słowo enterprise posiada trzy znaczenia:

1.       Podejmowanie działań, zwłaszcza o dużej skali lub ryzyku

2.       Biznes

3.       Gotowość na ryzyko, inicjatywa

Jest to zaskakująco bliska definicja tego, co próbują powiedzieć ludzie mówiący o korporacyjnej Javie i korporacyjnych serwletach. Można połączyć tradycyjne definicje w celu utworzenia definicji nowoczesnej:

  1. Gotowość do obsługi działań biznesowych o dużej skali

Innymi słowy, serwlety korporacyjne to serwlety zaprojektowane do obsługi zorientowanych biznesowo witryn WWW o dużej skali — witryny o wysokim obciążeniu i niezawodności wiążą się z dodatkowymi wymaganiami na temat skalowalności, rozkładu ładunku, obsługi błędów i integracji z innymi technologiami Java 2, Enterprise Edition (J2EE).

Odkąd serwlety zaczęły być coraz bardziej popularne i profesjonalne, a także kontenery serwletów stały się bardziej niezawodne i wszechstronne, coraz większa liczba witryn biznesowych tworzona jest przy pomocy serwletów. Tworzenie serwletów dla takich witryn różni się od tworzenia ich dla witryn tradycyjnych, i w tym rozdziale omówione zostaną specjalne wymagania i możliwości serwletów korporacyjnych.

Dystrybucja ładunku

W przypadku witryn o wysokiej wydajności i/lub niezawodności, często korzystna jest dystrybucja zawartości i przetwarzania witryny pomiędzy większą ilością serwerów wspierających. Dystrybucja ta pozwala kilku serwerom na współdzielenie ładunku, co zwiększa ilość równoczesnych żądań, które mogą zostać obsłużone, oraz pozwala na przetrwanie i działanie witryny nawet wtedy, gdy zawiedzie jeden konkretny składnik.

Dystrybucja nie jest polecana dla każdej witryny. Tworzenie i utrzymanie witryny dystrybuowanej może być w znaczący sposób trudniejsze niż wykonanie tych samych czynności w przypadku witryny samodzielne, oraz bardziej kosztowne w kwestii sprzętu dzielącego ładunek i/lub wymagań programów. Dystrybucja nie przynosi również znaczącej poprawy wydajności, jeżeli serwer nie znajduje się pod maksymalnym obciążeniem. Po wystąpieniu problemu z wydajnością, często najłatwiej jest „rzucić sprzęt na problem” poprzez instalację pojedynczego wysokowydajnego komputera, a nie podzielić ładunek pomiędzy dwoma komputerami o niewystarczającej wydajności.

Istnieje jednak wiele witryn, które wymagają skalowalności poza osiągami dowolnego pojedynczego komputera i poziomu niezawodności niemożliwego do uzyskania na pojedynczym komputerze. W witrynach tych należy zastosować dystrybucję.

Jak być dystrybuowalnym

Wymagania programistyczne dla serwletu dystrybuowalnego są dużo bardziej restrykcyjne niż wymagania odnoszące się do innych serwletów. Serwlet dystrybuowalny musi zostać napisany według konkretnych zasad tak, aby różne egzemplarze serwletu mogły być wykonywane na wielu serwerach wspierających. Każdy programista, który przyjmuje, że istnieje tylko jedna kopia serwletu, jeden kontekst serwletu, jedna wirtualna maszyna Javy i jeden system plików, może sprawić sobie poważne problemy.

Aby zrozumieć, jak serwlety mogą być dystrybuowane, proszę spojrzeć na technologię Enterprise JavaBeans (EJB), model elementów po stronie serwera stworzony w celu implementacji dystrybuowanych obiektów biznesowych, który jest sercem J2EE. EJB jest zaprojektowany od początku jako zbiór obiektów dystrybuowalnych. ELB implementuje logikę biznesową i pozwala kontenerowi (zwykle serwerowi), na którym pracuje, na uruchamianie usług zarządzających takich, jak transakcje, trwałość, współbieżność i bezpieczeństwo. EJB może być dystrybuowany pomiędzy większą ilością komputerów wspierających i może być przenoszony pomiędzy komputerami przy pomocy kontenera. Aby wykorzystywać ten model dystrybucji, EJB musi spełniać ścisły zestaw zasad opartych na specyfikacji, mówiący, co może on robić, a czego nie

Do red prow: w przypisie autor powołuje się na jakąś książkę, może jakąś naszą?

[1].

Serwlety nie posiadają takiego opartego na specyfikacji zestawu zasad. Wynika to z ich pochodzenia jako frontowych elementów po stronie serwera, wykorzystywanych do komunikacji z klientem i wywoływania dystrybuowanych EJB, podczas, gdy same serwlety nie są przeznaczone do dystrybucji. Jednak w przypadku witryn o dużym obciążeniu lub witryn, które wymagają wysokiej niezawodności, również serwlety muszą być dystrybuowane. Przypuszcza się, że przyszłe wersje Servlet API będą zawierać ściślejszą definicję implementacji dystrybuowalnych kontenerów serwletów.

Poniżej podano utworzone przez autorów niniejszej książki skrótowe zasady tworzenia serwletów, które mają być umieszczane w środowisku dystrybuowalnym:

·         Należy pamiętać, że różne egzemplarze serwletu mogą pracować w różnych JVM i/lub komputerach. W związku z tym zmienne egzemplarza i zmienne statyczne nie powinny być wykorzystywane do przechowywania stanu. Każdy stan powinien być przechowywany w obiekcie zewnętrznym, takim jak baza danych lub EJB (do poszukiwań można zastosować nazwę serwletu).

·         Należy pamiętać, że w każdym JVM i/lub komputerze mogą istnieć różne kopie ServletContext. W związku z tym kontekst nie powinien być wykorzystywany do przechowywania stanu aplikacji. Każdy stan powinien być przechowywany w obiekcie zewnętrznym, takim jak baza danych lub EJB (do poszukiwań można zastosować nazwę serwletu).

·         Należy pamiętać, że dowolny obiekt umieszczony w HttpSession powinien mieć możliwość przeniesienia do (lub uzyskania dostępu z) innego komputera. Na przykład, obiekt może wykorzystywać java.io.Serializable. Proszę pamiętać, że ponieważ sesje mogą poruszać się, zdarzenie usunięcia dowiązania sesji może wystąpić w innym komputerze niż zdarzenie jej dowiązania!

·         Należy pamiętać, że pliki nie muszą występować na wszystkich komputerach wspierających. W związku z tym powinno się unikać wykorzystywania pakietu java.io w celu uzyskania dostępu do pliku i zamiast tego wykorzystać mechanizm getServletContext().getResource() — lub upewnić się, że wszystkie wykorzystywane pliki zostały umieszczone na wszystkich komputerach wspierających.

·         Należy pamiętać, że synchronizacja nie jest globalna i działa jedynie w przypadku lokalnej JVM.

Aplikacja WWW, której elementy spełniają powyższe zasady może zostać oznaczona jako dystrybuowalna, a to oznaczenie pozwala serwerowi na uruchomienie jej na kilku serwerach wspierających. Znak dystrybuowalności jest umieszczany w deskryptorze web.xml jako pusty znacznik <distribuable/> umieszczony pomiędzy opisem aplikacji i jej parametrami kontekstu:

<web-app>

  <description>

    Wszystkie serwlety i strony JSP są gotowe do dystrybucji

  </description>

 

  <distribuable/>

 

  <context-param>

    <!--...-->

  </context-param>

</web-app>

Aplikacje z definicji nie są dystrybuowalne w celu zapewnienia niedoświadczonym programistom serwletów możliwości tworzenia ich bez potrzeby martwienia się o dodatkowe zasady związane z dystrybucją. Zaznaczenie aplikacji jako dystrybuowalnej niekoniecznie oznacza, że aplikacja zostanie podzielona pomiędzy kilkoma różnymi komputerami. Wskazuje to jedynie, że aplikacja ma możliwość bycia dystrybuowaną. Należy myśleć o tym jako o wystawionym przez programistę świadectwie aplikacji.

Serwery nie wymuszają większości podanych powyżej zasad dystrybucji aplikacji. Na przykład, serwlet może wykorzystywać zmienne egzemplarza i statyczne, a także przechowywać obiekty w swoim ServletContext oraz uzyskiwać bezpośredni dostęp do plików przy pomocy pakietu java.io. Od programisty zależy zabezpieczenie tych własności przed nadużyciem. Jedynym działaniem, jakie może podjąć serwer jest wywołanie wyjątku IllegalArgumentException, jeżeli obiekt dowiązany do HttpSession nie jest implementacją java.io.Serializable (a nawet to działanie jest opcjonalne, ponieważ, jak zostanie to opisano później, serwer kompatybilny z J2EE musi pozwalać na przechowywanie w sesji dodatkowych typów obiektów).

Wiele stylów dystrybucji

Dystrybucja serwletów (często nazywana klastrowaniem) jest opcjonalną własnością kontenera serwletów, a kontenery serwletów obsługujące klastrowanie mogą wykonywać je na kilka różnych sposobów. Istnieją cztery standardowe architektury, wymienione poniżej, od najprostszej do najbardziej zaawansowanej.

1.       Brak klastrowania. Wszystkie serwlety wykonywane są wewnątrz jednej wirtualnej maszyny Javy, a znacznik <distributable/> jest właściwie ignorowany. Projekt jest prosty, jednak działa poprawnie w przypadku standardowych witryn. W ten sposób działa samodzielny serwer Tomcat.

2.       Obsługa klastrowania, brak sesji wędrujących i unikania błędów. Serwlety w aplikacji WWW zaznaczone jako <distributable/> mogą być wykonywane na kilku komputerach. Żądania niezwiązane z sesjami są dystrybuowane losowo (z zachowaniem równomiernego obciążenia). Żądania sesji są „lepkie” i przywiązane do konkretnego serwera wspierającego, na którym rozpoczęły działanie. Dane sesji nie przemieszczają się pomiędzy komputerami, co posiada zaletę taką, że sesje mogą przechowywać nie przenoszone dane (nie-Serializable) oraz wadę taką, że sesje nie mogą być przenoszone do niewykorzystywanych serwerów, a serwer może załamać się z powodu uszkodzonej sesji. Architektura ta wykorzystywana jest przez Apache/JServ i Apache/Tomcat. Sesje są przywiązane do konkretnego komputera poprzez mechanizm, w którym łącznik mod_jserv/mod_jk będący częścią Apache'a wykorzystuje część identyfikatora sesji w celu wskazania, który wspierający JServ lub Tomcat jest właścicielem sesji. Wykorzystanych może być również kilka egzemplarzy Apache'a, obsługujących sprzęt lub oprogramowanie rozkładające obciążenie.

3.       Obsługa klastrowania i sesji wędrujących, brak unikania błędów. Architektura ta pracuje podobnie jak poprzednia, poza tym, że sesje mogą przenosić się z jednego serwera do drugiego w celu lepszego rozłożenia obciążenia. Aby uniknąć kwestii współbieżności, każda wędrówka sesji posiada gwarancję wystąpienia pomiędzy żądaniami klienta. Specyfikacja serwletów mówi, że „wewnątrz aplikacji oznaczonej jako dystrybuowalna wszystkie żądanie będące częścią sesji mogą być obsługiwane jedynie przez pojedynczą maszynę wirtualną w jednym czasie”. Wszystkie obiekty umieszczone w sesji, które mają być przenoszone muszą wykorzystywać java.io.Serializable lub mieć możliwość przenoszenia w pewien inny sposób.

4.       Obsługa klastrowania, sesji wędrujących i unikania błędów. Serwer wykorzystujący tę architekturę posiada dodatkową możliwość powielania zawartości sesji tak, że załamanie pojedynczego elementu niekoniecznie niszczy sesje klienta. Wyzwaniem tej architektury jest koordynacja wydajnego przepływu informacji. Architekturę tę wykorzystuje większość wysokowydajnych serwerów.

 

Szczegóły implementacji klastrowania różnią się między serwerami i są miejscem rywalizacji poszczególnych producentów serwerów. Proszę spojrzeć do dokumentacji własnego serwera w celu uzyskania szczegółów na temat obsługiwanego poziomu klastrowania. Inną przydatną własnością jest trwałość sesji, czyli zapisywanie w tle informacji sesji na dysk lub do bazy danych, co pozwala informacjom na przetrwanie załamań i ponownych uruchomień serwera.

Integracja z J2EE

W poprzednich częściach książki serwlety były wykorzystywane jako samodzielna technologia utworzona na standardowej podstawie Javy. Serwlety posiadają także inne życie, w którym działają jako integralna część czegoś, co nazywane jest Java 2, Enterprise Edition, w skrócie J2EE[2]. J2EE 1.2 zbiera razem kilka interfejsów pracujących po stronie serwera, w tym Servlet API 2.2, JSP 1.1, EJB, JavaMail, Java Messaging Service (JMS), Java Transactions (JTA), CORBA, JDBC, Java API for XML Parsing (JAXP) i Java Naming and Directory Interface (JNDI). J2EE integruje te elementy w coś więcej niż prostą sumę części poprzez zdefiniowanie sposobu współpracy tych technologii, ich wykorzystywania siebie nawzajem oraz dostarczania zaświadczeń, że konkretne serwery aplikacji są zgodne z J2EE, co oznacza, że obsługują wszystkie potrzebne usługi, a także mechanizmy ich integracji.

Podział pracy w J2EE

J2EE rozbija tworzenie aplikacji korporacyjnych na sześć różnych ról. Oczywiście pojedyncza osoba może pełnić więcej niż jedną rolę, albo też kilka osób może pracować wspólnie nad zadaną rolą.

·         Dostawca produktów J2EE — producent systemu operacyjnego, systemu baz danych, serwera aplikacji i/lub serwera WWW. Dostawca produktów dostarcza implementacji interfejsów J2EE i narzędzi do tworzenia i zarządzania aplikacją.

·         Dostawca elementów aplikacji — autor serwletów aplikacji, EJB i innego kodu, a także ogólnej zawartości takiej jak HTML. (Innymi słowy, Czytelnik tej książki.)

·         Monter aplikacji — pobiera elementy aplikacji i (przy pomocy narzędzi dostarczonych przez dostawcę produktów) nadaje im formę odpowiednią do wdrożenia. Częścią tej funkcji jest utworzenie opisu zewnętrznych zależności aplikacji, które mogą zmieniać się w zależności od wdrożenia, takich jak baza danych bądź informacje na temat logowania.

·         Wdrożeniowiec — pobiera wyniki pracy montera i (przy pomocy narzędzi dostarczonych przez dostawcę produktów) instaluje, konfiguruje i uruchamia aplikację. Zadanie konfiguracji wymaga dostosowania produktu do zewnętrznych zależności wymienionych przez montera.

·         Administrator systemu — konfiguruje i administruje infrastrukturę sieci, utrzymując pracę aplikacji.

·         Dostawca narzędzi — Tworzy narzędzia wspierające wdrażanie J2EE, poza tymi, które są dostarczone przez dostawcę produktów.

Podział pracy pomiędzy dostawcą elementów, monterem i wdrożeniowcem ma wpływ na zachowanie programisty serwletów w roli dostawcy zawartości. Zwłaszcza należy zaprojektować kod tak, aby uczynić zewnętrzne zależności jasnymi dla montera, a poza tym powinno się wykorzystać mechanizmy pozwalające wdrożeniowcowi na dostosowanie się do tych zależności bez konieczności modyfikacji plików otrzymanych od montera. Oznacza to, że żaden wdrożeniowiec nie edytuje pliku web.xml! Dlaczego nie? Ponieważ aplikacje J2EE są wmontowane w pliki archiwa Enterprise Archive (.ear), w których plik web.xml aplikacji WWW jest jedyną częścią, której nie można edytować.

Brzmi to o wiele trudniej, niż tak naprawdę wygląda. J2EE dostarcza standardowego mechanizmu w celu osiągnięcia tego wyłączenia przy pomocy JNDI i kilku specjalnych znaczników w deskryptorze web.xml. JNDI to mechanizm wyszukiwania obiektów, sposób na związanie ich z konkretnymi ścieżkami i późniejszego odnalezienia przy pomocy danych ścieżek. Można myśleć o tym mechanizmie jak o rejestrze RMI, poza tym, że jest on bardziej ogólny poprzez obsługę dostępu do wielu usług, włączając w to LDAP i NIS (a nawet, właściwie, rejestr RMI!). Monter deklaruje zewnętrzne zależności w web.xml przy pomocy specjalnych znaczników, wdrożeniowiec dostosowuje aplikacje do tych zależności przy pomocy właściwych serwerowi narzędzi, a podczas uruchomienia kod Javy wykorzystuje interfejs JNDI w celu uzyskania dostępu do zasobów zewnętrznych — umieszczonych tam przez serwer zgodny z J2EE. Wszystkie cele zostają wypełnione — kod Javy pozostaje przenośny pomiędzy serwerami zgodnymi z J2EE, a wdrożeniowiec może dostosować się do zależności zewnętrznych bez konieczności modyfikowania plików otrzymanych od montera. W tym miejscu pozostawiono nawet wystarczającą elastyczność, która pozwala producentom serwerów na rywalizację w implementacji standardu.

Pozycje środowiskowe

Parametry inicjacji kontekstu są użyteczne dla serwletów, ale w modelu J2EE istnieje z nimi problem— każda zmiana wartości parametru wymaga modyfikacji pliku web.xml. Zamiast wartości parametrów, które mogą zmienić się w trakcie wdrażania lepiej jest zastosować pozycje środowiskowe, wskazywane przez znaczniki <env-entry>. Znacznik <env-entry> może zawierać znaczniki <description> (opis), <env-entry-name> (nazwa), <env-entry-value> (wartość) i <env-entry-type> (typ). Poniższy <env-entry> określa, czy aplikacja powinna umożliwiać wysyłanie kodu PIN przy pomocy poczty elektronicznej:

<env-entry>

  <description>Wysyłanie kodu PIN pocztą</description>

  <env-entry-name>pocztaPIN</env-entry-name>

  <env-entry-value>false</env-entry-value>

  <env-entry-type>java.lang.Boolean</env-entry-type>  <!--PNK-->

</env-entry>

Znacznik <description> wyjaśnia wdrożeniowcowi cel tej pozycji. Jest on opcjonalny, lecz warto go umieszczać. <env-entry-name> jest wykorzystywany przez kod Javy jako część wyszukiwania JNDI. <env-entry-value> definiuje domyślną wartość przekazywaną wdrożeniowcowi. On także jest opcjonalny, lecz wart umieszczenia. <env-entry-type> definiuje domyślną pełną nazwę klasy (PNK) pozycji. Typ może być jednym z następujących — String, Byte, Short, Integer, Long, Boolean, Double lub Float (wszystkie z ich pełną kwalifikacją java.lang). Typ pomaga wdrożeniowcowi w zorientowaniu się, czego spodziewa się serwer. Powyższe znaczniki mogą wydawać się znajome osobom, którym nieobcy jest deskryptor EJB, posiadają one identyczne nazwy i semantykę.

Kod Javy może odczytać wartości <env-entry> przy pomocy JNDI:

Context poczKontekst = new InitialContext();

Boolean pocztaPIN = (Boolean) poczKontekst.lookup("java:comp/env/pocztaPIN");

Wszystkie pozycje umieszczane są przez serwer w kontekście java:comp/env. Osoby nie znające JNDI mogą o nim myśleć jako o bazie URL-a lub katalogu w systemie plików. Kontekst java:comp/env ma własności tylko do odczytu i jest unikatowy dla aplikacji WWW, tak więc jeżeli dwie różne aplikacje WWW zdefiniują taką samą pozycję środowiskową, pozycje te nie kolidują. Skrót nazwy kontekstu oznacza component environment (środowisko elementów).

Przykład 12.1 przedstawia serwlet wyświetlający wszystkie jego pozycje środowiskowe, wykorzystując interfejs JNDI do przeglądania kontekstu java:comp/env.

Przykład 12.1.

Przeglądanie kontekstu java:comp/env

import java.io.*;

import java.util.*;

import javax.servlet.*;

import javax.servlet.http.*;

 

import javax.naming.*;

 

public class PrzeglPozSrod extends HttpServlet {

 

  public void doGet(HttpServletRequest zad, HttpServletResponse odp)

                               throws ServletException, IOException {

    odp.setContentType("text/plain");

    PrintWriter wyj = odp.getWriter();

 

    try {

      Context poczKontekst = new InitialContext();

      NamingEnumeration enum = poczKontekst.listBindings("java:comp/env");

 

      // Wykorzystywane są metody JDK 1.2; można to zrobić, ponieważ J2EE wymaga JDK 1.2

      while (enum.hasMore()) {

        Binding wiazanie = (Binding) enum.next();

        wyj.println("Nazwa: " + wiazanie.getName());

        wyj.println("Typ: " + wiazanie.getClassName());

        wyj.println("Wartość: " + wiazanie.getObject());

        wyj.println();

      }

    }

    catch (NamingException w) {

      w.printStackTrace(wyj);

    }

  }

}

Przyjmując poprzednią pozycję web.xml, serwlet wygenerowałby:

Nazwa: pocztaPIN

Typ: java.lang.Boolean

Wartość: false

Proszę pamiętać, że serwer, który nie obsługuje J2EE niekoniecznie obsługuje powyższe znaczniki, ani żadne inne opisywane w tym podrozdziale.

Odwołania do elementów EJB

Jeśli obiekt w pozycji środowiskowej to element EJB, należy wykorzystać specjalny znacznik <ejb-ref>. Daje on serwletom możliwość obsługi EJB przy pomocy abstrakcyjnej nazwy. Wdrożeniowiec zapewnia dostępność odpowiedniego elementu w trakcie uruchamiania w oparciu o ograniczenia podane w znaczniku <ejb-ref>. Znacznik ten może zawierać znaczniki <description>, <ejb-ref-name>, <ejb-ref-type>, <home>, <remote> i <ejb-link>. Poniżej przedstawiony jest typowy <ejb-ref>:

...

Zgłoś jeśli naruszono regulamin