r11-05, Programowanie, ! Java, Java Server Programming


W niniejszym rozdziale:

Rozdział 11.

Współpraca serwletów

Serwlety pracujące wspólnie na jednym serwerze mogą porozumiewać się ze sobą na kilka sposobów. Istnieją dwa główne typy współpracy serwletów:

W przeszłości (przed pojawieniem się Servlet API 2.1) wymieniona zostałaby inna technika współpracy — manipulacja bezpośrednia. W tej technice serwlet może otrzymać odwołanie do innego poprzez metodę getServlet() i wywoływać jego metody. Ta metoda współpracy nie jest już obsługiwana; metoda getServlet() została wyłączona i zdefiniowana tak, by zwracała null w przypadku Servlet API 2.1 i późniejszych. Powód — serwlet może zostać zniszczony przez serwer WWW w dowolnym momencie, tak więc nic poza serwerem nie powinno posiadać bezpośredniego odwołania do serwletu. Wszystkie działania, które mogły być wykonywane przy pomocy getServlet() mogą być wykonane w sposób lepszy i bezpieczniejszy przez alternatywy opisane w niniejszym rozdziale.

Dzielenie informacji

Serwlety często współpracują poprzez dzielenie pewnych informacji. Informacje te mogą być informacjami o stanie, współdzielonymi zasobami, źródłem zasobów, lub czymkolwiek innym. W Servlet API 2.0 i wcześniejszych nie istniały wbudowane mechanizmy, przy pomocy których serwlety mogły dzielić informacje, a w pierwszym wydaniu niniejszej książki (napisanej według Servlet API 2.0) musiały zostać przedstawione pewne kreatywne sposoby pracy, między innymi umieszczanie informacji na liście właściwości System! Na szczęście sposoby te nie są już potrzebne, ponieważ począwszy od Servlet API 2.1 rozszerzona została klasa ServletContext, która działa na zasadzie składnicy współdzielonych informacji.

Współdzielenie przy pomocy ServletContext

Serwlet odczytuje ServletContext swojej aplikacji WWW przy pomocy wywołania getServletContext(). Serwlet może wykorzystywać kontekst tak, jakby był on zmienną typu Hashtable lub Map, przy pomocy następujących metod:

public void ServletContext.setAttribute(String nazwa, Object o)

public Object ServletContext.getAttribute(String nazwa)

public Enumeration ServletContext.getAttributeNames()

public void ServletContext.removeAttribute(String nazwa)

Metoda setAttribute() dowiązuje obiekt pod podaną nazwą. Każde istniejące dowiązanie o takiej samej nazwie zostaje usunięte. Nazwy atrybutów powinny stosować tę samą konwencję, co nazwy pakietów, w celu uniknięcia nadpisywania siebie. Nazwy pakietów java.*, javax.* i com.sun.* są zarezerwowane.

Metoda getAttribute() odczytuje obiekt dowiązany pod podaną nazwą lub zwraca null, jeżeli pakiet taki nie istnieje. Wywołanie może również odczytywać specyficzne dla danego serwera, zakodowane atrybuty (na przykład javax.servlet.context.tempdir), jak opisano w rozdziale 4, „Pobieranie informacji”.

Metoda getAttributeNames() zwraca zmienną typu Enumeration, która zawiera nazwy wszystkich dowiązanych atrybutów lub pustą Enumeration, jeżeli nie istnieją żadne dowiązania.

Metoda removeAttribute() usuwa obiekt dowiązany pod daną nazwą lub nie wykonuje żadnego działania, jeżeli atrybut nie istnieje. Dobrym pomysłem jest usuwanie niepotrzebnych już atrybutów w celu zredukowania wykorzystania pamięci.

Zastosowanie kontekstu do sprzedawania pizzy

Jako zabawny przykład, proszę wyobrazić sobie zbiór serwletów sprzedających pizzę i dzielących ofertę dnia. Serwlet administracyjny mógłby ustawiać ofertę dnia w sposób przedstawiony w przykładzie 11.1.

Przykład 11.1.

Pizzeria Italia

import java.io.*;

import java.util.*;

import javax.servlet.*;

import javax.servlet.http.*;

public class UstawiaOferta extends HttpServlet {

public void doGet(HttpServletRequest zad, HttpServletResponse odp)

throws ServletException, IOException {

odp.setContentType("text/plain");

PrintWriter wyj = odp.getWriter();

ServletContext kontekst = getServletContext();

kontekst.setAttribute("com.pizzeria.oferta.pizza", "Cappricciosa");

kontekst.setAttribute("com.pizzeria.oferta.dzien", new Date());

wyj.println("Pizza — oferta dnia została ustawiona.");

}

}

Następnie każdy inny serwlet na serwerze może uzyskać dostęp do oferty i wyświetlić ją przy pomocy kodu przedstawionego w przykładzie 11.2.

import java.io.*;

import java.text.*;

import java.util.*;

import javax.servlet.*;

import javax.servlet.http.*;

public class pobierzOferta extends HttpServlet {

public void doGet(HttpServletRequest zad, HttpServletResponse odp)

throws ServletException, IOException {

odp.setContentType("text/html");

PrintWriter wyj = odp.getWriter();

ServletContext kontekst = getServletContext();

String pizza = (String)

context.getAttribute("com.pizzeria.oferta.pizza");

Date dzien = (Date)

context.getAttribute("com.pizzeria.oferta.dzien");

DateFormat df = DateFormat.getDateInstance(DateFormat.MEDIUM);

String dzisiaj = df.format(dzien);

wyj.println("Oferta dnia(" + dzisiaj + ") to pizza: " + pizza);

}

}

Współdzielenie z innym ServletContext

Wykorzystanie ServletContext do współdzielenia informacji daje dodatkowy efekt w postaci posiadania przez każdą aplikację WWW swojego własnego składu informacji. Nie istnieje ryzyko przypadkowej kolizji nazw lub nawet kolizji nazw z tej samej aplikacji uruchomionej dwukrotnie na serwerze. Jednak czasami informacja musi być dzielona pomiędzy dwoma kontekstami WWW. W tej sytuacji można postąpić na dwa sposoby. Po pierwsze, wykorzystać zewnętrzny skład informacji taki, jak plik bądź bazę danych, lub, po drugie, wykorzystać specjalne punkty zaczepienia w celu bezpośredniego dostępu do innego kontekstu. Kwestia ta zostanie opisana później.

Serwlet może otrzymać uchwyt do innego kontekstu na tym samym serwerze przy pomocy punktu zaczepienia getContext() w swoim własnym kontekście:

public ServletContext ServletContext.getContext(String sciezkauri)

Metoda zwraca ServletContext zawierający podaną ścieżkę URI. Podana ścieżka musi być absolutna (rozpoczynająca się od /) i jest interpretowana według katalogu macierzystego dokumentów serwera. Metoda ta pozwala serwletowi na uzyskanie dostępu do kontekstu poza jego własnym. W środowisku wrażliwym na bezpieczeństwo lub rozpowszechnianym (proszę zobaczyć rozdział 12, „Serwlety korporacyjne i J2EE”), kontener serwletów może zwrócić null dla wszystkich ścieżek.

Aby to zademonstrować, proszę wyobrazić sobie, że serwlet poza dostępem do pizzerii musi uzyskać dostęp do oferty dnia. Proszę także przyjąć, że kontekst pizzerii znajduje się w katalogu /pizzeria. W końcu proszę założyć, że kontrola bezpieczeństwa serwera (nie dopuszczająca do komunikacji międzykontekstowej) została wyłączona. Pod tymi warunkami następujący kod pozwala dowolnemu serwletowi na serwerze na odczytanie ofert dnia z aplikacji pizzerii:

ServletContext mojKontekst = getServletContext();

ServletContext innyKontekst = mojKontekst.getContext("/pizzeria/indeks.httml");

String pizza = innyKontekst.getAttribute("com.pizzeria.oferta.pizza");

Date dzien = (Date)innyKontekst.getAttribute("com.pizzeria.oferta.dzien");

Aktualnie jedyną metodą pobierania odwołania do innego kontekstu jest wyszukiwanie ścieżki, tak więc powyższy przykład nie będzie działał, jeżeli pizzeria przeniesie się do innego URL-a. W przyszłości może pojawić się sposób na pobieranie odwołania według nazwy.

Kwestie mechanizmu ładowania klas

Jeżeli obiekt umieszczony w kontekście nie jest obiektem standardowym, dzielenie obiektu pomiędzy kontekstami staje się o wiele trudniejsze z powodu kwestii związanych z mechanizmem ładowania klas. Proszę pamiętać, że każda aplikacja WWW posiada swoje klasy w WEB-INF, ładowanym przez inny mechanizm ładowania klas. Proszę również pamiętać, że mechanizm ładowania klas jednej aplikacji nie umie zlokalizować klas umieszczonych w innej aplikacji. Nieszczęśliwy wynik — obiekt, którego plik .class istnieje jedynie wewnątrz WEB-INF/classess lub WEB-INF/lib nie może być w łatwy sposób wykorzystany przez żadną inną aplikację. Każda próba wywołania obiektu według jego właściciela daje wynik w postaci błędu NoClassDefFoundError.

Rozwiązanie w postaci skopiowania pliku .class do obu aplikacji również nie działa. Klasy ładowane przez różne aplikacje nie są tą samą klasą, nawet jeżeli ich definicje są identyczne. Próba wykonania tego daje w efekcie jedynie zamianę błędu NoClassDefFoundError na wyjątek ClassCastException.

Istnieją trzy możliwe sposoby obejścia tego problemu. Po pierwsze, skopiowanie pliku .class współdzielonego obiektu do standardowej ścieżki klas serwera (w przypadku serwera Tomcat jest to katalog_macierzysty_serwera/classes). Pozwala to na odnalezienie klasy przez podstawowy mechanizm ładowania klas współdzielony przez wszystkie aplikacje. Po drugie, uniknięcie wywoływania zwracanego obiektu i wywoływanie jego metod przy pomocy refleksji (techniki, w której klasa Javy może przeglądać i manipulować sobą w trakcie uruchomienia). Nie jest to tak eleganckie, ale pomaga, jeżeli nie posiada się pełnej kontroli nad serwerem. Po trzecie, wywołanie zwracanego obiektu do interfejsu, który deklaruje pożądane metody i umieszczenie interfejsu w standardowej ścieżce klas serwera. Każda klasa oprócz interfejsu może pozostać w pojedynczych aplikacjach WWW. Wymaga to przeprogramowania klasy dzielonego obiektu, ale pomaga w przypadkach, w których implementacja musi pozostać w swoich aplikacjach WWW.

Dzielenie kontroli

W celu bardziej dynamicznej współpracy, serwlety mogą dzielić kontrole żądania. Po pierwsze, serwlet może przekazać całe żądanie wykonując pewne wstępne przetwarzanie, po czym przekazać żądanie innemu elementowi. Po drugie, serwlet może dołączyć do swojej odpowiedzi pewną zawartość generowaną przez inny składnik, właściwie tworząc programowe dołączenie po stronie serwera. Patrząc pojęciowo, jeżeli myśli się o stronie wynikowej takiej, jak ekran, przekazanie daje innemu serwletowi pełną kontrole nad ekranem, podczas gdy dołączanie wyświetla jedynie część zawartości w pewnym miejscu ekranu.

Ta możliwość delegowania daje serwletom większą elastyczność i pozwala na lepsze rozdzielanie. Przy pomocy delegowania, serwlet może skonstruować swoją odpowiedź jako zbiór zawartości tworzonych przez różne składniki serwera WWW. Funkcjonalność ta jest szczególnie ważna dla JavaServer Pages, w której to technice często zdarza się, że jeden serwlet przetwarza żądanie, po czym przekazuje je stronie JSP w celu dokończenia zachowania spójności (proszę zobaczyć rozdział 18, JavaServer Pages).

Pobieranie dyspozytora żądań

W celu zapewnienia obsługi delegowania, Servlet API 2.1 wprowadził interfejs javax.servlet.RequestDispatcher. Serwlet pobiera egzemplarz RequestDispatcher wykonując metodę getRequestDispatcher() na pożądanym obiekcie. Metoda ta zwraca RequestDispatcher, który działa jako dyspozytor dla elementu (serwletu, JSP, pliku statycznego itp.) odnalezionego w podanej ścieżce URI:

public RequestDispatcher ServletRequest.getRequestDispatcher(String sciezka)

Przekazywana ścieżka może być ścieżką względną, chociaż nie powinna sięgać poza aktualny kontekst serwletu. Można wykorzystać opisaną w poprzednim podrozdziale metodę getContext() w celu sięgnięcia poza aktualny kontekst. Nie istnieje sposób rozciągnięcia dyspozycji na kontekst znajdujący się na innym serwerze. Jeżeli ścieżka rozpoczyna się od /, to jest interpretowana jako ścieżka względna wobec katalogu macierzystego aktualnego kontekstu, jeżeli ścieżka zawiera łańcuch zapytanie, parametry dodawane są na początku zbioru parametrów odbierającego elementu. Metoda zwraca null, jeżeli kontener serwletów nie jest w stanie zwrócić RequestDispatcher, niezależnie od powodu.

Co ciekawe, istnieje metoda o tej samej nazwie w klasie ServletContext:

public RequestDispatcher ServletContext.getRequestDispatcher(String sciezka)

Różnica pomiędzy tymi metodami polega na tym, że wersja znajdująca się w ServletContext (wprowadzona w API 2.1) przyjmuje jedynie URL-e absolutne (rozpoczynające się ukośnikiem), a wersja w ServletRequest (wprowadzona w API 2.2) akceptuje URL-e zarówno absolutne, jak i względne. W związku z tym nie ma powodu, aby stosować metodę znajdującą się w ServletContext. Istnieje ona jedynie z powodów historycznych i może być uważana za wyłączoną, choć decyzja o jej oficjalnym wyłączeniu jeszcze nie nastąpiła.

Możliwe jest również pobranie RequestDispatcher dla zasobu określonego przy pomocy nazwy, a nie ścieżki, przy pomocy metody getNamedDispatch(), znajdującej się w ServletContext:

public RequestDispatcher ServletContext.getNamedDispatcher(String nazwa)

Pozwala to na zarządzanie zasobami, które niekoniecznie są dostępne publicznie. Serwletom (a także stronom JSP) można nadawać nazwy poprzez deskryptor aplikacji WWW, jak opisano w rozdziale 3, „Cykl życia serwletów”. Metoda ta zwraca null, jeżeli kontekst nie może zwrócić dyspozytora, niezależnie od powodu.

RequestDispatcher posiada dwie metody, forward() i include(). Metoda forward() przekazuje całe żądanie innemu elementowi. Metoda include() dodaje wynik pracy innego elementu do odpowiedzi wywołującego serwletu, pozostawia jednak kontrolę w jego rękach.

Dyspozycja przekazaniem

Metoda forward() przekazuje żądanie od serwletu do innego zasobu na serwerze. Metoda pozwala jednemu serwletowi na wykonanie wstępnego przetwarzania żądania, a innemu elementowi na wygenerowanie odpowiedzi. Inaczej niż sendRedirect(), forward() działa jedynie wewnątrz serwera, a klient nie jest w stanie rozpoznać, że nastąpiło przekazanie. Informacja może zostać przekazana delegatowi przy pomocy dołączonego łańcucha zapytania lub przy pomocy atrybutów żądania ustawionych przy pomocy metody setAttribute(). Przykład 11.3 przedstawia serwlet wykonujący poszukiwanie, po czym przekierowujący jego wyniki innej stronie, która je wyświetla.

Przykład 11.3.

Wewnętrzny mechanizm wyszukiwarki

import java.io.*;

import javax.servlet.*;

import javax.servlet.http.*;

public class LogikaSzukaj extends HttpServlet {

public void doGet(HttpServletRequest zad, HttpServletResponse odp)

throws ServletException, IOException {

// Typ zawartości ani urządzenie wyświetlające nie jest określone

// Pobranie łańcucha do poszukiwań

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

// Utworzenie URL-i zawierających atrybut żądania

String[] wyniki = pobierzWyniki(szukaj);

// Określenie wyników jako atrybutu żądania

zad.setAttribute("wyniki", wyniki);

// Przekazanie do strony wyświetlającej

String wyswietl = "/servlet/wyswietlSzukaj";

RequestDispatcher dyspozytor = zad.getRequestDispatcher(wyswietl);

dyspozytor.forward(zad, odp);

}

// W prawdziwym zastosowaniu metoda ta wywoływałaby właściwą logikę wyszukiwarki

// i zwracałaby większą ilość informacji na temat każdego wyniku niż zwykły URL

String[]pobierzWyniki(String szukaj) {

return new String[] { "http://www.abc.com",

"http://www.xyz.com" };

}

}

Zadaniem tego serwletu jest bycie mózgiem wyszukiwarki online. Wykonuje on poszukiwania tekstu podanego w parametrze szukaj, po czym przechowuje wynikowe URL-e w atrybucie żądania wyniki. Następnie serwlet przekazuje żądanie do elementu wyświetlającego. Powyżej został on sztywno zakodowany jako /servlet/wyswietlSzukaj, ale ścieżka może być utworzona tak, aby mogła podlegać zmianom zależnie od preferencji użytkownika co do języka, kolorów witryny, tego, czy jest on użytkownikiem początkującym, czy zaawansowanym, itp.

Zasady, według których działać musi serwlet przekazujący, są stosunkowo rygorystyczne:

Element odbierający może zostać napisany tak, jak każdy inny element, jak przedstawia to przykład 11.4.

Przykład 11.4.

Fronton wyszukiwarki

import java.io.*;

import javax.servlet.*;

import javax.servlet.http.*;

public class WyswietlSzukaj extends HttpServlet {

public void doGet(HttpServletRequest zad, HttpServletResponse odp)

throws ServletException, IOException {

odp.setContentType("text/plain");

PrintWriter wyj = odp.getWriter();

// Pobranie wyników przeszukiwania z atrybutu żądania

String[] wyniki = (String[]) zad.getAttribute("wyniki");

if (wyniki == null) {

wyj.println("Brak wyników.");

wyj.println("Czy przypadkiem serwlet nie został wywołany bezpośrednio?");

}

else {

wyj.println("Wyniki:");

for (int i = 0; i < wyniki.length; i++) {

wyj.println(wyniki[i]);

}

}

wyj.println();

wyj.println("URI żądania: " + zad.getRequestURI());

wyj.println("Ścieżka kontekstu: " + zad.getContextPath());

wyj.println("Ścieżka serwletu: " + zad.getServletPath());

wyj.println("Informacje o ścieżce: " + zad.getPathInfo());

wyj.println("Łańcuch zapytania: " + zad.getQueryString());

}

}

Kiedy serwlet ten zostanie wypróbowany, można zauważyć, że uaktualniona została informacja o ścieżce. Nie jest ona taka, jak oryginalna informacja o żądaniu; zamiast tego pasuje do ścieżki wykorzystywanej do dotarcia do dyspozytora:

URI żądania: /servlet/WyswietlSzukaj

Ścieżka kontekstu:

Ścieżka serwletu:/ servlet/WyswietlSzukaj

Informacje o ścieżce: null

Łańcuch zapytania: null

Powód tego uaktualnienia jest taki, że jeżeli wykonane zostaje przekazanie serwletowi, serwlet ten powinien otrzymać tę samą informację o ścieżce, jaką otrzymałby, gdyby został wywołany bezpośrednio; od tej pory posiada on pełną kontrolę nad żądaniem. Jest to również powód, dla którego ważne jest, żeby bufor odpowiedzi został wyczyszczony przed jego wywołaniem i żaby odpowiedź nie została zatwierdzona.

Dyspozycja według nazwy

Jednym z problemów występujących przy dyspozycji według ścieżki jest fakt, że jeżeli element docelowy jest udostępniony elementom serwera pod pewną ścieżką, jest również pod tą samą ścieżką dostępny klientom. Dla bezpieczeństwa można rozważyć uczynienie komponentu niedostępnym publicznie (na przykład przez wyłączenie logiki wywołania /servlet) i dyspozycję żądań według nazwy zamiast według ścieżki. Umożliwia to metoda getNamedDispatcher(). Logika dyspozycji w przykładzie 11-3 może zostać zmieniona na następującą:

// Przekazanie do strony wyświetlającej

String wyswietl = "wyswietlSzukaj";

RequestDispatcher dyspozytor = zad.getNamedDispatcher(wyswietl);

dyspozytor.forward(zad, odp);

Przy korzystaniu z getNamedDispatcher() należy pamiętać o fakcie, że ponieważ nie jest wykorzystana ścieżka URI, nie mogą zostać dołączone żadne łańcuchy zapytania i nie występuje uaktualnianie ścieżki.

Przekazanie czy przekierowanie

Czy lepiej jest zastosować forward(), czy sendRedirect()? Obie metody posiadają swoje zastosowania. forward() działa najlepiej, kiedy jeden z elementów musi wykonać pewną logikę biznesową, po czym podzielić się wynikami z innym elementem, a sendRedirect() działa najlepiej wtedy, gdy klient powinien zostać przekierowany z jednej strony na inną. Kuszące jest zastosowanie forward() zamiast sendRedirect() w przypadku prostych przekierowań, ponieważ forward() pracuje wewnątrz serwera i działa szybciej od sendRedirect(), który wymaga bardzo okrężnej komunikacji z klientem. Niestety, powoduje to problemy ze względną obsługą URL-i, jak przedstawiono w przykładzie 11.5, serwlecie, który przekazuje żądanie do strony głównej witryny.

Przykład 11.5.

Bezpośrednia podróż do strony głównej

import java.io.*;

import javax.servlet.*;

import javax.servlet.http.*;

public class PrzekazStronaGlowna extends HttpServlet {

public void doGet(HttpServletRequest zad, HttpServletResponse odp)

throws ServletException, IOException {

RequestDispatcher dyspozytor = zad.getRequestDispatcher("/indeks.html");

dyspozytor.forward(zad, odp);

}

}

Jeżeli wykorzysta się serwer Tomcat w celu uzyskania dostępu do powyższego serwletu pod URL-em http://localhost:8080/servlet/PrzekazStronaGlowna, można zauważyć, że wszystkie obrazki na stronie są uszkodzone. Dzieje się tak dlatego, że strona ta zawiera znaczniki <IMG> z względnymi ścieżkami do plików obrazków. Podczas, gdy sendRedirect() powiadomiłaby klienta o tym, że źródłem pliku jest katalog macierzysty dokumentów serwera, forward() nie przekazuje tej wiadomości. W związku z tym nie działają żadne względne URL-e. Metoda sendRedirect() ułatwia również dyspozycję do zasobów w innych kontekstach, ponieważ nie jest w tym przypadku konieczne wyszukiwanie getContext(). Ogólnie poleca się wykorzystywane sendRedirect(), gdzie to tylko możliwe, a forward() tylko wtedy, gdy jest to konieczne.

Dyspozycja dołączania

Metoda include() RequestDispatcher dołącza zawartość zasobu do aktualnej odpowiedzi. Pozwala na coś, co mogłoby być nazwane programistycznym dołączaniem po stronie serwera. Różni się to od forward(), ponieważ wywołujący serwlet utrzymuje kontrolę nad odpowiedzią i może dołączyć zawartość zarówno przed, jak i po zawartości dołączonej. Przykład 11.16 przedstawia serwlet, który dołącza egzemplarz z katalogu do aktualnej odpowiedzi.

Przykład 11.6.

Dołączanie egzemplarza z katalogu

import java.io.*;

import javax.servlet.*;

import javax.servlet.http.*;

public class Ksiazki extends HttpServlet {

public void doGet(HttpServletRequest zad, HttpServletResponse odp)

throws ServletException, IOException {

odp.setContentType("text/html");

PrintWriter wyj = odp.getWriter();

wyj.println("<HTML><HEAD><TITLE>Witamy</TITLE></HEAD>");

wyj.println("<BODY>");

// Wyświetlenie egzemplarza z katalogu

wyj.println("Proszę nacieszyć się tym pięknem:");

RequestDispatcher dyspozytor =

zad.getRequestDispatcher("/servlet/Egzemplarz?egzemplarz=0596000405");

dyspozytor.include(zad, odp);

wyj.println("A ponieważ lubimy Cię, te książki są o 20% tańsze!");

wyj.println("</BODY></HTML>");

}

}

Podobnie jak w przypadku forward(), informacje mogą być przesyłane do wywołanego zasobu przy pomocy dołączonego łańcucha zapytania lub przy pomocy atrybutów żądania ustawionych za pomocą metody setAttribute(). Wykorzystanie atrybutów zamiast parametrów umożliwia przekazywanie obiektów zamiast prostych łańcuchów, jak przedstawiono w przykładzie 11.7.

Przykład 11.7.

Dołączanie kilku egzemplarzy z katalogu

import java.io.*;

import javax.servlet.*;

import javax.servlet.http.*;

public class Ksiazki extends HttpServlet {

public void doGet(HttpServletRequest zad, HttpServletResponse odp)

throws ServletException, IOException {

odp.setContentType("text/html");

PrintWriter wyj = odp.getWriter();

wyj.println("<HTML><HEAD><TITLE>Witamy</TITLE></HEAD>");

wyj.println("<BODY>");

// Wyświetlenie egzemplarza z katalogu

RequestDispatcher dyspozytor =

zad.getRequestDispatcher("/servlet/Egzemplarz");

wyj.println("Proszę nacieszyć się tym pięknem:");

zad.setAttribute("egzemplarz", Ksiazka.pobierzKsiazka("0596000405"));

dyspozytor.include(zad, odp);

// Usunięcie atrybutu „egzemplarz” po jego wykorzystaniu

zad.removeAttribute("egzemplarz");

wyj.println("A może ta:");

zad.setAttribute("egzemplarz", Ksiazka.pobierzKsiazka("0395282659"));

dyspozytor.include(zad, odp);

wyj.println("A ponieważ lubimy Cię, te książki są o 20% tańsze!");

wyj.println("</BODY></HTML>");

}

}

// prosta klasa Ksiazka

class Ksiazka {

String isbn;

String tytul;

String autor;

private static Ksiazka JSERWLET =

new Ksiazka("0596000405", "Java Serwlet — programowanie", "Hunter");

private static Ksiazka HOBBIT =

new Ksiazka("0395282659", "Hobbit", "Tolkien");

// Symulacja wyszukiwania w bazie danych

public static Ksiazka pobierzKsiazka(String isbn) {

if (JSERWLET.pobierzISBN().equals(isbn)) {

return JSERWLET;

}

else if (HOBBIT.pobierzISBN().equals(isbn)) {

return HOBBIT;

}

else {

return null;

}

}

private Ksiazka(String isbn, String tytul, String autor) {

this.isbn = isbn;

this.tytul = tytul;

this.autor = autor;

}

public String pobierzISBN() {

return isbn;

}

public String pobierzTytul() {

return tytul;

}

public String pobierzAutor() {

return autor;

}

}

W powyższym przykładzie zamiast wysyłać jedynie ISBN, serwlet przekazuje kompletny obiekt Ksiazka. W prawdziwym zastosowaniu, informacja o książce pochodziłaby z wyszukiwania ISBN w bazie danych. Powyżej para książek została po prostu sztywno zakodowana.

Serwlet Egzemplarz może otrzymywać atrybut żądania egzemplarz poprzez wywołanie zad.getAttribute("egzemplarz"), jak przedstawiono w przykładzie 11.8.

Przykład 11.8.

Wyświetlanie egzemplarza z katalogu.

import java.io.*;

import javax.servlet.*;

import javax.servlet.http.*;

public class Egzemplarz extends HttpServlet {

public void doGet(HttpServletRequest zad, HttpServletResponse odp)

throws ServletException, IOException {

// Typ zawartości nie jest ustawiany

PrintWriter wyj = odp.getWriter();

Ksiazka ksiazka = (Ksiazka) zad.getAttribute("egzemplarz");

wyj.println("<BR>");

if (ksiazka != null) {

wyj.println("<I>" + ksiazka.pobierzTytul() + "</I>");

wyj.println(" autorstwa " + ksiazka.pobierzAutor());

}

else {

wyj.println("<I>Nie znaleziono książki</I>");

}

wyj.println("<BR>");

}

}

Zawartość dołączonego serwletu może zostać umieszczona w dowolnym miejscu strony. W związku z tym nie posiada ona możliwości zmiany kodu stanu ani nagłówków HTTP wysłanych w odpowiedzi. Każda próba dokonania zmiany jest ignorowana.

Inaczej niż w przypadku forward(), elementy ścieżki i parametry żądania pozostają niezmienione w porównaniu z elementami serwletu wywołującego. Jeżeli dołączony składnik wymaga dostępu do elementów swojej własnej ścieżki, może on je odczytać przy pomocy następujących preasygnowanych atrybutów żądania:

javax.servlet.include.request_uri

javax.servlet.include.context_path

javax.servlet.include.servlet_path

javax.servlet.include.path_info

javax.servlet.include.query_string

Parametry żądania i odpowiedzi muszą być tymi samymi obiektami, które zostały przekazane metodzie usług serwletu wywołującego, a include() musi być wywołana wewnątrz tego samego wątku obsługującego.

Dołączane zasoby muszą wykorzystywać mechanizm wyjścia, pasujący do mechanizmu wywołującego. Jeżeli mechanizm wywołujący wykorzystuje PrintWriter, dołączane zasoby muszą również wykorzystywać PrintWriter. Jeżeli mechanizm wykorzystuje OutputStream, dołączane zasoby również muszą wykorzystywać OutputStream. Jeżeli mechanizmy nie pasują do siebie, dołączony serwlet powoduje wyjątek IllegalStateException. Jeżeli jest to możliwe, należy się upewnić, co do stosowania PrintWriter dla każdego wyświetlanego tekstu, a nie stanie się to problemem.

2 Część I Podstawy obsługi systemu WhizBang (Nagłówek strony)

2 C:\0-praca\Java Servlet - programowanie. Wyd. 2\r11-t.doc



Wyszukiwarka

Podobne podstrony:
r12-05, Programowanie, ! Java, Java Server Programming
r20-05, Programowanie, ! Java, Java Server Programming
O Autorach-05, Programowanie, ! Java, Java Server Programming
r05-05, Programowanie, ! Java, Java Server Programming
r07-05, Programowanie, ! Java, Java Server Programming
r03-05, Programowanie, ! Java, Java Server Programming
rE-05, Programowanie, ! Java, Java Server Programming
r19-05, Programowanie, ! Java, Java Server Programming
r17-05, Programowanie, ! Java, Java Server Programming
rD-05, Programowanie, ! Java, Java Server Programming
rF-05, Programowanie, ! Java, Java Server Programming
r04-05, Programowanie, ! Java, Java Server Programming
r13-05, Programowanie, ! Java, Java Server Programming
r01-05, Programowanie, ! Java, Java Server Programming
r10-05, Programowanie, ! Java, Java Server Programming
r02-05, Programowanie, ! Java, Java Server Programming
rB-05, Programowanie, ! Java, Java Server Programming
r12-05, Programowanie, ! Java, Java Server Programming
05 Programowanie

więcej podobnych podstron