r20-05.doc

(120 KB) Pobierz
Szablon dla tlumaczy

W niniejszym rozdziale:

·         Zmiany w Servlet API 2.3

·         Konkluzja

Rozdział 20.

Zmiany w Servlet API 2.3

Krótko przed przekazaniem niniejszej książki do druku firma Sun Microsystems opublikowała Proposed Final Draft (Proponowaną Ostateczną Próbę) specyfikacji Servlet API 2.3[1]. Nie jest to ostateczna wersja specyfikacji; Proposed Final Draft to krok do formalnej Final Release (Wersji Ostatecznej), tak więc szczegóły techniczne ciągle mogą ulec zmianie. Jednak zmiany te nie powinny być znaczące — tak naprawdę producenci serwerów już rozpoczęli implementację nowych funkcji. W niniejszym rozdziale opisane zostaną w skrócie wszystkie zmiany, jakie zaszły pomiędzy API 2.2 i 2.3. Wyjaśnione także zostaną powody zmian i przedstawione zostaną sposoby tworzenia serwletów przy pomocy nowych funkcji[2].

Zmiany w Servlet API 2.3

Servlet API 2.3 właściwie pozostawia nietknięte jądro serwletów, co oznacza, że serwlety osiągnęły wysoki poziom dojrzałości. Większość działań związana była z dodaniem nowych funkcji poza jądrem. Zmiany te to między innymi:

·         Serwlety wymagają teraz JDK 1.2 lub późniejszego.

·         Utworzony został (nareszcie) mechanizm filtrów.

·         Dodane zostały nowe zdarzenia okresu trwałości aplikacji.

·         Dodana została nowa obsługa internacjonalizacji.

·         Sformalizowana została technika wyrażania zależności pomiędzy plikami JAR.

·         Wyjaśnione zostały zasady ładowania klas.

·         Dodane zostały nowe atrybuty błędów i bezpieczeństwa.

·         Opuszczona została klasa HttpUtils.

·         Dodane zostały różne nowe pomocne metody.

·         Rozszerzone i wyjaśnione zostało kilka zachowań DTD.

Wykonane zostały także inne wyjaśnienia, ale skupiają się one głównie na producentach serwerów, nie ogólnie na programistach serwletów (poza faktem, że programiści ujrzą poprawioną przenośność), tak więc te szczegóły zostaną tu ominięte.

Przed rozpoczęciem przyglądania się zmianom należy zaznaczyć, że wersja 2.3 została udostępniona jedynie jako specyfikacja próbna. Większość opisanych tu funkcji nie będzie działać na wszystkich serwerach. Aby przetestować te funkcje, poleca się pobranie oficjalnego wzorcowego serwera, Apache Tomcat 4.0. Jest to Open Source i może być pobrany za darmo. Tomcat 4.0 jest aktualnie dostępny w wersji beta; obsługa Servlet API 2.3 staje się coraz lepsza, ale ciągle jest niekompletna. Proszę przeczytać plik NEW_SPECS.txt dołączony do Tomcata 4.0 w celu poznania jego poziomu wsparcia dla wszystkich funkcji nowej specyfikacji.

Serwlety w J2SE i J2EE

Jedną z pierwszych rzeczy, jakie zazwyczaj zauważa się na temat Servlet API 2.3 jest fakt, że serwlety zależą teraz od platformy Java 2, Standard Edition (znanej także jako J2SE 1.2 lub JDK 1.2). Ta mała, ale ważna zmiana oznacza, że w serwletach można teraz wykorzystywać funkcje J2SE 1.2 z gwarancją, że serwlety te będą działać na wszystkich kontenerach. Poprzednio funkcje J2SE mogły być wykorzystywane, ale ich obsługa przez serwery nie była obowiązkowa.

Servlet API2.3 ma stać się częścią platformy Java 2, Enterprise Edition 1.3 (J2EE 1.3). Poprzednia wersja, Servlet API 2.2 była częścią J2EE 1.2. Jedyną zauważalną różnicą jest fakt dodania kilku stosunkowo obskurnych znaczników deskryptora związanych z J2EE w deskryptorze web.xml<resource-env-ref> obsługującego „obiekty administrowane”, takie jak te wymagane przez Java Messaging System (JMS), <res-ref-sharing-scope> pozwalający na współdzielony lub wyłączny dostęp do odwołania do zasobów oraz <run-as> określający identyfikator bezpieczeństwa dla wywołującego EJB. Większość autorów serwletów nie powinna przejmować się tymi znacznikami J2EE; pełny ich opis dostępny jest w specyfikacji J2EE 1.3.

Filtry

Najbardziej znaczącą częścią API 2.3 jest dodanie filtrów. Filtry są obiektami potrafiącymi zmieniać kształt żądania lub modyfikować odpowiedź. Proszę zauważyć, że nie są to serwlety; tak naprawdę nie tworzą one odpowiedzi. Przetwarzają one wstępnie żądanie zanim dotrze ono do serwletu, oraz przetwarzają odpowiedź opuszczającą serwlet. W skrócie, filtry są dojrzałą wersją starego pomysłu „łańcuchów serwletów”. Filtr potrafi:

·         Przechwycić wywołanie serwletu przed jego wywołaniem.

·         Sprawdzić żądanie przed wywołaniem serwletu.

·         Zmodyfikować nagłówki i dane żądania poprzez dostarczenie własnej wersji obiektu żądania, który zostaje dołączony do prawdziwego żądania.

·         Zmodyfikować nagłówki i dane odpowiedzi poprzez dostarczenie własnej wersji obiektu odpowiedzi, który zostaje dołączony do prawdziwej odpowiedzi.

·         Przechwycić wywołanie serwletu po jego wywołaniu.

Filtr może być skonfigurowany tak, by działał jako serwlet lub grupa serwletów; ten serwlet lub grupa może zostać przefiltrowana przez dowolną ilość filtrów. Niektóre praktyczne idee filtrów to między innymi filtry uwierzytelniania, filtry logowania i nadzoru, filtry konwersji obrazków, filtry kompresji danych, filtry kodowania, filtry dzielenia na elementy, filtry potrafiące wywołać zdarzenia dostępu do zasobów, filtry XSLT przetwarzające zawartość XML lub filtry łańcuchowe typu MIME (podobne do łańcuchów serwletów).

Filtr jest implementacją javax.servlet.filter i definiuje trzy metody tej klasy:

void setFilterConfig(FilterConfig konfig)

Ta metoda ustawia obiekt konfiguracyjny filtra.

FilterConfig getFilterConfig()

Ta metoda zwraca obiekt konfiguracyjny filtra.

void doFilter(ServletRequest zad, ServletResponse odp, FilterChain lancuch)

Ta metoda wykonuje właściwą pracę filtra.

Serwer wywołuje jeden raz setFilterConfig() w celu przygotowania filtru do działania, następnie wywołuje doFilter() dowolną ilość razy dla różnych obiektów. Interfejs FilterConfig posiada metody pobierające nazwę filtra, jego parametry inicjacji oraz aktywny kontekst filtra. Serwer może również przekazać setFilterConfig() wartość null aby wskazać, że filtr zostaje wyłączony.

UWAGA!!!!

Przewiduje się, że w ostatecznej wersji 2.3 metoda getFilterConfig() zostanie usunięta, a metoda setFilterConfig(FilterConfig konfig)zostanie zastąpiona przez init(FilterConfig) i destroy().

Każdy filtr pobiera aktualne żądanie i odpowiedź w swojej metodzie doFilter(), a także FilterChain zawierający filtry, które muszą stale być przetwarzane. W metodzie doFilter() filtr może wykonać dowolne działanie żądania i odpowiedzi. (Na przykład może zbierać dane przez wywoływanie ich metod, dołączać informacje nadające im nowe zachowanie). Filtr następnie wywołuje lancuch.doFilter() w celu przekazania kontroli następnemu filtrowi. Kiedy wywołanie to wraca, filtr może, na końcu swojej własnej metody doFilter(), wykonać dodatkowe działania na odpowiedzi; na przykład może zapisać informacje na temat odpowiedzi w dzienniku. Jeżeli filtr chce całkowicie zatrzymać przetwarzanie żądania i zyskać pełną kontrolę nad odpowiedzią, może specjalnie nie wywoływać następnego filtra.

Filtr może zmieniać obiekty żądania i odpowiedzi w celu dostarczenia własnego zachowania, zmiany konkretnej metody wywołują implementację wpływającą na późniejsze działania obsługujące żądania. Serwlet API 2.3 dostarcza nowych klas HttpServletRequestWrapper i HttpServletResponseWrapper w tym pomagających. Posiadają one domyślną implementację wszystkich metod żądań i odpowiedzi oraz domyślnie przekazują wywołania do oryginalnych żądań i odpowiedzi. Poniżej przedstawiony jest kod prostego filtra logowania, który zapamiętuje czas trwania wszystkich żądań:

import javax.servlet.*;

import javax.servlet.http.*;

 

public class FiltrDziennik implements Filter {

 

  FilterConfig konfig;

 

  public void setFilterConfig(FilterConfig konfig) {

    this.konfig = konfig;

  }

 

  public FilterConfig getFilterConfig() {

    return konfig;

  }

 

  public void doFilter(ServletRequest zad,

                       ServletResponse odp,

                       FilterChain lancuch) {

    ServletContext kontekst = getFilterConfig().getServletContext();

    long przed = System.currentTimeMillis();

    lancuch.doFilter(zad, odp); // nie jest potrzebny żaden parametr łańcucha

    long po = System.currentTimeMillis();

    kontekst.log("Żądanie" + zad.getRequestURI() + ": " +

                                                     (przed-po));

  }

}

Kiedy serwer wywołuje setFilterConfig(), filtr zapamiętuje w swojej zmiennej konfig odwołanie do konfiguracji filtra, które później zostanie wykorzystane w metodzie doFilter() w celu pobrania ServletContext. Logika doFilter() jest prosta — obliczenie czasu obsługi żądania i zapamiętanie czasu po zakończeniu przetwarzania. Aby wykorzystać powyższy filtr, trzeba wcześniej zadeklarować go w deskryptorze DTD przy pomocy znacznika <filter>, jak przedstawiono poniżej:

<filter>

  <filter-name>

    dziennik

  </filter-name>

  <filter-class>

    FiltrDziennik

  </filter-class>

</filter>

Powyższy fragment kodu przekazuje serwerowi informację, że filtr o nazwie dziennik jest zaimplementowany w klasie FiltrDziennik. Można przypisać zarejestrowany filtr do konkretnych wzorów URL-i lub nazw serwletów przy pomocy znacznika <filter-mapping>:

<filter-mapping>

  <filter-name>log</filter-name>

  <url-pattern>/*</url-pattern>

</filter-mapping>

Powyższy fragment konfiguruje filtr tak, aby był stosowany do wszystkich żądań serwera (statycznych lub dynamicznych), co jest działaniem odpowiednim dla filtra zapisującego informacje w dzienniku. Jeżeli nastąpi połączenie, dziennik mógłby wyglądać następująco:

Żądanie /indeks.jsp: 10

Zdarzenia okresu trwałości

Drugą bardzo poważną zmianą w Servlet API 2.3 jest dodanie zdarzeń okresu trwałości aplikacji, które umożliwiają powiadomienie obiektów „nasłuchujących", kiedy inicjowane bądź niszczone są konteksty serwletów i sesje, a także kiedy dodawane lub usuwane z kontekstu lub sesji są atrybuty.

Okres trwałości serwletu jest podobny do zdarzeń Swing. Każdy obiekt nasłuchujący zainteresowany obserwacją okresu trwałości ServletContext może zaimplementować interfejs ServletContextListener. Interfejs ten posiada dwie metody:

void contextInitialized(ServletContextEvent z)

Wywoływana z aplikacji WWW, kiedy jest ona po raz pierwszy gotowa o przetwarzania żądań (tzn. podczas uruchomienia serwera lub dodania albo przeładowania kontekstu). Żądania nie są obsługiwane, dopóki metoda nie zwróci swoich wartości.

void contextDestroyed(ServletContextEvent z)

Wywoływana z aplikacji WWW, która powinna zostać zakończona (tzn. podczas wyłączania serwera lub usuwania albo przeładowania kontekstu). Obsługa żądań zostaje zatrzymana przed wywołaniem tej metody.

Klasa ServletContextEvent przekazywana obu metodom zawiera jedynie metodę pobierzZrodloKontekst(), która zwraca kontekst, który jest inicjowany lub niszczony.

Obiekt nasłuchujący zainteresowany obserwacją okresem trwałości atrybutu ServletContext może zaimplementować interfejs ServletContextAttributesListener, który posiada trzy metody:

void attributeAdded(ServletContextAttributeEvent z)

Wywoływana, kiedy atrybut zostaje dodany do kontekstu serwletu.

void attributeRemoved(ServletContextAttributeEvent z)

Wywoływana, kiedy atrybut zostaje usunięty z kontekstu serwletu.

void attributeReplaced(ServletContextAttributeEvent z)

Wywoływana, kiedy atrybut w kontekście serwletu zostaje wymieniony na inny.

Klasa ServletContextAttributeEvent jest rozszerzeniem ServletContextEvent i dodaje metody getName() i getValue(), tak więc obiekt nasłuchujący może uzyskać informacje na temat zmieniających się atrybutów. Jest to przydatna własność, ponieważ aplikacje WWW które muszą zsynchronizować stan aplikacji (atrybuty kontekstu) z czymś w rodzaju bazy danych mogą to teraz uczynić w jednym miejscu.

Model obiektu nasłuchującego sesji jest podobny do modelu obiekt nasłuchującego okresu trwałości. W modelu sesji występuje interfejs HttpSessionListener posiadający dwie metody:

void SessionCreated(HttpSessionEvent z)

Wywoływana podczas tworzenia sesji.

void SessionDestroyed(HttpSessionEvent z)

Wywoływana podczas zniszczenia (unieważnienia) sesji.

Powyższe metody przyjmują egzemplarz HttpSessionEvent z metodą dostępu getSession() w celu zwrócenia sesji, która jest tworzona bądź niszczona. Metody te mogą być zastosowane podczas implementacji interfejsu administratora, który śledzi wszystkich aktywnych użytkowników w aplikacji WWW.

Model sesji posiada również interfejs HttpSessionAttributesListener posiadający trzy metody. Metody te informują obiekt nasłuchujący, kiedy zmieniają się atrybuty i mogłyby być zastosowane, na przykład przez aplikację synchronizującą dane profilu przechowywane w sesji do bazy danych:

void attributeAdded(HttpSessionBindingEvent z)

Wywoływana, kiedy atrybut zostaje dodany do sesji.

void attributeRemoved(HttpSessionBindingEvent z)

Wywoływana, kiedy atrybut zostaje usunięty z sesji.

void attributeReplaced(HttpSessionBindingEvent z)

Wywoływana, kiedy atrybut w sesji zostaje wymieniony na inny.

Jak można się było spodziewać, klasa HttpSessionBindingEvent jest rozszerzeniem HttpSessionEvent i dodaje metody getName() i getValue(). Jedyna dość niezwykła własność jest taka, że klasa zdarzeń nosi nazwę HttpSessionBindingEvent, a nie HttpSessionAttributeEvent. Dzieje się tak z powodu tradycji — API posiadał już klasę HttpSessionBindingEvent, tak więc została ona wykorzystana ponownie. Ten nieco mylący aspekt API może zostać poprawiony w ostatecznej wersji.

Możliwym praktycznym zastosowaniem dla zdarzeń okresu trwałości jest współdzielone połączenie z bazą danych zarządzane przez obiekt nasłuchujący kontekstu. Obiekt ten deklarowany jest w pliku web.xml, w następujący sposób:

<listener>

  <listener-class>

    com.firma.MojZarzadcaPolaczen

  </listener-class>

</listener>

Serwer tworzy egzemplarz klasy nasłuchującej służącej do otrzymywania zdarzeń i wykorzystuje introspekcje w celu określenia, jaki interfejs (lub interfejsy) nasłuchu powinien zostać wykorzystany przez klasę. Należy pamiętać, że ponieważ mechanizm nasłuchujący jest konfigurowany w deskryptorze, można dodawać nowe mechanizmy bez konieczności zmiany kodu. Sam obiekt nasłuchujący mógłby wyglądać następująco:

import javax.servlet.*;

import javax.servlet.http.*;

 

public class MojZarzadcaPolaczen implements ServletContextListener {

 

  public void contextInitialized(ServletContextEvent z) {

    Connection pol =     // utworzenie połączenia

      z.getServletContext().setAttribute("pol", pol);

  }

 

  public void contextDestroyed(ServletContextEvent z) {

    Connection pol =

       (Connection) z.getServletContext().getAttribute("pol");

    try { pol.close(); }

    catch (SQLException ignored) { } // zamknięcie połączenia

  }

}

Powyższy obiekt nasłuchujący zapewnia dostępność połączenia z bazą danych w każdym nowym kontekście serwletu oraz zamknięcie wszystkich połączeń po zamknięciu kontekstu.

Interfejs HttpSessionActivationListener, kolejny nowy interfejs nasłuchu w API 2.3 jest zaprojektowany do obsługi sesji wędrujących z jednego serwera do drugiego. Obiekt nasłuchujący będący implementacją HttpSessionActivationListener jest powiadamiany o gotowości każdej sesji do wędrówki oraz kiedy sesja gotowa jest do aktywacji na drugim komputerze. Metody te dają aplikacji szansę przesuwania danych niemożliwych do zserializowania pomiędzy wirtualnymi maszynami Javy, lub doklejania i odklejania obiektów zserializowanych pomiędzy pewnego rodzaju modelem obiektów przed lub po migracji. Interfejs ten posiada dwie metody:

void sessionWillPassivate(HttpSessionEvent z)

Sesja ma zamiar dokonać wędrówki. Sesja jest już niedostępna kiedy następuje to wywołanie.

void sessionDidActivate(HttpSessionEvent z)

Sesja została aktywowana. Sesja nie jest jeszcze w użytku, kiedy następuje to wywołanie.

Taki obiekt nasłuchujący jest rejestrowany tak, jak inne. Jednak różnica pomiędzy nimi jest taka, że wywołania migracji i aktywacji najprawdopodobniej będą występować na dwóch różnych serwerach!

Wybranie kodowania znaków

Servlet API 2.3 dostarcza tak bardzo potrzebnej obsługi formularzy wysyłanych w różnych językach. Nowa metoda, zadanie.setCharacterEncoding(String kodowanie) pozwala na poinformowanie serwera o kodowaniu znaków żądania. Kodowanie znaków (albo po prostu kodowanie) to sposób odwzorowania bajtów na znaki. Serwer może wykorzystać określone kodowanie, aby prawidłowo zanalizować parametry i dane POST.

Domyślnie serwer analizuje parametry przy pomocy popularnego kodowania Latin-1 (ISO 8859-1) Niestety jest ono odpowiednie tylko dla języków zachodnioeuropejskich. Kiedy przeglądarka wykorzystuje inne kodowanie, powinna wysyłać informacje o kodowaniu w nagłówku żądania Content-Type, ale prawie żadna przeglądarka nie stosuje się do tej zasady. Metoda setCharacterEncoding() pozwala serwletowi na poinformowanie serwera, które kodowanie jest wykorzystywane (zazwyczaj jest to kodowanie strony, która zawiera formularz); serwer zajmuje się resztą. Na przykład, serwlet pobierający japońskie parametry z formularza zakodowanego przy pomocy Shift_JIS powinien odczytywać parametry w następujący sposób:

// ustawienie kodowania na Shift_JISD

zad.setCharacterEncoding("Shift_JIS");

 

// Odczytanie parametru przy pomocy tego kodowania

String nazwa = zad.getParameter("nazwa");

Proszę pamiętać o ustawieniu kodowania przed wywołaniem getParameter() lub getReader(). Wywołanie setCharacterEncoding() może zgłosić wyjątek java.io.UnsupportedEncodingException, jeżeli dane kodowanie nie jest obsługiwane. Funkcjonalność ta jest również dostępna użytkownikom ...

Zgłoś jeśli naruszono regulamin