Java
EE 6
Programowanie aplikacji WWW
Szybko i bez kïopotów poznaj Java Enterprise Edition
Naucz siÚ praktycznie tworzyÊ ciekawe aplikacje WWW
DoïÈcz do elity programistów nowoczesnych rozwiÈzañ webowych
È
Ú
Ú
ï
È
Ú
È
È
ĝ
Ă
È
Ú
ĝ
ĝ
ĂÊ
È ñ
Ú
È ĝ
Ă
ï
È
Ú
Ú
È
È
Ă
ĝ
Ă
Ú
È
È
Ú
Ă
ĝ
ĝ
Ê
ïÈ
È È
È
Ú
Ú
ï
Úĝ
Ú
ï
È
Ă
Ă
È
Ê Ă
Ú
È
ě
ï
ï
ï
ĂÊ
Ă
Ú Ú
ï
Èĝ
Ú
È
ĝ
ĂÊ
Ú È
ġ Ê
Ú
Ă
ï Ă
Ú
Ú
È
ġ È
ĝ
Ă
È
ï Ă
Ê
È
Ă
Ú
ÈÊ
Ú
Èĝ Ú
ĝ
ï
Ú
Ă
Krzysztof Rychlicki-Kicior
Juĝ dziĂ siÚgnġ po jedyne kompendium wiedzy na temat Java EE!
k
È
ĝ
Ú
Ú
ï
Java EE 6.
Programowanie
aplikacji WWW
Autor:
Krzysztof Rychlicki-Kicior
ISBN: 978-83-246-2659-5
Format: 158×235, stron: 232
• Szybko i bez k³opotów poznaj Java Enterprise Edition
• Naucz siê praktycznie tworzyæ ciekawe aplikacje WWW
• Do³¹cz do elity programistów nowoczesnych rozwi¹zañ webowych
Ju¿ dziœ siêgnij po jedyne kompendium wiedzy na temat Java EE!
Java Enterprise Edition to standard tworzenia aplikacji biznesowych wykorzystuj¹cych
jêzyk Java. Opracowany przez firmê Sun Microsystems, dzia³a w oparciu o wielowarstwow¹
architekturê komponentow¹, oferuj¹c programistom bardzo rozbudowane mo¿liwoœci
tworzenia oprogramowania funkcjonuj¹cego na niemal dowolnym sprzêcie, w ka¿dym
systemie operacyjnym, z wykorzystaniem licznych serwerów aplikacji. Du¿a popularnoœæ
rozwi¹zañ Java EE i coraz powszechniejszy dostêp do technologii WWW sprawiaj¹,
¿e programiœci sprawnie pos³uguj¹cy siê tego rodzaju narzêdziami rzadko figuruj¹ na
listach osób poszukuj¹cych pracy, a jeœli ju¿ jakimœ cudem siê na nich znajd¹, bardzo
szybko otrzymuj¹ atrakcyjne propozycje zatrudnienia. Nauka swobodnego poruszania
siê w tym œrodowisku mo¿e te¿ byæ wspania³¹, poszerzaj¹c¹ horyzonty przygod¹,
a gdy poznasz platformê Java EE, bêdziesz dysponowa³ potê¿nym narzêdziem,
u³atwiaj¹cym tworzenie nawet najbardziej skomplikowanych aplikacji internetowych
w bardzo efektywny i szybki sposób.
Studenci, programiœci i hobbyœci pragn¹cy poznaæ œrodowisko Java Enterprise Edition
czêsto napotykaj¹ problem ze znalezieniem solidnych Ÿróde³ wiedzy, które pozwoli³yby
im szybko i ³atwo wejœæ w œwiat tej coraz bardziej popularnej technologii. Lukê tê
z powodzeniem wype³nia ksi¹¿ka „Java EE 6. Programowanie aplikacji WWW”.
Dziêki niej wszyscy zainteresowani tematem zyskaj¹ mo¿liwoœæ poznania Java EE od
podstaw i zdobycia praktycznej wiedzy, na podstawie której bêd¹ mogli rozwijaæ swoje
umiejêtnoœci programistyczne w przysz³oœci. Ten podrêcznik pozwala na szybkie
rozpoczêcie przygody z tworzeniem aplikacji webowych, skutecznie wprowadzaj¹c
w zagadnienia wykorzystywanych przy tym platform i mechanizmów, lecz nie pomijaj¹c
te¿ informacji o charakterze ogólnym. Jeœli niewiele mówi¹ Ci skróty JSP, JPA, JSF
czy JPQL, a chcia³byœ zmieniæ ten stan rzeczy, bez w¹tpienia powinieneœ siêgn¹æ po tê
ksi¹¿kê, podobnie jak wszystkie osoby zainteresowane bezproblemowym u¿ywaniem
ca³ego spektrum nowoczesnych narzêdzi oferowanych przez œrodowisko Java EE.
• Tworzenie serwletów
• Zastosowanie szablonów JSP
• Integracja danych z aplikacjami za pomoc¹ mechanizmu JPA
• U¿ywanie interfejsów i komponentów
• Korzystanie z technologii JSF
• Uniwersalny i wygodny dostêp do danych, czyli jêzyk JPQL
• Praktyczne przyk³ady realizacji
Spraw, aby tworzenie aplikacji WWW z wykorzystaniem Java EE nie mia³o przed Tob¹ tajemnic
Spis tre
Ĉci
CzöĈè I
Podstawy ....................................................................... 7
Rozdziaä 1. Java EE — naprawdö krótkie wprowadzenie ...................................... 9
Web vs Enterprise ........................................................................................................... 10
Serwery aplikacji ............................................................................................................ 11
Streszczenie, czyli krótki przewodnik po niniejszej publikacji ...................................... 11
Serwlety — na dobry początek ................................................................................. 11
Deskryptor wdroĪenia .............................................................................................. 12
JSP — HTML + Java ............................................................................................... 13
JPA — czas na dane! ................................................................................................ 13
JSF — wyĪszy poziom prezentacji ........................................................................... 13
Facelets ..................................................................................................................... 14
Rozdziaä 2. Pierwsza aplikacja webowa ............................................................ 15
Integrowanie Tomcata z Netbeansem ............................................................................. 16
Pierwsza aplikacja .......................................................................................................... 17
Dodawanie nowych elementów ...................................................................................... 18
Pierwszy serwlet? ........................................................................................................... 20
Rozdziaä 3. Serwlet — na dobry poczñtek ......................................................... 25
ĩycie serwletu ................................................................................................................ 25
Serwlet pod lupą ............................................................................................................. 26
ĩądanie — odpowiedĨ .................................................................................................... 27
Przesyáanie odpowiedzi ............................................................................................ 29
Om nom nom, czyli ciasteczka w peánej krasie ........................................................ 31
Sesje — nie tylko dla studentów .............................................................................. 31
Konfiguracja w kodzie Javy — moĪna tego uniknąü ...................................................... 33
Parametry serwletów ................................................................................................ 34
Kontekst serwletów .................................................................................................. 35
Trzech muszkieterów? .................................................................................................... 36
Atrybuty a mnogoĞü ĪądaĔ ....................................................................................... 36
Sáuchowisko ................................................................................................................... 39
ServletContextListener ............................................................................................. 39
ServletContextAttributeListener ............................................................................... 39
ServletRequestAttributeListener i ServletRequestListener ....................................... 39
HttpSessionAtributteListener i HttpSessionListener ................................................ 40
4
Java EE 6. Programowanie aplikacji WWW
HttpSessionBindingListener ..................................................................................... 40
Sesja + wiele JVM = HttpSessionActivationListener ............................................... 40
Filtry ............................................................................................................................... 41
Techniczny aspekt filtrów ........................................................................................ 41
Konfiguracja filtrów w pliku web.xml ..................................................................... 42
Rozdziaä 4. JSP — gdy out.println() nie wystarcza ............................................. 45
Zacznijmy od początku, czyli JSP w Ğwiecie serwletów ................................................ 46
Pliki JSP dostĊpne bezpoĞrednio .............................................................................. 46
Pliki JSP wywoáywane z poziomu serwletów .......................................................... 46
Pochodzenie JSP — dziedzictwo serwletów .................................................................. 47
Pierwsze kroki w JSP ..................................................................................................... 47
Doceniü wygodĊ, czyli jak to lat temu kilka bywaáo… ............................................ 50
Expression Language — elegancja i wygoda ................................................................. 54
Remedium — warto byáo czekaü! ............................................................................ 55
DostĊp do obiektów w jĊzyku EL ............................................................................. 56
Beany, czyli ziarna — kult kawy wiecznie Īywy ..................................................... 57
Ziarna + EL = kolejne uáatwienie ............................................................................. 58
Ziarna, mapy i co dalej? ........................................................................................... 59
EL — nie tylko atrybuty ........................................................................................... 59
Akcje JSP ....................................................................................................................... 61
Include vs Forward — odsáona druga ....................................................................... 62
Akcje + ziarna = kolejne potĊĪne narzĊdzie ............................................................. 63
Dynamiczne generowanie elementów ...................................................................... 66
Rozdziaä 5. JSTL — wisienka na torcie JSP ....................................................... 69
Skrzynka z narzĊdziami .................................................................................................. 69
RdzeĔ .............................................................................................................................. 70
c:out .......................................................................................................................... 70
Ale to juĪ byáo, czyli c:set ........................................................................................ 72
Czwarty muszkieter .................................................................................................. 73
Kontrola sterowania ................................................................................................. 73
PĊtelka do kompletu ................................................................................................. 75
Wyjątki + JSP = … .................................................................................................. 76
Adresy URL — same káopoty ........................................................................................ 77
Adresy URL bez tajemnic ........................................................................................ 77
Tajemnica sesji… ..................................................................................................... 78
Trzech tenorów ............................................................................................................... 79
Na deser — funkcje! ....................................................................................................... 80
Przez kolekcje do serca ............................................................................................ 80
Funkcje áaĔcuchowe ................................................................................................. 81
Podsumowanie ................................................................................................................ 82
CzöĈè II Frameworki webowe ..................................................... 83
Rozdziaä 6. JavaServer Faces ........................................................................... 85
Frameworki — kolejny dowód na lenistwo czáowieka ................................................... 85
JSF — kanonu ciąg dalszy .............................................................................................. 86
JSF, czyli MVC w praktyce ...................................................................................... 87
Kontroler — uniwersalny spawacz ........................................................................... 88
Maáe zanurzenie .............................................................................................................. 88
Pierwsze przykáady .................................................................................................. 89
Aplikacja Notowania gieádowe ....................................................................................... 90
Tajemniczy zapis — # vs $ ...................................................................................... 95
Notowania historyczne, czyli kolekcja w kolekcji ................................................... 97
Spis treĈci
5
Najpierw szablon, póĨniej treĞü ................................................................................ 98
Klient szablonu ......................................................................................................... 99
Przygotowania… .................................................................................................... 100
Czas na obliczenia! ................................................................................................. 103
Maáy zastrzyk ......................................................................................................... 105
JSF — komponenty, komponenty, komponenty! ......................................................... 106
Output — (prawie) wszystko, czego do szczĊĞcia potrzeba ................................... 107
UIInput — teraz do szczĊĞcia nie brakuje juĪ nic ................................................... 108
Powrót do szarej rzeczywistoĞci… ......................................................................... 112
Zasady dziaáania JSF .................................................................................................... 115
Przykáadowa aplikacja — maszyna licząca ............................................................ 115
Przywrócenie widoku (1) ....................................................................................... 118
Pobranie danych z Īądania (2) ................................................................................ 119
Walidacja (3) .......................................................................................................... 119
Aktualizacja wartoĞci w modelu (ziarnach — 4) .................................................... 120
Wywoáanie zadeklarowanych uprzednio metod (5) ............................................... 120
Renderowanie odpowiedzi (6) ................................................................................ 120
Cykl Īycia w praktyce .................................................................................................. 120
Podsumowanie .............................................................................................................. 121
Rozdziaä 7. Konwertowanie i walidacja ........................................................... 123
Uroki transformacji ...................................................................................................... 123
Konwertery standardowe ........................................................................................ 124
Piszemy konwerter! ................................................................................................ 126
Walidator — nieodáączny partner konwertera .............................................................. 130
Walidatory — prawie jak konwertery .................................................................... 131
Walidacja niestandardowa — jak zawsze wiĊcej pracy .......................................... 132
CzöĈè III Obsäuga danych .......................................................... 135
Rozdziaä 8. JPA, czyli ORM + Java .................................................................. 137
DostĊp do danych w Javie ............................................................................................ 137
OĞwiecenie ............................................................................................................. 138
Pierwszy przykáad ........................................................................................................ 139
ZaáoĪenia ................................................................................................................ 139
Realizacja ............................................................................................................... 139
Tworzenie projektu ................................................................................................ 140
Hibernate a JPA — co i jak w ORM-owym Ğwiecie .............................................. 141
Pierwsza klasa encji ............................................................................................... 141
Jednostka utrwalania .............................................................................................. 145
Graficzna strona aplikacji ....................................................................................... 146
Dodawanie przychodni ........................................................................................... 150
EntityManager i spóáka… ...................................................................................... 152
MenedĪer encji — elegancki dostĊp != áatwa sprawa ............................................ 153
Nudni sáuchacze — nareszcie przydatni! ............................................................... 156
C juĪ jest, czas na RUD .......................................................................................... 158
Niewiele Ci mogĊ daü… (póki nie pozwolisz mi zaprezentowaü danych) ............. 158
Sáuchacz akcji vs akcja — starcie numer 2 ............................................................. 160
Istotny drobiazg — nasza aplikacja to niemowa! ................................................... 162
Rozdziaä 9. Zwiñzki miödzy encjami — jedna tabela to za maäo! ...................... 165
Przychodnia… i co dalej? ............................................................................................. 165
Związki miĊdzy tabelami — krótkie przypomnienie .............................................. 165
Związki SQL w praktyce ........................................................................................ 166
Jeden do wielu, wiele do jednego ........................................................................... 167
6
Java EE 6. Programowanie aplikacji WWW
Wiele do wielu — najwyĪszy stopieĔ wtajemniczenia ........................................... 167
Dodajemy tabele do bazy ....................................................................................... 168
Encje klas Javy — czas na związki! ............................................................................. 170
Encja Przychodnia — zmiana na lepszy model ...................................................... 171
Czas na nowoĞci! .................................................................................................... 172
Wizyta — encja JPA w peánej krasie ..................................................................... 178
CRUD dla lekarza — to juĪ byáo, ale nie do koĔca ...................................................... 183
Nowy lekarz — nowe pole, duĪa zmiana ............................................................... 184
Magikonwersja ....................................................................................................... 185
Ziarnko do ziarnka i zbierze siĊ aplikacja .............................................................. 186
Kolejne metody ziarna LekarzBean… .................................................................... 188
Na zakoĔczenie — edycja ...................................................................................... 189
Pacjenci — suplement ............................................................................................ 191
Danie gáówne: all in one, czyli wizyty! ........................................................................ 192
Od czegoĞ trzeba zacząü, czyli zmiany ................................................................... 193
Dodawanie wizyty .................................................................................................. 196
Ostatnie ziarno ....................................................................................................... 197
Edycja i usuwanie — powrót ................................................................................. 200
Koniec coraz bliĪej, czyli edycja w peánej krasie ................................................... 201
Podsumowanie .............................................................................................................. 202
Rozdziaä 10. JPQL i jego moĔliwoĈci ................................................................. 203
Prawie jak SQL… „prawie” robi róĪnicĊ ..................................................................... 203
Podstawy ...................................................................................................................... 204
Pobieranie z wariantami ......................................................................................... 204
JPQL a atrybuty záoĪone i null ............................................................................... 206
Nieco wiĊcej o SELECT ........................................................................................ 207
Funkcje obliczeniowe ............................................................................................. 208
Operacje niezwiązane z pobieraniem ..................................................................... 209
Mechanizmy zaawansowane ........................................................................................ 209
JOIN na lewo, JOIN na prawo… ............................................................................ 210
Grupowanie i sortowanie ........................................................................................ 211
Podzapytania — prawdziwa moc ........................................................................... 212
Podsumowanie .............................................................................................................. 213
Dodatki ..................................................................................... 215
Dodatek A Instalacja serwera Apache Tomcat ............................................... 217
Pobranie ........................................................................................................................ 217
Konfiguracja ................................................................................................................. 217
Dodatek B Bibliografia .................................................................................. 219
Skorowidz ....................................................................................................... 221
Rozdziaä 3.
Serwlet
— na dobry pocz
ñtek
Aplikacja z poprzedniego rozdziaáu wprowadziáa kilka istotnych elementów, których
omawianiem zajmiemy siĊ w przeciągu najbliĪszych trzech rozdziaáów. Rozpocznie-
my od podstawy podstaw, czyli elementu, który jest wykorzystywany poĞrednio lub
bezpoĞrednio we wszystkich aplikacjach webowych — mowa o serwlecie.
Serwlet, czyli klasa rozszerzająca moĪliwoĞci serwera aplikacji, moĪe byü traktowany
jako pojĊcie niesáychanie ogólne. Praktycznie jedynym istotnym wymaganiem sta-
wianym serwletom jest dziaáanie w trybie Īądanie — odpowiedĨ — serwlet powinien
generowaü treĞü odpowiedzi w oparciu o to, co otrzyma w Īądaniu. W poprzednim
rozdziale spotkaáeĞ siĊ z jednym z typowych zastosowaĔ serwletów — generowaniem
kodu HTML. Nie jest to jednak w Īadnym razie kres ich moĪliwoĞci — nic nie stoi
na przeszkodzie, aby za pomocą serwletów generowaü zarówno dane tekstowe (np.
w formacie XML), jak i dane binarne (np. pliki wykonywalne, obrazy, etc.). Zanim
jednak zabierzemy siĊ za praktyczne przykáady (pierwszy z nich mogáeĞ przeanali-
zowaü w poprzednim rozdziale), konieczne jest krótkie wprowadzenie teoretyczne,
w którym dowiesz siĊ, jak serwlet wspóápracuje z serwerem aplikacji, a takĪe jakie
podstawowe opcje związane z serwletami moĪna ustawiü w pliku web.xml.
ēycie serwletu
Gdy uruchamiasz zwykáą aplikacjĊ, graficzną lub konsolową, w swoim systemie ope-
racyjnym, moĪesz w wiĊkszoĞci przypadków okreĞliü precyzyjnie, kiedy rozpoczyna
siĊ, a kiedy koĔczy jej dziaáanie. W przypadku popularnych technologii dynamicznych
stron internetowych (np. PHP) pliki są interpretowane na bieĪąco (aczkolwiek istnieje
moĪliwoĞü ich poĞredniej kompilacji). Jak moĪna opisaü cykl Īycia serwletu?
Zacznijmy od klasy w takiej postaci, jaką juĪ znamy — nieskompilowanego kodu Ĩró-
dáowego. Zanim serwer zostanie uruchomiony, wszystkie pliki klas muszą zostaü pod-
dane kompilacji. Powstaáe pliki (o rozszerzeniu .class) są kopiowane do odpowiednich
26
CzöĈè I i Podstawy
katalogów. Dopiero wtedy serwer moĪe byü uruchomiony, na nowo lub ponownie. Na
szczĊĞcie w nowszych wersjach serwerów aplikacji (np. Apache Tomcat 6) istnieje
moĪliwoĞü automatycznego wykrywania i aktualizacji klas w trakcie dziaáania serwera.
Gdy uruchamiasz serwer aplikacji, z punktu widzenia naszego serwletu nie dzieje siĊ
nic istotnego. NastĊpuje wtedy, rzecz jasna, inicjalizacja samego serwera, a takĪe nie-
których ustawieĔ caáej aplikacji webowej. Sam serwlet pozostaje jednak nienaruszo-
ny. Caáa zabawa zaczyna siĊ, gdy dowolny uĪytkownik Twojej aplikacji po raz pierw-
szy spróbuje z niego skorzystaü. Serwer wykonuje wtedy nastĊpujące czynnoĞci:
zaáadowanie klasy serwletu,
utworzenie instancji serwletu,
wywoáanie metody
init()
,
wywoáanie metody
service()
.
Gdy serwlet znajdzie siĊ w trakcie wywoáania metody
service()
, moĪe on rozpocząü
normalną obsáugĊ ĪądaĔ. Od tego momentu w przypadku otrzymania przezeĔ dowol-
nego Īądania HTTP, nastąpi próba wywoáania odpowiedniej metody serwletu, wedáug
schematu
nazwa
/
doNazwa()
, np.
GET
/
doGet()
,
POST
/
doPost()
, itd.
Sporo pracy, nieprawdaĪ? Na szczĊĞcie do obowiązków programisty naleĪy obsáuga
wybranych metod ze sáowem
do
w nazwie. JeĞli wiĊc chcesz, aby serwlet obsáugiwaá
tylko Īądanie
GET
, zadeklaruj jedynie metodĊ
doGet()
.
W przypadku klasy serwletu utworzonej przez Netbeans, proces tworzenia serwletu
zostaá uproszczony jeszcze bardziej. Twórcy szablonu zaáoĪyli (skądinąd sáusznie), Īe
znamienita wiĊkszoĞü programistów korzysta jedynie z metod HTTP
GET
i
POST
. Z tego
wzglĊdu w klasie serwletu są przesáaniane dwie metody —
doGet()
i
doPost()
, które
odwoáują siĊ do jednej i tej samej metody — o nazwie
processRequest()
. Z jednej
strony uáatwia to Īycie w wiĊkszoĞci sytuacji, z drugiej jednak mogą siĊ zdarzyü sytu-
acje, w których inaczej chcemy zareagowaü w przypadku Īądania
GET
, a inaczej w przy-
padku
POST
. W takiej sytuacji naleĪy usunąü wygenerowany mechanizm i napisaü wáa-
sne metody obsáugi
doGet()
i/lub
doPost()
.
Serwlet pod lup
ñ
Przed chwilą poznaáeĞ przepáyw sterowania w serwlecie; najwyĪsza pora, abyĞ zapo-
znaá siĊ pokrótce z kluczowymi klasami powiązanymi z obsáugą serwletów. OmówiĊ
jedynie najwaĪniejsze elementy; warto je zapamiĊtaü, poniewaĪ bĊdą siĊ one pojawiaü
takĪe w dalszych przykáadach, ilustrujących kolejne omawiane technologie.
Jak juĪ wspomniaáem, serwlety, którymi zajmujemy siĊ w niniejszej ksiąĪce, dziedzi-
czą po klasie
HttpServlet
. Ze wzglĊdu na fakt, Īe serwlet z zaáoĪenia jest konstrukcją
niezwykle uniwersalną, w hierarchii dziedziczenia pojawiają siĊ dodatkowe elementy,
które ową uniwersalnoĞü wprowadzają. Oto krótki opis elementów hierarchii dziedzi-
czenia, począwszy od tych najbardziej ogólnych:
Rozdziaä 3. i Serwlet — na dobry poczñtek
27
Interfejs
Servlet
— okreĞla najwaĪniejsze metody, które muszą
implementowaü wszystkie serwlety. Metody te są niezaleĪne od stosowanych
protokoáów przesyáania danych, a dotyczą one gáównie zarządzania cyklem
Īycia serwletu (
init()
,
service()
,
destroy()
).
Abstrakcyjna klasa
GenericServlet
— podstawowa implementacja
interfejsów
Servlet
i
ServletConfig
, dająca dostĊp do parametrów i ustawieĔ
serwletu. Klasa ta zawiera proste implementacje metod obu interfejsów,
dziĊki czemu stanowi podstawĊ dla klasy
HttpServlet
i wszystkich innych
klas serwletów.
Klasa
HttpServlet
— to wáaĞnie po tej klasie bĊdziesz dziedziczyá, tworząc
wáasne serwlety. Poza wáasnymi implementacjami metod ze wspomnianych
wczeĞniej interfejsów, klasa
HttpServlet
udostĊpnia metody
do*
, czyli
doGet()
,
doPost()
etc. DziĊki temu we wáasnych serwletach musisz
zdefiniowaü jedynie te metody, które Twój serwlet zamierza obsáugiwaü.
Protokóá HTTP zawiera definicje oĞmiu metod:
GET
,
POST
,
PUT
,
HEAD
,
OPTIONS
,
TRACE
,
DELETE
,
CONNECT
. Serwlety mogą obsáugiwaü wszystkie metody, na wyĪej omówionej
zasadzie. W praktyce zdecydowanie najczĊĞciej stosuje siĊ metody
GET
i
POST
i to na
nich skupimy siĊ w dalszej czĊĞci tego rozdziaáu.
ēñdanie — odpowiedĒ
Mimo niewątpliwie istotnej roli klasy
HttpServlet
, w trakcie pracy z serwletami czĊ-
Ğciej przyjdzie Ci zapewne korzystaü z interfejsów
HttpServletRequest
/
HttpServlet
´Response
. Reprezentują one odpowiednio obiekty Īądania i odpowiedzi, przekazy-
wane do metod
doGet()
,
doPost()
etc. Peány nagáówek metody
doGet()
wygląda na-
stĊpująco:
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, java.io.IOException
Twoim zadaniem jest takie zdefiniowanie metod
do*
, aby generowaáy one odpowiedĨ,
zazwyczaj zaleĪną od przesáanych parametrów. Wszystkie niezbĊdne metody znajdziesz
w dwóch wyĪej wspomnianych klasach. Zacznijmy od interfejsu
HttpServletRequest
— to na jego podstawie bĊdziemy w kolejnych przykáadach generowaü odpowiedzi
przesyáane za pomocą interfejsu
HttpServletResponse
.
Niemal wszystkie metody
HttpServletRequest
są faktycznie przydatne, niemniej w tym
miejscu omówimy metody najistotniejsze z punktu widzenia samych serwletów:
Object getParameter(String nazwa)
— pobiera parametr o danej nazwie
przesáany w Īądaniu.
Enumeration<String> getParameterNames()
— pobiera nazwy wszystkich
parametrów znajdujących siĊ w danym Īądaniu.
String getRemoteUser()
— zwraca login uwierzytelnionego uĪytkownika
lub
null
, w przypadku braku uwierzytelnienia.
28
CzöĈè I i Podstawy
Cookie[] getCookies()
— zwraca tablicĊ ciasteczek — specjalnych plików
przechowywanych na komputerze uĪytkownika.
String getQueryString()
— zwraca áaĔcuch parametrów, przesáanych
w adresie URL (za znakiem zapytania).
String getHeader(String nazwa)
— zwraca wartoĞü nagáówka HTTP
o podanej nazwie.
int getIntHeader(String nazwa)
— zwraca wartoĞü nagáówka HTTP
o podanej nazwie jako liczbĊ caákowitą.
long getDateHeader(String nazwa)
— zwraca wartoĞü nagáówka HTTP
o podanej nazwie jako liczbĊ milisekund, począwszy od początku epoki
(1 stycznia 1970 roku). WartoĞü ta moĪe byü przekazana w konstruktorze
klasy
Date
.
String getContextPath()
— zwraca ĞcieĪkĊ kontekstu aplikacji.
String getServletPath()
— zwraca ĞcieĪkĊ dostĊpu do serwletu.
String getPathInfo()
— zwraca dodatkowe informacje zawarte w ĞcieĪce.
Trzy ostatnie metody są ze sobą związane, poniewaĪ zwracają one kolejne elementy
adresu URL, wykorzystanego do wykonania Īądania. Przeanalizujmy poniĪszy przy-
káad, prezentujący wiadomoĞü o okreĞlonym identyfikatorze:
http://localhost:8080/MojaAplikacja/serwlety/info/235/
Pomijamy oczywiĞcie nazwĊ protokoáu (http) i nazwĊ serwera z portem (localhost:8080).
Zostaje nam wiĊc ciąg:
/MojaAplikacja/serwlety/info/235/
Metoda getContextPath() zwraca fragment adresu okreħlajæcy naszæ aplikacjú:
/MojaAplikacja
ĝcieĪka do kontekstu zawsze zaczyna siĊ od ukoĞnika (ale nigdy na nim siĊ nie koĔ-
czy!), chyba Īe aplikacja zostanie umieszczona w katalogu gáównym serwera — wte-
dy zwracana wartoĞü to áaĔcuch pusty. Fragment ten jest wspólny dla wszystkich pli-
ków wchodzących w skáad tej aplikacji. Kolejny fragment adresu okreĞla ĞcieĪkĊ do
serwletu. W naszym przypadku jest to fragment:
/serwlety/info
PowyĪszy áaĔcuch znaków musi pasowaü do odpowiednich wzorców, zdefiniowanych
w deskryptorze wdroĪenia (pamiĊtasz znacznik
<url-pattern>
z poprzedniego roz-
dziaáu?). Zasady okreĞlania odpowiednich ĞcieĪek do serwletów omówimy w nastĊp-
nym rozdziale; na razie wystarczy Ci informacja, Īe ten fragment adresu umoĪliwia
jednoznaczne zidentyfikowanie serwletu.
Ostatni fragment ĞcieĪki (
/235/
) zostanie zwrócony przez metodĊ
getPathInfo()
. Do-
káadnie rzecz biorąc, metoda
getPathInfo()
zwraca fragment adresu URL od ĞcieĪki
serwletu do początku áaĔcucha parametrów (czyli do znaku zapytania). Oznacza to,
Īe nawet doáączenie parametrów, tak jak w poniĪszym przykáadzie, nie zmieni warto-
Ğci ĞcieĪki.
http://localhost:8080/MojaAplikacja/serwlety/info/235?param=1
Rozdziaä 3. i Serwlet — na dobry poczñtek
29
Przesyäanie odpowiedzi
Po przeanalizowaniu wszystkich moĪliwych atrybutów Īądania musisz odesáaü klien-
towi odpowiedĨ. Do tego celu sáuĪy obiekt interfejsu
HttpServletResponse
. W jego
przypadku otrzymujemy nieco mniejszy zestaw metod, jednak nie oznacza to wcale
mniejszych moĪliwoĞci. Przede wszystkim musimy okreĞliü, jakie operacje chcemy
wykonywaü w związku z przesyáaniem odpowiedzi do klienta:
przesáanie odpowiedzi w postaci danych tekstowych lub binarnych,
utworzenie i przesáanie ciasteczek,
dodanie do odpowiedzi dowolnych nagáówków,
przekierowanie Īądania lub przesáanie kodu báĊdu.
Transmisja danych
ChociaĪ technologie internetowe mają swoją specyfikĊ, nie zapominajmy, Īe Īyjemy
w Ğwiecie Javy. Z tego wzglĊdu operacje zarówno odczytu, jak i zapisu wiąĪą siĊ
z wykorzystaniem strumieni i/lub obiektów klas
Reader
/
Writer
. Nie inaczej jest w tym
przypadku: zanim przeĞlemy jakiekolwiek dane, musimy uzyskaü odpowiednie obiek-
ty zapisujące:
ServletOutputStream getOutputStream()
— zwraca strumieĔ zapisu
dla danych binarnych
PrintWriter getWriter()
— zwraca obiekt zapisujący dla danych tekstowych.
W przypadku danych binarnych moĪemy skorzystaü z obiektu klasy
ServletOutput
´Stream
. Jest to zwykáy strumieĔ zapisu, rozszerzony o moĪliwoĞü zapisywania do-
wolnych danych typów prymitywnych, a takĪe áaĔcuchów znaków (za pomocą metod
print()
i
println()
). Z tej klasy naleĪy korzystaü w przypadku przesyáania plików —
jeĞli serwer musi w dynamiczny sposób wygenerowaü treĞü takiego pliku.
Znacznie czĊĞciej przyjdzie Ci jednak korzystaü z danych tekstowych. W tym przypad-
ku zazwyczaj bĊdziesz korzystaü z obiektu klasy
PrintWriter
i jego metody
println()
.
Nagäówki i ciasteczka
O ile w przypadku Īądania mamy do czynienia z odczytem nagáówków i ciasteczek
przesáanych przez klienta, o tyle w przypadku odpowiedzi wystĊpuje proces odwrot-
ny. Aby dodaü ciasteczko, wystarczy skorzystaü z metody
addCookie()
:
void addCookie(Cookie c)
WiĊcej na temat ciasteczek w osobnym podrozdziale. W przypadku nagáówków sytu-
acja jest nieco bardziej skomplikowana — do dyspozycji mamy dwie metody (wraz
z odpowiednikami dla liczb i dat):
void addHeader(String nazwa, String wartoħè)
void setHeader(String nazwa, String wartoħè)
30
CzöĈè I i Podstawy
Na czym polega róĪnica? OtóĪ metoda
addHeader()
doda podaną wartoĞü do juĪ istnie-
jącej zawartoĞci nagáówka, natomiast metoda
setHeader()
zastąpi wartoĞü, jeĞli tako-
wa juĪ istnieje. Tak samo dziaáają bliĨniacze metody
addIntHeader()
,
addDateHeader()
,
setIntHeader()
i
setDateHeader()
.
Kody odpowiedzi, bäödy i przekierowania
Do obowiązków odpowiedzi HTTP naleĪy takĪe przekazywanie kodów odpowiedzi,
jeĞli chcemy zaznaczyü, Īe odpowiedĨ nie zostanie zakoĔczona w zwykáy sposób. Aby
przekazaü kod odpowiedzi, korzystamy z metody
setStatus()
:
void setStatus(int kod)
W ten sposób przekazujemy kody, które nie okreĞlają sytuacji problematycznych.
W przypadku báĊdów (np. 404 — brak zasobu) zaleca siĊ zdecydowanie wykorzysty-
wanie metody
sendError()
:
void sendError(int kod)
void sendError(int kod, String komunikat)
Jak widaü, istnieje moĪliwoĞü przesáania dodatkowej informacji na temat samego báĊ-
du. Ostatnią funkcjonalnoĞü związaną z kodami odpowiedzi stanowi przekierowanie.
ChociaĪ z technicznego punktu widzenia przekierowanie jest teĪ rodzajem kodu od-
powiedzi, do przekierowania wykorzystuje siĊ oddzielną metodĊ:
void sendRedirect(String adres)
Korzystając z metod
sendError()
i
sendRedirect()
, naleĪy pamiĊtaü o subtelnych
kwestiach związanych z fizycznym przesyáaniem danych do klienta. Przesyáanie ko-
munikatów o báĊdach lub przekierowaĔ wiąĪe siĊ z doĞü brutalną ingerencją w proces
przesyáania odpowiedzi. Proces ten jest natychmiast przerywany, a klient otrzymuje
odpowiedĨ z wybranym kodem odpowiedzi. Co jednak stanie siĊ, gdy zdąĪymy wy-
sáaü do klienta jakieĞ dane?
OdpowiedĨ jest prosta — nastąpi báąd. Po wysáaniu danych nie moĪesz ingerowaü
w treĞü nagáówków, przez co nie moĪesz ustawiü kodu odpowiedzi, a co za tym idzie
takĪe przekierowania. Czy oznacza to, Īe musisz uwaĪaü, gdzie wywoáujesz metodĊ
println()
obiektu
PrintWriter
? Na szczĊĞcie nie do koĔca.
DomyĞlnym zachowaniem obiektu w przypadku odpowiedzi jest zapisywanie danych
do bufora. Oznacza to, Īe dane zostaną wysáane po zakoĔczeniu metody lub w przy-
padku wywoáania metody
flush()
tego obiektu. Co za tym idzie, poniĪsza konstruk-
cja (wewnątrz metody
doGet()
) nie spowoduje wygenerowania báĊdu:
PrintWriter out = response.getWriter();
out.println("test");
response.sendRedirect("url/do/innego/serwletu");
JeĞli przed wywoáaniem metody
sendRedirect()
wywoáasz metodĊ
out.flush()
, wte-
dy báąd nastąpi. Zazwyczaj jednak takie wywoáanie jest pomijane, dziĊki czemu pro-
blem wystĊpuje stosunkowo rzadko.
Rozdziaä 3. i Serwlet — na dobry poczñtek
31
Om nom nom, czyli ciasteczka w peänej krasie
Twórcy aplikacji webowych, podobnie jak Ciasteczkowy Potwór, mają szczególny
sentyment do ciasteczek (ang. cookies). Są to niewielkie pliki przechowywane na kom-
puterach uĪytkowników aplikacji webowych, dziĊki czemu jesteĞmy w stanie zapamiĊ-
tywaü ich preferencje, loginy i hasáa itd. Metody operujące na ciasteczkach poznaliĞmy
w poprzednim podrozdziale, ale teraz zaprezentujemy ich dziaáanie w praktycznym
przykáadzie (listing 3.1):
Listing 3.1. Przykáad obsáugi ciasteczek
protected void processRequest(HttpServletRequest request, HttpServletResponse
response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
try {
Cookie lastVisit = null;
for (Cookie c : request.getCookies())
if (c.getName().equals("obecnosc")) {
lastVisit = c;
break;
}
if (lastVisit != null)
out.println("Twoja ostatnia wizyta na stronie miađa miejsce w dniu " +
lastVisit.getValue());
else
out.println("Do tej pory nie odwiedziđeħ/aħ naszej strony. Wstydļ siú!");
lastVisit = new Cookie("obecnosc", new Date().toString());
response.addCookie(lastVisit);
} finally {
out.close();
}
}
Zadaniem powyĪszego serwletu jest przechowywanie informacji o dacie ostatniej wi-
zyty na stronie i wyĞwietlanie jej. W przypadku braku ciasteczka z datą (co jest równo-
znaczne z pierwszymi odwiedzinami na tej stronie, przynajmniej od czasu wyczysz-
czenia ciasteczek w przeglądarce) wyĞwietlamy inną informacjĊ. Warto zwróciü uwagĊ
na dwie kwestie. Po pierwsze, jeĞli chcemy odczytaü juĪ istniejące ciasteczka — ko-
rzystamy z metody
getCookies()
znajdującej siĊ w obiekcie
request
. JeĞli chcemy
dodaü ciasteczko — korzystamy z obiektu
response
. Nigdy odwrotnie! Sprawa druga,
znacznie bardziej przykra — powyĪszy sposób dostĊpu do ciasteczek uĪytkownika
(pĊtla
for..in
) stanowi jedyną metodĊ znajdywania ciasteczek o okreĞlonej nazwie.
W przypadku tworzenia prawdziwych aplikacji trzeba zdefiniowaü osobną metodĊ do
wyszukiwania ciasteczek.
Sesje — nie tylko dla studentów
Obsáuga sesji jest kolejnym nieodáącznym elementem niemal wszystkich aplikacji we-
bowych. W przypadku JEE interakcje z sesją moĪemy prowadziü na róĪne sposoby,
32
CzöĈè I i Podstawy
takĪe za pomocą poznanych juĪ klas. W tym rozdziale poznamy sposób na dostĊp do
sesji za pomocą obiektu klasy
HttpServletRequest
. Kluczową rolĊ odgrywa metoda
getSession()
, wystĊpująca w dwóch wariantach:
HttpSession getSession()
HttpSession getSession(boolean czyTworzyc)
Na wstĊpie zaznaczĊ, Īe pierwszy wariant tej metody jest równowaĪny drugiemu wy-
woáanemu z parametrem
true
. Drugi wariant postĊpuje róĪnie w zaleĪnoĞci od prze-
kazanej wartoĞci logicznej:
JeĞli parametr ma wartoĞü
true
, metoda zwraca obiekt sesji lub tworzy nowy,
jeĞli ten nie istnieje.
JeĞli parametr ma wartoĞü
false
, metoda zwraca obiekt sesji lub
null
, jeĞli ten
nie istnieje.
Jak widaü, wartoĞü
true
naleĪy przekazaü, jeĞli chcesz po prostu uzyskaü dostĊp do
sesji. WartoĞü
false
stosuje siĊ, gdy chcesz sprawdziü, czy sesja istnieje. MoĪna sko-
rzystaü z tego mechanizmu, aby sprawdziü, czy dane Īądanie jest pierwszym Īądaniem
uĪytkownika w danej sesji. Mechanizm ten jest realizowany w poniĪszym przykáadzie
z listingu 3.2:
Listing 3.2. Przykáad wykorzystania sesji
protected void processRequest(HttpServletRequest request, HttpServletResponse
response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
try {
if (request.getSession(false)==null)
{
out.println("Witaj na stronie po raz pierwszy!");
request.getSession();
}
else
out.println("Witaj na stronie po raz kolejny!");
} finally {
out.close();
}
}
JeĞli po utworzeniu sesji chcesz sprawdziü, czy sesja zostaáa dopiero co utworzona,
skorzystaj z metody
isNew()
:
boolean isNew()
— zwraca
true
, jeĞli obiekt sesji zostaá utworzony podczas
tego Īądania.
Korzystanie z obiektu sesji
Podstawowa funkcjonalnoĞü obiektu sesji sprowadza siĊ do dwóch metod:
Object getAttribute(String nazwa)
— zwraca atrybut sesji o podanej nazwie.
Rozdziaä 3. i Serwlet — na dobry poczñtek
33
void setAttribute(String nazwa, Object wartoħè)
— dodaje obiekt
do sesji, przypisując mu podany klucz (nazwĊ). JeĞli jakiĞ obiekt o takiej
samej nazwie juĪ istniaá, zostanie on zastąpiony.
Wiemy juĪ, jak utworzyü sesjĊ, wiemy teĪ, jak z niej skorzystaü. Pozostaáo nam omó-
wienie, jakie są warunki zakoĔczenia sesji. MoĪe ono nastąpiü w wyniku kilku róĪnych
sytuacji:
rĊczne zakoĔczenie sesji przez programistĊ,
upáyniĊcie czasu Īycia sesji,
zamkniĊcie okna przeglądarki przez uĪytkownika.
Ostatni przypadek, jest rzecz jasna, najprostszy — nie wymaga on naszej ingerencji.
RĊczne zakoĔczenie sesji wiąĪe siĊ z wywoáaniem nastĊpującej metody:
void invalidate()
— koĔczy sesjĊ.
Najciekawiej sytuacja wygląda w przypadku okreĞlania terminu waĪnoĞci sesji. Istnie-
ją bowiem dwie moĪliwoĞci okreĞlenia tej wartoĞci — pierwsza z nich jest stosowana
w pliku konfiguracyjnym web.xml:
<web-app>
<session-config>
<session-timeout>10</session-timeout>
</session-config>
</web-app>
Podana wartoĞü okreĞla czas waĪnoĞci sesji w minutach. Obowiązuje on dla wszyst-
kich sesji, chyba Īe skorzystasz z moĪliwoĞci okreĞlenia czasu Īycia sesji w kodzie:
void setMaxInactiveInterval(int czas)
— okreĞla czas Īycia sesji
w sekundach. Podanie wartoĞci 0 i ujemnych powoduje, Īe sesja nigdy
nie wygasa (do jej zakoĔczenia jest wiĊc konieczne wywoáanie metody
invalidate()
lub zamkniĊcie okna przeglądarki przez uĪytkownika).
Konfiguracja w kodzie Javy
— mo
Ĕna tego uniknñè
Podczas tworzenia wiĊkszoĞci aplikacji programiĞci muszą zmierzyü siĊ z problemem
obsáugi róĪnego rodzaju ustawieĔ wpáywających na dziaáanie aplikacji. Problemem
staje siĊ lokalizacja tych ustawieĔ. Z jednej strony nikt nie chce utrudniaü sobie Īycia
— w koĔcu nie ma nic prostszego, niĪ wczytaü wartoĞü umieszczoną w staáej/zmiennej.
Z drugiej jednak strony zmiana takich ustawieĔ wymagaáaby rekompilacji caáego pro-
jektu, w najlepszym przypadku — jednej biblioteki.
Z tego wzglĊdu powszechnym standardem staáo siĊ umieszczanie róĪnego rodzaju
ustawieĔ w zewnĊtrznych Ĩródáach danych — plikach binarnych, tekstowych, XML;
34
CzöĈè I i Podstawy
rzadziej w bazach danych. W przypadku aplikacji webowych JEE miejscem takim
jest deskryptor wdroĪenia — plik web.xml. Poza licznymi ustawieniami związanymi
z funkcjonowaniem aplikacji jako takiej (czĊĞü z nich juĪ poznaáeĞ), w pliku web.xml
moĪesz takĪe zdefiniowaü parametry dla poszczególnych serwletów, a takĪe caáej apli-
kacji webowej.
Parametry serwletów
Parametry serwletów moĪesz okreĞlaü za pomocą znacznika
<init-param>
w nastĊpu-
jący sposób:
<servlet>
<servlet-name>ParameterServlet</servlet-name>
<servlet-class>pl.helion.jeeweb.ParameterServlet</servlet-class>
<init-param>
<param-name>autor</param-name>
<param-value>Krzysztof Rychlicki-Kicior</param-value>
</init-param>
</servlet>
Po dwóch znanych juĪ znacznikach (
servlet-name
i
servlet-class
) nastĊpuje dowol-
na liczba znaczników
init-param
. KaĪdy taki znacznik zawiera dwa kolejne, okreĞla-
jące nazwĊ i wartoĞü parametru. Parametry serwletów moĪna teĪ dodawaü w Ğrodowi-
sku Netbeans, podczas tworzenia serwletu (w ostatnim kroku kreatora).
Pierwszy parametr utworzony, najwyĪsza pora, aby odczytaü go we wnĊtrzu serwletu.
Do zarządzania parametrami serwletów sáuĪy interfejs
ServletConfig
, który jest im-
plementowany przez znane nam klasy
GenericServlet
i
HttpServlet
. Dwie metody
tego interfejsu, które interesują nas w tej chwili najbardziej, to:
String getInitParameter(String nazwa)
— zwraca wartoĞü parametru
o podanej nazwie.
String[] getInitParameterNames()
— zwraca wszystkie nazwy parametrów
danego serwletu.
protected void processRequest(HttpServletRequest request, HttpServletResponse
response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
try {
out.println("Autorem serwletu jest " + this.getInitParameter("autor"));
} finally {
out.close();
}
}
DziĊki umieszczeniu konfiguracji w pliku XML odnieĞliĞmy wymierną korzyĞü. Zmia-
na wartoĞci w pliku XML nie wymaga rekompilacji kodów Ĩródáowych, a jedynie prze-
áadowania aplikacji (w przypadku Tomcata istnieje takĪe opcja automatycznego wy-
krywania zmian i przeáadowywania aplikacji).
Rozdziaä 3. i Serwlet — na dobry poczñtek
35
Interfejs
ServletConfig
poza dwoma poznanymi metodami udostĊpnia metodĊ
get
´ServletName()
, zwracającą nazwĊ serwletu, a takĪe metodĊ
getServletContext()
.
Zwraca ona (a jakĪeby inaczej) kontekst serwletów — jeden z najwaĪniejszych obiek-
tów w caáym Ğwiecie aplikacji webowych JEE.
Kontekst serwletów
Kontekst serwletów to obiekt, który sáuĪy do komunikacji serwletów z kontenerem.
DziĊki niemu moĪesz dynamicznie dodawaü serwlety do aplikacji, uzyskiwaü dostĊp
do zasobów znajdujących siĊ w jej obrĊbie, zapisywaü logi do serwerowego dzienni-
ka, a co najwaĪniejsze z obecnego punktu widzenia — moĪesz korzystaü z parametrów
aplikacji webowej (kontekstu). Od parametrów serwletów róĪni je zasiĊg oddziaáywa-
nia. KaĪdy parametr kontekstu jest widoczny we wszystkich serwletach i innych pli-
kach. Parametry serwletu są okreĞlane w podobny sposób jak w przypadku serwletów:
<web-app>
<context-param>
<param-name>tytul </param-name>
<param-value>Java EE 6. Tworzenie aplikacji webowych</param-value>
</context-param>
…
</web-app>
RównieĪ sposób wykorzystywania parametrów kontekstu przypomina ten znany
z serwletów:
try {
out.println("Wszystkie przykđady pochodzæ z ksiæľki " +
this.getServletContext().getInitParameter("tytul"));
} finally {
out.close();
}
Jedyną róĪnicĊ stanowi odwoáanie siĊ do obiektu kontekstu. Reszta pozostaje bez zmian
— nawet nazwa metody. CiekawostkĊ stanowi metoda wprowadzona w specyfikacji
Java Servlets 3.0. OtóĪ aĪ do momentu wprowadzenia tej specyfikacji parametry, za-
równo serwletów, jak i kontekstu, byáy wartoĞciami tylko do odczytu. Jedyną moĪli-
woĞcią zmiany parametrów byáa edycja pliku web.xml. W wersji JavaServlet 3.0 API
pojawiáa siĊ jednak innowacja — moĪliwoĞü dynamicznego ustawiania parametrów
kontekstu za pomocą metody
setInitParameter()
. Wynika to z wprowadzenia duĪej
elastycznoĞci — klasa
ServletContext
w wersji 3.0 uzyskaáa wiele metod, takich jak
addServlet()
, czy
addFilter()
, które umoĪliwiają dynamiczne dodawanie róĪnych
skáadników aplikacji, do tej pory deklarowanych jedynie w pliku web.xml. Nie naleĪy
jednak naduĪywaü tej metody.
Kontekst serwletów pojawi siĊ ponownie juĪ niebawem, tymczasem nadszedá czas,
aby zmierzyü siĊ z przeciwnikiem o wiele waĪniejszym od parametrów — mowa
o atrybutach.
36
CzöĈè I i Podstawy
Trzech muszkieterów?
Parametry, czy to serwletów, czy to aplikacji, mają swoje zastosowania i bywają nie-
zwykle przydatne. Mimo to gáównym Ğrodkiem komunikacji miĊdzy serwletami, kon-
tenerem, sesją, uĪytkownikiem i obiektem Īądania — czyli z grubsza miĊdzy wszyst-
kimi elementami aplikacji — są atrybuty. Z technicznego punktu widzenia miĊdzy
parametrami i atrybutami wystĊpują dwie zasadnicze róĪnice:
W przypadku parametrów zarówno klucz, jak i wartoĞü są áaĔcuchami
znaków, zaĞ w przypadku atrybutów — klucz jest áaĔcuchem, wartoĞü
moĪe byü obiektem.
Parametry z zaáoĪenia są tylko do odczytu (choü w Ğwietle ostatniej wersji
specyfikacji wygląda to inaczej…), natomiast atrybuty są przeznaczone
zarówno do odczytu, jak i do zapisu.
Niezwykáe znaczenie ma takĪe wprowadzenie zasiĊgu atrybutów. Atrybut dodany w za-
siĊgu Īądania (request) nie bĊdzie widoczny w innych zasiĊgach. Tabela 3.1 przedsta-
wia zestawienie parametrów i atrybutów w poszczególnych zakresach.
Tabela 3.1. MoĪliwoĞci zapisywania i odczytywania parametrów i atrybutów w poszczególnych
zakresach
Parametry
Atrybuty
Zakres
Zapis
Odczyt
Zapis
Odczyt
ĩądanie
nie
tak
tak
tak
Serwlet
nie
tak
brak
brak
Sesja
brak
brak
tak
tak
Kontekst aplikacji
tak (od wersji 3.0)
tak
tak
tak
Na podstawie powyĪszej tabeli wydaü wyraĨnie, Īe parametry peánią jedynie funkcjĊ
ustawieĔ, opcji konfiguracyjnych, które uáatwiają zmianĊ w dziaáaniu aplikacji bez
koniecznoĞci ponownej rekompilacji kodu. Atrybuty natomiast mają zastosowanie
o wiele szersze — sáuĪą do wymiany informacji pomiĊdzy poszczególnymi elementa-
mi aplikacji.
W dalszej czĊĞci rozdziaáu skupimy siĊ tylko na atrybutach. Ich ogromna przydatnoĞü
ma bowiem pewne ograniczenia. Jedno z nich jest związane z najwaĪniejszą chyba ce-
chą odróĪniającą aplikacje webowe od aplikacji typu standalone — koniecznoĞü jed-
noczesnej obsáugi wielu uĪytkowników.
Atrybuty a mnogoĈè Ĕñdaþ
Jedna aplikacja webowa moĪe byü uĪywana nawet przez setki czy tysiące uĪytkowni-
ków jednoczeĞnie. KaĪde Īądanie (HTTP request) jest obsáugiwane przez kontener
w osobnym wątku. Istotną kwestią jest wiĊc zapewnienie integralnoĞci operacji wyko-
Rozdziaä 3. i Serwlet — na dobry poczñtek
37
nywanych przez kaĪdego z nich — nie moĪe byü tak, Īe operacje jednego uĪytkowni-
ka wpáyną na efekt operacji innego.
W przypadku parametrów problem ten raczej nie wystĊpuje. Co prawda, w wersji 3.0
pojawiáa siĊ moĪliwoĞü modyfikowania parametrów kontekstu aplikacji, jednak moĪli-
woĞü ta powinna byü uĪywana w bardzo sporadycznych sytuacjach, gdy obsáuga wielu
uĪytkowników nie powinna sprawiaü problemów (np. z powodu wywoáywania takiego
kodu przez superadministratora witryny). JeĞli jednak zabezpieczenie jest konieczne,
moĪna zrealizowaü je w sposób analogiczny do tego, który zaprezentujĊ za chwilĊ.
Zdecydowanie bardziej skomplikowana sytuacja wystĊpuje w przypadku atrybutów.
Wszystkie trzy przypadki omówiĊ w kolejnych podrozdziaáach.
Atrybuty Ĕñdania
W przypadku atrybutów Īądania sytuacja jest stosunkowo prosta. ĩądanie jest realizo-
wane przez jednego uĪytkownika; w dodatku pojedyncze Īądanie nie wiąĪe siĊ w Īa-
den sposób z innymi Īądaniami (nawet tego samego uĪytkownika), dlatego problem
jednoczesnego dostĊpu przez wielu uĪytkowników nie wystĊpuje. Pojawia siĊ jednak
inne pytanie — skoro obiekt Īądania nie wchodzi w interakcje z innymi Īądaniami, po
co miaáby korzystaü z atrybutów?
Takie rozwiązanie wynika ze stosowanych w praktyce mechanizmów obsáugi stron.
Serwlety same w sobie rzadko generują treĞü — na ogóá wykonują one róĪnorodne
operacje (np. pobranie danych z bazy, realizacja logiki biznesowej — choü w wiĊk-
szych aplikacjach i te zadania są delegowane), a nastĊpnie przekazują sterowanie do
pliku JSP. W takiej sytuacji konieczne jest przekazanie informacji miĊdzy serwletem
a plikiem JSP. Voilà! — znaleĨliĞmy zastosowanie atrybutów Īądania. Dokáadne wy-
jaĞnienie i przykáady poznasz w rozdziale poĞwiĊconym JSP.
Atrybuty sesji
W nieco gorszej sytuacji są atrybuty sesji. Wiemy juĪ, Īe jedna sesja jest powiązana
z konkretnym uĪytkownikiem. Teoretycznie nie powinno wiĊc byü problemów. Ale
uĪytkownicy bywają okrutni — wyobraĨ sobie, co mogáoby siĊ staü, gdyby uĪytkow-
nik uruchomiá Twoją aplikacjĊ webową w dwóch zakáadkach i próbowaá jednoczeĞnie
áadowaü róĪne (lub te same) serwlety?
OdpowiedĨ jest prosta: mogáoby dojĞü do jednoczesnego dostĊpu do sesji. Odczyt da-
nych nie stanowiáby problemu, ale atrybuty sesyjne mogą byü przecieĪ równieĪ zapi-
sywane. Taka sytuacja to potencjalny problem. Jak wiĊc mu zaradziü?
Powiem krótko: naleĪy skorzystaü ze standardowego mechanizmu Javy, chroniącego
dane przed zapisem przez wiele wątków jednoczeĞnie — synchronizacji. Teraz musi-
my okreĞliü, dostĊp do czego dokáadnie chcemy synchronizowaü.
Na początek odrzuümy obiekt Īądania (klasy
HttpServletRequest
). Jest to obiekt zwią-
zany tylko z jednym, konkretnym Īądaniem, wiĊc zablokowanie dostĊpu do niego nie
wpáynĊáoby na inne obiekty ĪądaĔ — nadal wszystkie one mogáyby korzystaü bez
38
CzöĈè I i Podstawy
skrĊpowania z sesji. Nie ma sensu równieĪ blokada obiektu serwletu — dostĊp do se-
sji mają róĪne serwlety, wiĊc zablokowanie jednego z nich nie powstrzyma innych od
zapisu do sesji. Jedynym sensownym rozwiązaniem pozostaje zablokowanie obiektu
sesji, do którego uzyskujemy dostĊp za pomocą obiektu Īądania. PoniĪszy kod, wsta-
wiony we wszystkich serwletach, pozwoli na zliczenie wszystkich wywoáaĔ serwle-
tów, które miaáy miejsce w aplikacji webowej dla danego uĪytkownika:
HttpSession sesja = request.getSession();
synchronized(sesja) {
if (sesja.isNew())
sesja.setAttribute("licznik", 1);
else
{
int licznik = Integer.parseInt(sesja.getAttribute("licznik").toString());
sesja.setAttribute("licznik", licznik + 1);
}
}
W ten sposób, gdy jeden serwlet wejdzie w blok synchronizowany, uzyskujemy gwa-
rancjĊ, Īe Īaden inny serwlet w tym momencie dostĊpu do sesji nie uzyska. Wszystkie
inne serwlety bĊdą musiaáy czekaü, aĪ pierwszy serwlet zwolni blokadĊ.
Atrybuty kontekstu serwletów
NajwiĊksze niebezpieczeĔstwo niesie za sobą korzystanie z atrybutów naleĪących do
kontekstu aplikacji. KaĪdy taki atrybut moĪe byü odczytany i zmodyfikowany w do-
wolnym niemal miejscu aplikacji. Z tego wzglĊdu kaĪda próba korzystania z atrybu-
tów (zwáaszcza zapisu) powinna byü synchronizowana.
Zasada dziaáania jest taka sama, jak w przypadku sesji. W tym przypadku musimy jednak
synchronizowaü obiekt kontekstu. Kod synchronizujący przedstawia siĊ nastĊpująco:
ServletContext sc = this.getServletContext();
synchronized(sc)
{
Object licznik = sc.getAttribute("licznik");
if (licznik == null)
sc.setAttribute("licznik", 1);
else
{
licznik = sc.getAttribute("licznik");
sc.setAttribute("licznik", Integer.parseInt(licznik.toString()) + 1);
}
}
PowyĪszy kod realizuje funkcjonalnoĞü podobną do przykáadu z sesją — tym razem
zliczamy jednak wszystkie wywoáania serwletów wykonane przez wszystkich uĪyt-
kowników.
Z obiektami ĪądaĔ, sesji i kontekstu, jak równieĪ z ich atrybutami, wiąĪą siĊ waĪne
klasy — sáuchaczy zdarzeĔ. Choü istnieje moĪliwoĞü tworzenia caáych aplikacji we-
bowych bez ĞwiadomoĞci istnienia tych klas, zdarzają siĊ sytuacje, w których znajo-
moĞü tego typu mechanizmów jest niezbĊdna.
Rozdziaä 3. i Serwlet — na dobry poczñtek
39
S
äuchowisko
Sáuchacze zdarzeĔ to obiekty spotykane w Javie niezwykle czĊsto. Początkujący pro-
gramiĞci Javy spotykają siĊ z nimi np. podczas tworzenia prostych aplikacji graficz-
nych. Sáuchacz zdarzeĔ powiązany z przyciskiem pozwalaá na wykonanie dowolnego
kodu np. po jego klikniĊciu. PojĊcie sáuchacza zdarzeĔ nie ogranicza siĊ oczywiĞcie
do tworzenia aplikacji z graficznym interfejsem — równieĪ aplikacje webowe dają
sáuchaczom zdarzeĔ spore pole do popisu.
W poniĪszych podrozdziaáach przedstawiĊ interfejsy sáuchaczy zdarzeĔ przeznaczone
do uĪycia w aplikacjach webowych. Nie jest to moĪe najciekawszy fragment niniej-
szej ksiąĪki, ale prĊdzej czy póĨniej znajdziesz siĊ w sytuacji, w której bĊdziesz mu-
siaá skorzystaü z opisanych w nastĊpnych akapitach mechanizmów.
ServletContextListener
Jest to najrzadziej chyba wykorzystywany sáuchacz zdarzeĔ. Zawiera dwie metody:
contextInitialized()
i
contextDestroyed()
, które są wywoáywane w momencie utwo-
rzenia/usuniĊcia kontekstu aplikacji, czyli — w momencie startu i zakoĔczenia aplika-
cji. Obydwie metody przyjmują parametr typu
ServletContextEvent
— umoĪliwia on
pobranie kontekstu aplikacji za pomocą metody
getServletContext()
.
ServletContextAttributeListener
Drugim sáuchaczem zdarzeĔ związanym z kontekstem aplikacji jest sáuchacz obser-
wujący kolekcjĊ atrybutów kontekstu. Reaguje on na dodawanie (metoda
attribute
´Added()
), usuwanie (
attributeRemoved()
) i zamianĊ (
attributeReplaced()
) atrybu-
tów. Wszystkie trzy metody przyjmują jeden parametr typu
ServletContextAttribute
´Event
— umoĪliwia on pobranie nazwy modyfikowanego atrybutu (
getName()
), jego
wartoĞci (
getValue()
).
ServletRequestAttributeListener
i ServletRequestListener
Obydwa interfejsy peánią analogiczne funkcje, co ich „kontekstowi” koledzy — na-
wet nazwy metod są podobne (w przypadku pierwszego interfejsu — identyczne,
w przypadku drugiego —
requestInitialized()
i
requestDestroyed()
). Jedyną real-
ną zmianą jest wprowadzenie dodatkowej funkcjonalnoĞci do klas argumentów zdarzeĔ
—
ServletRequestAttributeEvent
i
ServletRequestEvent
. UdostĊpniają one metodĊ
getServletRequest()
. Pozwala ona na skorzystanie z obiektu Īądania, którego zda-
rzenia dotyczą.
40
CzöĈè I i Podstawy
HttpSessionAtributteListener
i HttpSessionListener
RównieĪ sáuchacze zdarzeĔ powiązani z sesjami zostaáy utworzone zgodnie z omówio-
nymi powyĪej zasadami. Sáuchacz
HttpSessionListener
jest wykorzystywany przy
tworzeniu (metoda
sessionCreated()
) i koĔczeniu sesji (
sessionDestroyed()
). Prze-
kazywany argument — obiekt klasy
HttpSessionEvent
— udostĊpnia metodĊ
getSes-
sion()
, która daje dostĊp do utworzonej (zakoĔczonej) sesji. W przypadku interfejsu
HttpSessionAttributeListener
mamy do dyspozycji te same trzy metody, co w po-
przednich przypadkach. Typ zdarzenia to
HttpSessionBindingEvent
. Jego moĪliwoĞci
sprowadzają siĊ do pobrania obiektu sesji i nazwy/wartoĞci dodawanego/usuwanego/
zmienianego atrybutu.
HttpSessionBindingListener
Nareszcie coĞ ciekawego! Tytuáowy interfejs odbiega nieco od schematu, z jakim mie-
liĞmy do czynienia przez ostatnie trzy podrozdziaáy. Wszystkie trzy interfejsy z czáo-
nem
AttributeListener
w nazwie odpowiadaáy za informowanie o zmianach zacho-
dzących w kolekcji atrybutów. Dla odmiany interfejs
HttpSessionBindingListener
powinien byü implementowany przez klasy, których obiekty bĊdą umieszczane w se-
sji! JeĞli wiĊc tworzysz wáasne klasy do przechowywania danych, które w momencie
zapisu do sesji powinny byü w jakiĞ sposób przetworzone, powinieneĞ skorzystaü z tego
interfejsu. Metody tego interfejsu to
valueBound()
i
valueUnbound()
, wywoáywane od-
powiednio w momencie doáączenia obiektu do sesji lub jego usuniĊcia. Klasa argu-
mentu zdarzeĔ to znana z poprzedniego akapitu
HttpSessionBindingEvent
.
Sesja + wiele JVM = HttpSessionActivationListener
Java EE to technologia uniwersalna, która moĪe byü z powodzeniem stosowana w roz-
budowanych aplikacjach webowych i biznesowych. Jednym z waĪnych aspektów two-
rzenia takich aplikacji jest skalowalnoĞü — moĪliwoĞü poprawnego i wydajnego dziaáa-
nia aplikacji w dowolnych warunkach (niewielkiego, jak i bardzo duĪego obciąĪenia),
bez koniecznoĞci zmiany samej aplikacji. Bardzo czĊsto, w celu poprawy wydajnoĞci,
ta sama aplikacja jest instalowana na wielu komputerach poáączonych w sieü, a spe-
cjalny serwer, bĊdący bramą do Ğwiata zewnĊtrznego (czyli do klientów), kieruje „ru-
chem” i przydziela poszczególne Īądania do tych komputerów, które w danym mo-
mencie są najmniej obciąĪone. Taka technika nosi nazwĊ równowaĪenia obciąĪenia
(ang. load balancing).
Nie bĊdziemy zagáĊbiaü siĊ teraz w szczegóáy tej techniki; w niniejszej ksiąĪce bĊdzie
nas interesowaü co najwyĪej wpáyw tej techniki na kod aplikacji lub informacje za-
warte w deskryptorze wdroĪenia. W tym podrozdziale zajmiemy siĊ sáuchaczem zda-
rzeĔ, który jest związany z dwoma istotnymi zagadnieniami: obsáugą sesji i równowa-
Īeniem obciąĪenia.
Rozdziaä 3. i Serwlet — na dobry poczñtek
41
Gdy uĪytkownik tworzy obiekt sesji, jest on zapisywany (np. w postaci pliku) na kom-
puterze, który obsáugiwaá w danej chwili Īądanie. NastĊpne Īądanie uĪytkownika mo-
Īe jednak byü przydzielone do zupeánie innego komputera wewnątrz sieci — dlatego
konieczne jest wprowadzenie mechanizmu, który przeniesie obiekt sesji z pierwszego
komputera na drugi.
Na szczĊĞcie mechanizm ten jest automatyczny. Jedynym (opcjonalnym) zadaniem
programisty jest wykonanie operacji przed przeniesieniem sesji z komputera pierwot-
nego i po przeniesieniu sesji na komputer docelowy. SáuĪą do tego celu metody:
void sessionWillPassivate(HttpSessionEvent hse)
— metoda wywoáywana
tuĪ przed przesáaniem sesji do innego komputera;
void sessionDidActivate(HttpSessionEvent hse)
— metoda wywoáywana
tuĪ po otrzymaniu sesji od pierwszego komputera.
Filtry
Do tej pory wszystkie przykáady omawialiĞmy, korzystając z modelu obsáugi Īądania/
odpowiedzi. ĩądanie HTTP przesáane przez klienta powodowaáo utworzenie nowego
wątku, wykorzystującego obiekt odpowiedniego serwletu. Na podstawie metody Īą-
dania HTTP nastĊpowaá wybór odpowiedniej metody klasy
HttpServlet
(
doGet()
,
doPost()
itd.). Model ten moĪna jednak rozszerzyü o dodatkowe elementy — filtry.
Filtr umoĪliwia wykonywanie operacji w momencie nadejĞcia ĪądaĔ do serwletów
i wygenerowania przezeĔ odpowiedzi, przy czym nie ingeruje on w dziaáanie samego
serwletu. MoĪna zatem np. zapisaü w dzienniku datĊ kaĪdego Īądania, jego parametry,
a takĪe sprawdziü, jaka jest dáugoĞü odpowiedzi wygenerowanej przez serwlet.
W praktyce serwlety stosuje siĊ równieĪ do kontroli, a ewentualnie takĪe modyfikacji
obiektów Īądania i odpowiedzi. DziĊki filtrom moĪesz skompresowaü caáą treĞü od-
powiedzi, zanim zostanie przesáana ostatecznie do klienta. MoĪesz teĪ w uniwersalny
sposób odrzucaü Īądania, które nie speániają okreĞlonych warunków (np. wartoĞü
ustalonych parametrów). NajwiĊkszą zaletą filtrów jest moĪliwoĞü podáączenia ich
do dowolnej grupy serwletów — filtry są áączone z serwletami za pomocą znacznika
url-pattern
, dziaáającego analogicznie jak w przypadku serwletów. Jak widaü, doda-
nie jednego znacznika pozwala na wáączenie szyfrowania, kompresji lub kontroli do-
stĊpu dla caáej grupy serwletów.
Techniczny aspekt filtrów
Z technicznego punktu widzenia filtr jest klasą implementującą interfejs
javax.servlet.
Filter
. Interfejs ów skáada siĊ z trzech metod:
void init(FilterConfig fc)
— metoda wywoáywana przy utworzeniu
filtru. Pozwala na uzyskanie obiektu ustawieĔ filtru — obiektu interfejsu
FilterConfig
.
42
CzöĈè I i Podstawy
void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain)
— metoda wywoáywana w momencie nadejĞcia Īądania.
Dokáadny opis w dalszej czĊĞci podrozdziaáu.
void destroy()
— metoda wywoáywana przez serwer w momencie
zakoĔczenia dziaáania filtru.
NajwiĊksze znaczenie ma, rzecz jasna, metoda
doFilter()
. Typowy sposób jej wyko-
rzystania przedstawiam poniĪej:
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) {
if (!req.isSecure())
((HttpServletResponse)res).sendError(HttpServletResponse.SC_RESPONSE);
chain.doFilter(req, res);
bool spakowany = false;
if (req.getParameter("format") != null &&
req.getParameter("format").equals("spakowany"))
spakowany = true;
if (spakowany) {
// pobierz treĞü odpowiedzi, spakuj ją, a nastĊpnie
// zapisz, korzystając z obiektu res.
}
}
TreĞü powyĪszej metody moĪna podzieliü na trzy czĊĞci. Na początku sprawdzamy,
czy dane Īądanie jest realizowane w trybie bezpiecznym, czyli z wykorzystaniem pro-
tokoáu SSL. JeĞli nie — odsyáamy uĪytkownikowi kod báĊdu 403 — forbidden (brak
uprawnieĔ do zrealizowania Īądania).
NajwaĪniejszym fragmentem metody jest wywoáanie
doFilter()
obiektu
chain
. ZbieĪ-
noĞü nazw w tej sytuacji jest przypadkowa (klasy
FilterChain
i
Filter
nie są ze sobą
w Īaden formalny sposób związane). Zadaniem interfejsu
FilterChain
jest zapewnie-
nie komunikacji miĊdzy filtrem (lub grupą filtrów) a serwletem. W zaleĪnoĞci od ko-
lejnoĞci deklaracji filtrów w deskryptorze wdroĪenia Īądanie HTTP przechodzi przez
kolejne filtry (dziĊki wywoáaniom metody
doFilter()
w kaĪdym filtrze), aĪ w koĔcu
dociera do serwletu. Po zakoĔczeniu obsáugi przez serwlet, sterowanie powraca do ko-
lejnych filtrów (w naszym przypadku od deklaracji zmiennej
spakowany
, aĪ do opusz-
czenia ostatniego filtru).
Konfiguracja filtrów w pliku web.xml
Samo utworzenie klas filtrów to za maáo. Musisz dodaü takĪe odpowiednie wpisy w pli-
ku web.xml. Zasada dziaáania filtrów przypomina tĊ znaną z serwletów, dlatego rów-
nieĪ informacje podawane w deskryptorze wdroĪenia powinny wydaü Ci siĊ znajome:
<filter>
<filter-name>Pierwszy filtr</filter-name>
<filter-class>pl.helion.jeeweb.Filtr<filter-class>
</filter>
<filter-mapping>
<filter-name>Pierwszy filtr</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>