Java EE 6. Programowanie aplikacji WWW. Wydanie II - Krzysztof Rychlicki-Kicior (2015)

271 Pages • 85,170 Words • PDF • 3.5 MB
Uploaded at 2021-09-24 18:17

This document was submitted by our user and they confirm that they have the consent to share it. Assuming that you are writer or own the copyright of this document, report to us by using this DMCA report button.


Spis treści Część I Rozdział 1.

Podstawy

.

.....................................................................

Java EE - naprawdę krótkie wprowadzenie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11

Web vs Enterprise Serwery aplikacji Streszczenie, czyli krótki przewodnik po niniejszej publikacji Serwlety - na dobry początek Deskryptor wdrożenia JSP - HTML + Java JPA - czas na dane! JSF - wyższy poziom prezentacji F acelets

. ...........................................................................................................

. . ........................................................................................................... . .....................................

.....................................................................................

. ................................................................................................

. .................................................................................................

. . ................................................................................................. .

............................................................................

. ......................................................................................................................

Rozdział 2.

12 13 13 13 14 15 15 16 16

Pierwsza aplikacja webowa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17

Integrowanie Tomcata z Netbeansem Pierwsza aplikacja Dodawanie nowych elementów Pierwszy serwlet?

.

............................................................................

. . ......................................................................................................... .......................................................................................

. ...........................................................................................................

Rozdział 3.

9

18 19 21 23

Serwlet - na dobry początek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27

Ż ycie serwletu Serwlet pod lupą Ż ądanie - odpowiedź Przesyłanie odpowiedzi Om nom nom, czyli ciasteczka w pełnej krasie Sesje - nie tylko dla studentów Konfiguracja w kodzieJavy- można tego uniknąć Parametry serwletów Kontekst serw letów Trzech muszkieterów? Atrybuty a nmogość żądań Słuchowisko ServletContextL istener ServletContextAttributeL istener ServletRequestAttributeL istener i ServletRequestL istener HttpSessionAttributeL istener i HttpSessionL istener HttpSessionBindingL istener Sesja + wiele JVM HttpSessionActivationL istener

. ................................................................................................................ . ............................................................................................................. . ...................................................................................................... . .............................................................................................. . .........................................................

.

............................................................................... . .....................................................

. ..................................................................................................

. .................................................................................................... . . ................................................................................................... . .........................................................................................

. ................................................................................................................... . . .............................................................................................. .

................................................................................ . . ......................................

. .................................................

........................................................................................

=

. ................................................

27 28 29 31 33 34 36 36 37 38 39 41 42 42 42 42 43 43

Java EE 6. Programowanie apl i kacji WWW

4

F iltry Techniczny aspekt filtrów Konfiguracja filtrów w pliku web.xml Asynchroniczna obsługa żądań

. ............................................................................................................................... . .......................................................................................... .

......................................................................

. . ......................................................................................

Rozdział 4.

JSP - gdy out.printlnO nie wystarcza . ........................................... 49

Zacznijmy od początku, czyli JSP w świecie serw letów Pliki JSP dostępne bezpośrednio Pliki JSP wywoływane z poziomu serwletów Pochodzenie JSP - małe dochodzenie Pierwsze kroki w JSP Docenić wygodę, czyli jak to lat temu kilka bywało Expression L anguage - elegancja i wygoda Remedium - warto było czekać ! Dostęp do obiektów w językuEL Beany, czyli ziarna - kult kawy wiecznie żywy Ziarna + EL kolejne ułatwienie Ziarna, mapy i co dalej? EL - nie tylko atrybuty Akcje JSP Include VS. F orward - odsłona druga Akcje + ziarna kolejne potężne narzędzie Dynamiczne generowanie elementów .

. ...............................................

............................................................................... . ...........................................................

.

.........................................................................

. . .................................................................................................... . . .

. ................................................

. ................................................................

.

.

............................................................................

............................................................................. . ......................................................

=

.

..............................................................................

. ............................................................................................. . .............................................................................................

. ....................................................................................................................... .

.......................................................................

.

.......................................................................

=

Rozdział 5.

44 44 45 46

.

..............................................................

50 50 50 51 52 54 58 59 60 61 62 63 64 65 66 67 70

JSTL - wisienka na torcie JSP . ..................................................... 73

Skrzynka z narzędziami 73 Rdzeń 74 c: out 74 Ale to już było, czyli c: set 76 Czwarty muszkieter 77 Kontrola sterowania 77 Pętelka do kompletu 79 Wyjątki + JSP . ....................................................................................................80 Adresy URL - same klopoty 81 Adresy URL bez tajenmic 81 Tajenmica sesji 82 Trzech tenorów 83 Na deser: funkcje! 84 Przez kolekcje do serca 84 F unkcje łańcuchowe 85 Podsumowanie 86 . . .................................................................................................

. ..............................................................................................................................

. . ..........................................................................................................................

. ..........................................................................................

. .................................................................................................... . . ..................................................................................................

. . ..................................................................................................

=

. . .

.........................................................................................

. . .........................................................................................

. . .

. . ......................................................................................................

. ............................................................................................................... . . ..........................................................................................................

. ..............................................................................................

. ...................................................................................................

. ................................................................................................................

87

Część I I

Frameworki webowe

Rozdział 6.

JavaServer Faces . ......................................................................... 89

.

...................................................

F rameworki - kolejny dowód na lenistwo człowieka 89 JSF - kanonu ciąg dalszy 90 JSF , czyli MYC w praktyce 91 Kontroler - uniwersalny spawacz 92 Małe zanurzenie 92 Pierwsze przykłady 93 Aplikacja notowania giełdowe 94 Tajenmiczy zapis - # vs $ .........................................................................................99 Notowania historyczne, czyli kolekcja w kolekcji 101 . ..................................................

. ..............................................................................................

........................................................................................ .

...........................................................................

. .............................................................................................................. . ....................................................................................................

. .......................................................................................

. ..................................................

5

Spis treści

Najpierw szablon, później treść . ...............................................................................102 Klient szablonu .........................................................................................................103 Przygotowania ... .......................................................................................................104 Czas na obliczenia! ...................................................................................................107 Mały zastrzyk . ...........................................................................................................109 JSF - komponenty, komponenty, komponenty! . ........................................................110 Output - (prawie) wszystko, czego do szczęścia potrzeba . ....................................111 UIInput - teraz do szczęścia nie potrzeba już nic . ..................................................112 Powrót do szarej rzeczywistości ... . ..........................................................................116 Zasady działania JSF .....................................................................................................119 Przykładowa aplikacja - maszyna licząca . .............................................................119 Przywrócenie widoku ( l ) ..........................................................................................122 Pobranie danych z żądania (2 ) ...................................................................................123 Walidacja (3 ) . ............................................................................................................123 Aktualizacja wartości w modelu (ziarnach) (4 ) . .......................................................123 Wywołanie zadeklarowanych uprzednio metod (5 ) . ................................................124 Renderowanie odpowiedzi (6 ) . .................................................................................124 Cykl życia w praktyce ...................................................................................................124 Podsumowanie . ..............................................................................................................125 Rozdział 7.

Konwertowanie i walidacja . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . .. . . . . 127

Uroki transformacji .......................................................................................................127 Konwertery standardowe ...........................................................................................128 Piszemy konwerter! ...................................................................................................130 Walidator - nieodłączny partner konwertera . .............................................................134 Walidatory - prawie jak konwertery . .....................................................................135 Walidacja niestandardowa- więcej pracy . .............................................................136

139

Część I I I

Obsługa danych

Rozdział 8.

JPA, czyli ORM + Java . . . . . .. . . . . . . . . . . . . .. . . . . . . . . . . . . .. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 141

.

........................................................

Dostęp do danych w Javie .............................................................................................141 Oświecenie . ...............................................................................................................142 Pierwszy przykład .........................................................................................................143 Założenia 143 Realizacja . .................................................................................................................143 Tworzenie projektu ...................................................................................................144 Hibemate a JPA - co i jak w ORM- owym świecie . ...............................................145 Pierwsza k1asa encji ..................................................................................................145 Jednostka utrwalania .................................................................................................149 Graficzna strona aplikacji ..........................................................................................150 Dodawanie przychodni ..............................................................................................154 EntityManager i spółka ... .........................................................................................156 Menedżer encji - elegancki dostęp! łatwa sprawa . .............................................157 Nudni słuchacze - nareszcie przydatni! . ................................................................160 C już jest, czas na RUD .............................................................................................162 Niewiele Ci mogę dać ... (póki nie pozwolisz mi zaprezentować danych) ...............162 Słuchacz akcji vs akcja - starcie numer 2 . .............................................................164 Istotny drobiazg - nasza aplikacja to niemowa! . ....................................................166 . . .................................................................................................................

=

Rozdział 9.

Związki między encjami - jedna tabela to za mało! . . . . . . . . . . . . . . . . . . . . . 169

Przychodnia ... i co dalej? ..............................................................................................169 Związki między tabelami - krótkie przyponmienie . ..............................................169 Związki SQL w praktyce ...........................................................................................170

6

Java EE 6. Programowanie apl i kacji WWW

Jeden do wielu, wiele do jednego 171 Wiele do wielu - najwyższy stopień wtajemniczenia 171 Dodajemy tabele do bazy 172 Encje klas Javy - czas na związki! 174 Encja Przychodnia - zmiana na lepszy model 175 Czas na nowości! 176 Wizyta- encjaJPA w pełnej krasie 183 CRUD dla lekarza - to już było, ale nie do końca 188 Nowy lekarz - nowe pole, duża zmiana 188 Magikonwersja 190 Ziarnko do ziarnka i zbierze się aplikacja 191 Kolejne metody ziarna L ekarzBean . .. . ....................................................................193 Na zakończenie - edycja 193 Pacjenci - suplement 195 Danie główne: all in one, czyli wizyty! 197 Od czegoś trzeba zacząć , czyli zmiany 197 Dodawanie wizyty 200 Ostatnie ziarno 202 Edycja i usuwanie - powrót 204 Koniec coraz bliżej, czyli edycja w pełnej krasie 205 Podsumowanie 206 .

............................................................................ . ............................................

. ......................................................................................... .

............................................................................ . .......................................................

. ...................................................................................................... .

......................................................................

. .....................................................

.

................................................................

. .........................................................................................................

. ...............................................................

.........................................................................................

. .............................................................................................. .

....................................................................... .

...................................................................

. ....................................................................................................

. .........................................................................................................

....................................................................................

. ....................................................

. ..............................................................................................................

Rozdział 10. JPQl i jego możliwości . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . .. . . . . . . . . . . . . .. . . . . . . . . . . . . . . .. . . 207

Prawie jak SQL . .. "prawie" robi różnicę Podstawy Pobieranie z wariantami JPQL a atrybuty złożone i null Nieco więcej o SELECT F unkcje obliczeniowe Operacje niezwiązane z pobieraniem Mechanizmy zaawansowane JOIN na lewo,JOIN na prawo Grupowanie i sortowanie Podzapytania - prawdziwa moc Podsumowanie

.

....................................................................

. ...................................................................................................................... . ........................................................................................... .

................................................................................

. . .........................................................................................

. . .............................................................................................. .

......................................................................

......................................................................................... . . .

.

............................................................................

. ......................................................................................... .

............................................................................

. ..............................................................................................................

207 208 208 210 211 212 213 213 214 215 216 217

Rozdział 11. Szybko, jeszcze szybciej, czyli WebSockets w akcji . . . . . . . . . . . . . . . . .. . . . . 219

Haki, haczyki i obejścia - jak to dawniej bywało Ramki AJAX Techniki zewnętrzne Comet Zdarzenia wysyłane przez serwer Gniazda webowe (ang.WebSockets) Przykładowy projekt Założenia Obsługa gniazd webowych w Javie EE Podsumowanie

. ......................................................

. ....................................................................................................................... . ....................................................................................................................... . . ...............................................................................................

. . ...................................................................................................................... .

................................................................................ .

..........................................................................

. .....................................................................................................

. . ................................................................................................................. .

...................................................................

. ..............................................................................................................

220 220 221 222 223 224 224 225 225 230 234

Rozdział 12. Wstrzykiwanie zależności i jego zastosowania . . . . . . . . . . . . .. . . . . . . . . . . . . .. . . 235

Wstrzykiwanie zależności a JavaEE 237 Konfiguracja aplikacji webowej w celu obsługi cm . ..............................................237 Co daje nam cm .......................................................................................................238 .

...........................................................................

7

Spis treści

Rozdział 13. Integracja aplikacji webowej z mobilną na przykładzie JAX-RS . . . . . . . 245

Przykładowa aplikacja Kod bez kodu, czyli konfiguracja wstępna Aplikacja mobilna

. . ................................................................................................. .

..............................................................

. ....................................................................................................

Dodatki Dodatek A

. ...................................................................................

247 247 253

263

Instalacja serwera Apache Tomcat . . . . . . . . . . .. . . . . . . . . . . . . .. . . . . . . . . . . . . . . .. . . . . 265

Pobranie Konfiguracja

. . ....................................................................................................................... . .................................................................................................................

265 266

Bibliografia . . . .. . . . . . . . . . . . . .. . . . . . . . . . . . . . . .. . . . . . . . . . . . . .. . . . . . . . . . . . . .. . . . . . . . . . . . . . . .. . . 267 Skorowidz . . . . . .. . . . . . . . . . . . . . . .. . . . . . . . . . . . . .. . . . . . . . . . . . . .. . . . . . . . . . . . . . . .. . . . . . . . . . . . . .. . . 269

8

Java EE 6. Programowanie apl i kacji WWW

Część

I

Podstawy

10

Java EE 6. Programowanie apl i kacji WWW

Rozdział 1.

Java EE naprawdę krótkie wprowadzenie -



Javą"'�IS; � �1) �

o Javie Enterprise Edition, nazywanej w skrócie (dawniej J2EE), napi­ sano już wiele inte :esując�ch i rozbu ?wanych ksi lko je en rozdział pośv-:ię­ cony wprowa �ell1u do tej technol�gl1 mo�e d � Slę szal�nstwen: - w koncu . okreslane mianem wpro­ ozycJe, można odnalezc na rynku ponadtyslącstr " st opisanie całego spektrum Javy EE. wadzenia" do tej technologii. Moim cele Zanim jednak przejdziemy do meritum rozdzialu, warto się zastanowić, czym w istocie jest ta J ava EE.





�ft � �� � �

� O



J ava Enterprise Edition jest st m tworzenia zorientowanych na usługi aplikacji biznesowych (ang. Service OrNiJJk d Architecture), utworzonym na bazie języka Java i technologii Java 2 Stand cI;tion (definicja zajava. sun. com). W praktyce jest to zbiór różnorodnych technol ameworków, umożliwiających tworzenie wyżej wymietego było mało, zewnętrzne firmy i organizacje (np. Struts, nionych aplikacji. J Spring czy Sea w zą własne frameworki i biblioteki, które rozszerzają możliwo­ ści Javy EE.

1JL� �

Nietrudno się domyślić, że to właśnie ów ogromny zasięg platformy JEE powoduje trud­ ności w dogłębnym poznaniu wszystkich technologii wchodzących w jej sklad. Nie oznacza to jednak, że bycie dobrym programistą JEE wymaga przeczytania i zapamiętania dokumentacji każdej technologii skladowej. Podobnie jak w innych technologiach infor­ matycznych, tak i tu mamy do czynienia zarówno ze skladnikami o dużej wadze (EJB, JSF, serwlety, JSP, JPA), jak i mniej istotnymi - wykorzystywanymi rzadko lub w ograniczo­ nym zakresie (Streaming API for XML, JavaMail). Skoro zdecydowaleś się na zakup tej książki, prawdopodobnie jesteś studentem, samo­ ukiem, a może nawet doświadczonym programistą, jednak prawie na pewno możesz przyznać jedno - nie masz dużego doświadczenia w Javie EE. Jednym z najwięk­ szych problemów tej platformy jest względny (w porównaniu do innych technologii internetowych) brak materiałów dla osób początkujących. Powodów takiej sytuacji może być wiele:

Część I • Podstawy

12

• •



brak technologii będącej absolutną podstawą dla całej reszty; rozmiar i zasięg nawet prostych aplikacji biznesowych mogą z początku przerażać osoby niezaznajomione z tematem; pozorny brak zapotrzebowania na tego typu materiały zgodny z zasadą, " że "kto chce, ten i tak się nauczy .

Zwłaszcza trzeci argument ma swoich gorących zwolenników. Sęk w tym, że wiele osób może mieć chęci, ale brak im wprowadzenia, dzięki któremu poczują swoisty "klimat" aplikacji klasy enterprise. Sporo studentów w trakcie studiów musi zaliczyć przedmiot związany z JEE - oni zwłaszcza mogą docenić krótki (w porównaniu do opasłych tomisk) przewodnik po tej fascynującej platformie programistycznej.

Web vs Enłerprise Y'

��





Wiesz już, że JEE to technologia złożona. Wielkie fin� -�'o niej wielkie aplikacje, , zatrudniając doświadczonych programistów z wiel �iejętnościami, po co więc my, początkujący programiści, mamy . . . STOP



�� ,,-

Ten stereotyp, z którym przez długi czas ' ie a się zgadzałem, jest bardzo popu­ larny wśród rzeszy twórców aplikacji we , korzystających z takich technologii, jak PythonlDjango, Ruby on Rails, c "", wionego PHP z jego licznymi framewor­ ypu w Javie rob się rzeczy dużego kalibru, ka�i np Symfony). �edług t o s� . alowalne systemy mformatyczne dla wielkich takie Jak duże portale mterneto cji. I to wszystko jest prawdą. Rzecz w tym, że nie firm czy powszechnie znanych ' jest to cała prawda.

�� �





'\ i��J � Oj:gO � A....

mniej szego (nie młodszego!) brata. Są nim aplikacje Aplikacje biznesowe webowe - czyli s internetowe,. wzbogacone o dynamiczną funkcjonalność, czę­ sto wykorzystu.ii... bazy danych. Zaliczają się do nich sklepy internetowe, systemy Management System - system zarządzania treścią) i wszystkie CMS (ang. inne aplikacje, które przyczyniły się do powstania nurtu Web 2.0 - sieci tworzonej przez internautów.

eę C�

Z formalnego punktu widzenia aplikacje webowe korzystają z grupy tych samych elementów, co aplikacje biznesowe - rzecz w tym, że aplikacje nazywane webowy­ mi powinny korzystać jedynie z czterech technologii JEE, a mianowicie : •

JavaServer Faces,



JavaServer Pages,



JavaServer Pages Standard Tag Library,



Java Servlet.

Powyższa lista nie zawiera jednak technologii odpowiedzialnych za bezpośrednią obsługę danych (giównie JP A), dlatego w praktyce mało kto przejmuje się formalnymi defini­ cjami. Najważniejszą cechą aplikacji webowych jest pominięcie rozbudowanych tech-

Rozdział 1. • Java EE

- naprawdę krótkie wprowadzenie

13

nologii ściśle biznesowych, z technologią EJB (ang. Enterprise JavaBeans) na czele. Przyjęło się określać, że jeśli aplikacja korzysta z ziaren EJB, nazywa się ją wtedy aplikacją biznesową, w przeciwnym razie należy mówić o aplikacji webowej. Defini­ cja ta wynika również z powodów czysto technicznych - mowa o serwerach, które są wymagane do uruchomienia aplikacji JEE.

Serwery aplikacji Java EE wyróżnia się pod wieloma względami. Jedną z ważniejszych cech, które od­ różniają aplikacje JEE od innych aplikacji internetowych czy biznesowych, jest ko­ nieczność uruchamiania ich na specjalnych serwerach aplikacji. Jeśli więc tworzyłeś już aplikacje internetowe w jednej z wymienionych wcześni� technologii i przywiązałeś się do serwe�a Apache, mam dla Ciebie złą wiadomość - będ esz musiał z niego zrezygnowac. ....... " Dla fanów "indiańskiej organizacji mam też wiado �ść rą - zajmuje się ona tworzeniem najpopulamiejszego serwera aPlikaCji WD ��'.•czyli Apache Tomcat. Ser­ wer ten obsługuje aplikacje webowe, korzystąjąc c erech technologii, które zostały . dnak, że korzystając z Tomcata, wymienione w poprzednim akapicie. Nie oznac będziesz musiał zrezygnować np. z baz onieważ wiele innych technologii, często o�isy:van�ch jako stricte biznes�w � ż będzi� funkcj�nowało na Tomcacie. . szeJ kSiążce zdecydowana lllllleJ c przykładow będzie wykonywana właSllle na tym serwerze.

( ""

� � �

�� � � �

-YV.

\..)

m """Q �

Mimo wielu swoich zalet TO� jest w pełni funkcjonalnym serwerem aplikacji JEE, ponieważ brakuje U rz�wszystkim obsługi wspomnianego już EJB. Z tego względu do zastosowań bi o�ch wykorzystuje się serwery aplikacji, takie jak Gl assFish 1TI1ly Sun Microsystems (twórców Javy), implementujący w wersji 4.0. Jest o s wszystkie technolo ' hodzące w skład Javy EE. W niniejszej książce w zakresie apli­ ka�ji webowych",- d� my korzystać z T��cata w wersji który za:viera impl�men­ . taCJę Javy EE 7. Zestawtechnologl1 webowych, o ktorym mowlmy w tej kSiążce, nosi zbiorczą nazwę Web Profile.

� �JI

�,

Streszczenie, czyli krótki przewodnik po niniejszej publikacji Serwlety

-

na dobry początek

Poznawanie Javy EE można rozpocząć na kilka różnych sposobów. Wybór ścieżki nauczania ma ogromny wpływ na zrozumienie całej technologii. W niniejszej książce koncentrujemy się głównie na technologiach wykorzystywanych przy tworzeniu apli­ kacji webowych. Na wybór ten ma wpływ kilka różnych przyczyn:

Część I • Podstawy

14







Aplikacje webowe z pewnością wydadzą się bliższe autorom dynamicznych stron internetowych, którzy stanowią sporą grupę osób potencjalnie zainteresowanych J avą EE. Efekty działania aplikacji webowych, szczególnie tych prostych, są łatwe do sprawdzenia oraz wymierne. W przypadku bardziej zaawansowanych komponentów biznesowych proces tworzenia i testowania nawet prostych mechanizmów jest nieco bardziej skomplikowany. Komercyjne aplikacje webowe mogą być tworzone nawet przez nieduże grupy programistów, podczas gdy skomplikowane aplikacje biznesowe są produkowane z reguły przez duże, uznane firmy.

Podstawą wszystkich aplikacji webowych, tworzonych zarówno za pomocą standardo­ wych technologii JEE, jak i dodatkowych frameworków, jest serwlet. Upraszczając teo­ retyczną definicję, serwlet w Javie jest klasą, która może przyjmować żądania i genero­ wać odpowiedzi. JEE - jako technologia stawiająca na uni�rsalność (podobnie jak rwletów działających inne technologie javowe) - teoretycznie udostępnia mecha dla dowolnych protokołów. W praktyce absolutna wi zó rwletów wykorzystuje protokół HITP.



�� �

�• � �



T'."o:zeni� a�likacji webowych za pomocą sam� etów byłobX za aniem niewąt­ pliwie UCiążliwym. Z tego względu oprac w n �reg technologu, ktore w znaczny ześnie ułatwiają korzystanie z nich. sposób rozwijają możliwości serwletów i n wią podstawę dla wszystkich aplikacji Nie zmienia to jednak faktu, że serwlet webowych.

� O

Deskryptor wdrożenia





Większość rozdziałów ch w niniejszej książce jest poświęcona konkretnym technologiom, któ;� ią wzbogacać Twoje umiejętności o konkretne, coraz to nowe możliwości . N eni kilku roz ziałów będzie pojawiał się jednak stały motyw - odwołam. a kryptora wdrożema.

� \ ��� �



Deskryptor wdrożenia (ang. deployment descriptor) to plik XML stanowiący central­ ny element każdej aplikacji webowej. To właśnie w nim zapisywane są kluczowe ustawienia dotyczące serwletów i innych elementów aplikacji webowych. Większość mechanizmów, które będziesz poznawać w trakcie lektury niniejszej książki, będzie w jakiś sposób wiązała się także z uzupełnieniem odpowiednich wpisów w deskrypto­ rze wdrożenia. Możesz zapytać - po co tworzyć osobny plik XML skoro ustawienia można zapisywać bezpośrednio, np. w kodzie serwletów. Rozwiązanie z wykorzystaniem pliku XML ma jednak swoje zalety: ,



Zmiany w pliku XML nie wymagają rekompilacji plików źródłowych.



Zmiany w pliku XML mogą być dokonywane nie tylko przez programistów.



Plik XML oddziałuje na wszystkie pliki projektu - nie trzeba dbać o widoczność poszczególnych ustawień w całej aplikacji.

Rozdział 1. • Java EE

-

naprawdę krótkie wprowadzenie

15

Z drugiej strony na skutek coraz większej popularności adnotacji w całym świecie związanym z Javą coraz więcej ustawień może być przenoszone do kodu, co z kolei po­ zwala na lepszą separację pracy poszczególnych programistów. Oznacza to, że po za­ kończeniu tworzenia klasy serwletu nie trzeba prosić o pomoc innej osoby odpowiedzial­ n� za deskryptor wdrożenia (lub ryzykować, że przy samodzielnym wprowadzaniu zmiany powstanie konflikt w pliku web.xml na repozytorium). Jak widać, w Javie EE każdy może tworzyć aplikacje w wygodny dla siebie sposób.

JSP -

HTML

+

Java

Serwlety to niezwykle uniwersalne i potężne narzędzie do tworzenia mechanizmów obsługi żądań i odpowiedzi. Zalety te wynikają głównie z tego, że serwlety są zwykłymi klasami Javy spełniającymi niezbyt restrykcyjne warunki (więcej na ten temat w roz­ dziale 2.). Dzięki temu mogą one generować rozmaite dane - binarne, tekstowe, XML. W praktyce w zdecydowanej większości przypadków serwl� służą do generowania plików (X)HTML. Uniwersalne podejście staje się w tym ��dku problemem, po­ nieważ klasy serwletów same w sobie nie dysponują �ch� mami szybkiego i pro­ c1zą jednak pliki JSP, czyli stego generowania tego rodzaju treści. Z pomocą p JavaServer Pages.

��



�y � �

Pliki JSP w dużej mierze przypominają pli� e w innych technologiach interneto­ tvro wych, takich jak PHP, ASP.NET czy Djan� · 'lOwią one miksturę zwykłych znaczni­ ków języka HTML oraz konstrukcji char�stycznych dla JSP. Konstrukcje te są dy­ namicznie przekształcane po stronie s�a na tekst lub znaczniki HTML, dzięki czemu Ykod HTML. końcowy użytkownik otrzymUj





Strony JSP - będące swoistym szerzeniem mechanizmu serwletów - same również stanowią podstawę dla i.&Lr1Vc� technologii odpowiedzialnych za prezentację, m in JSF Chociaz obec rzy się stron wyłącznie w technologii JSP, jej znajomość �: (cie wykorzystywania bardziej złozonych frameworków przydaje się nawet �

� ��

JPA - czas �dane!

Pogaduszki o serwletach i stronach JSP są przyjemne i w pewien sposób rozwijające, jednak mało kto będzie w stanie tworzyć praktyczne, komercyjne aplikacje, jeśli nie będą one mogły korzystać z danych - a najlepiej z baz danych. Rozwiązaniem tego problemu jest jedna z technologii JEE, niewchodząca jednak w skład technologii we­ bowych - mowa tu o JP A (Java Persistence API).

JP A to technologia, która zapewnia uniwersalny dostęp do źródeł danych bez wzglę­ du na faktycznie stosowaną technologię bazodanową (oczywiście w pewnym ograni­ czonym zakresie). Oczywiście JPA samo w sobie jest jedynie interfejsem, specyfikacją, która określa sposób komunikacji ze źródłami danych. W normalnych aplikacjach trzeba korzystać z istniejących dostawców implementacji tego API. Najczęściej stosowanym do tego celu frameworkiem jest Hibernate.

Część I • Podstawy

16

Co ważne,Hibernate może być stosowane nie tylko w aplikacjach biznesowych; nic nie stoi na przeszkodzie, aby korzystać z niego w ramach aplikacji webowych, a nawet aplikacji okienkowych (tworzonych w J2SE)!

JSF - wyższy poziom prezentacji Istotne miejsce w niniejszej książce zajmuje także technologia JSF. Jest to technolo­ gia prezentacji, która pozwala na skoncentrowanie wysiłków projektantów stron in­ ternetowych na technologiach widoku. Programiści z kolei mogą skupić się na pisaniu mechanizmów logiki aplikacji. Sporo mechanizmów, które w aplikacjach korzystają­ cych z serwletów i JSP są tworzone ręcznie, w JSF zostało poddanych automatyzacji. Dzięki temu tworzenie aplikacji w JSF przebiega sprawniej i prościej.

Facelets JSF kontroluje to, co dzieje się w warstwie prezentacji, i łączy ją z pozostałymi ele­ mentami aplikacji, jednak za prezentację sensu stricto odpowiada osobna technologia. Przed JSF w wersji 2 było to JSP,jednak od wersji JSF 2 postanowiono wykorzystywać w tym celu framework Facelets. Przyjrzymy się mu z bliska, co więcej, będziemy go wykorzystywać w przykładowych aplikacjach.

Rozdział 2.

Pierwsza aplikacja webowa Mam nadzieję, że treść poprzedniego rozdziału nie przeraziła Cię zbytnio. Chciałem Ci jedynie przedstawić zarys technologii,z którymi przyjdzie nam się zetknąć w tej książce. Po lekturze niniejszego rozdziału z pewnością nabierzesz przekonania, że tworzenie apli­ kacji webowych nie jest tak trudne, jak mogłoby się wydawać. Przed rozpoczęciem pracy należy skonfigurować własne środowisko pracy. Przede wszyst­ kim musisz zainstalować serwer aplikacji. W tym momencie potrzebny Ci będzie je­ dynie Apache Tomcat (opis jego instalacji i niezbędnej konfiguracji znajdziesz w do­ datku A). Teoretycznie serwer jest jedynym wymaganym elementem, trudno przecież sobie wyobrazić,abyś aplikacje webowe składające się z wielu plików tworzył w Notatni­ ku i kompilował je ręcznie z poziomu konsoli. Dlatego skorzystamy z Netbeans IDE (ang. Integrated Development Environment - zintegrowane środowisko programi­ styczne) - jednego z dwóch najpopularniejszych środowisk open source dla technologii javowych. Najnowszą wersję środowiska Netbeans znajdziesz na stronie http://netbeans. org/ downloads/index.html. Sprawdź uważnie, jaką wersję pobierasz, ponieważ do różnych technologii przeznaczone są różne wersje tego środowiska. Nas interesuje wersja o nazwie Java, obsługująca zarówno wszystkie trzy główne platformy javowe (SE, ME, EE), jak i zintegrowane serwery aplikacji. W tej paczce znajdziesz także serwer Apache Tomcat, jednak moim zdaniem warto pobrać i zainstalować osobną wersję tego serwera,ponieważ: •

Serwer pobrany bezpośrednio u źródła będzie zawsze bardziej aktualny od serwera umieszczonego w dużym,zbiorczym instalatorze.



Serwer pobrany wraz z Netbeansem integruje się z nim automatycznie. W przypadku wielu aplikacji jest to zaleta,jednak w ten sposób pomija się istotny w przypadku serwerów etap pracy, czyli konfigurację i zapoznanie ze sposobem działania.



Serwer pobrany z Netbeansem tworzy automatycznie własną kopię plików konfiguracyjnych (więcej na ten temat w dodatku A),co może powodować pewne problemy.

Część I • Podstawy

18

W niniejszej książce wszystkie operacje wykonywane są na środowisku Netbeans

w wersji 8.0.2. Chociaż do czasu ukazania się tej książki pojawią się zapewne nowe wer­ sje tego środowiska, różnice w działaniu większości mechanizmów nie powinny być szczególnie istotne. W paeciwieństwie do środowiska Eclipse,gdzie instałacja sprowadza się do rozpakowania

archiwum ZIP, w przypadku Netbeansa mamy do czynienia z normalnym instałatorem. W trakcie instałacji warto zwrócić uwagę na kilka szczegółów. Faede wszystkim przyj­

rzyj się folderom instałacyjnym serwerów,które zostaną zainstałowane wraz ze środowi­ skiem. Mimo że serwery te zostaną zintegrowane ze środowiskiem, są to normalne, funkcjonalne aplikacje, które mogą też działać bez niego.

Integrowanie Tomcata z Netbeansem Zainstałowałeś już środowisko, skonfigurowałeś niezależnego Tomcata (pam dodatek A), nadszedł więc czas, aby połączyć ze sobą obie aplikacje. Zakładam, że Tomcat został zainstalowany w katalogu C: Itamcat8 . Aby zintegrować obie aplikacje, wykonaj po­ niższe kroki: 1. Uruchom środowisko Netbeans. 2. Z menu Taals wybiea opcję Servers.

3. Kliknij przycisk Add Server. Jeśli uruchomiłeś środowisko po raz pierwszy,

może pojawić się okno Activating Java Web and EE. 4.

Z listy dostępnych serwerów wybierz opcję Apache Tamcat ar TamEE (rysunek 2. 1 ).

Rysunek 2.1. Lista serwerów aplikacji

o Add Server Ins1ance Steps

Choose Server

1. 2.

�erver:

Choose Server ...



Tomcct or Toma::

G1assFish Sen/er

JBoss App!ication Server Orade WebLogic Server

�ame:

!Apache Tomca! or Tonfi

Rozdział 2 . • Pierwsza apli kacja webowa

19

5. W następnym oknie musisz skonfigurować serwer (rysunek 2.2). Na początku

określ katalog, w którym znajduje się Tomcat. Następnie upewnij się, że pole Use Private Configuration Folder (Catalina Base) jest odznaczone. W polu Username wpisz wartość admin, a w polu hasło haslo. Upewnij się, że pole Create user ifit does not exist jest zaznaczone. Kliknij Finish. -

o Add Server Insłance Installation and Login Oetails

Steps

1. 2.

n (Catakla m Llc--'-:\to__c._t8

Spedfy !he Server l0G3tio

Oloose Server Insta llation and Login Details

�er localion:

Home) and � delail,

---'1 [

_ _ _ _ _ _ _ _ _ _ _ _

LI U�e Pnvate Coofiguration Folder (Calalina Base) C!ltalina Base:

L

an tad i n :;= m == =====: �I._ Loo_o0..c.1

Enter the credentials ot Y,sername: e.asswor d :

existng user in the manager or

manager-script

-=:::J [

B[Olhlse

•••

Br�wse . . .

role

-' ___

� ereate user [fil does not e>óst

/ ąack

II

Next >

I[

Eilish

I[

CaoceI

I�

Rysunek 2.2. Globalna konfiguracja serwera

6. Upewnij się, że Server Port i Shutdown Port w oknie Servers mają wartości

odpowiednio 8080 i 8005. Kliknij Close. W tym momencie konfigurowanie zupełnie nowego serwera można uznać za zakończone. Gdybyś w punkcie 5 . zaznaczył opcję Use Private Configuration Folder, mógłbyś wyodrębnić folder, gdzie będą zapisywane ustawienia Tomcata dla aplikacji webowych, które będziesz tworzyć w Netbeansie. To rozwiązanie ma swoje zalety, ale czasami prowadzi do specyficznych błędów, ponieważ tworzą się różne wersje plików konfi­ guracyjnych. Musisz więc pamiętać, które pliki należy zmienić, aby Twoja aplikacja działała poprawnie (problemy te ujawnią się, gdy będziemy tworzyli konkretne, przy­ kładowe aplikacje).

Pierwsza aplikacja Pierwszą aplikacją webową, którą przyjdzie nam wspólnie utworzyć, będzie lekko zmody­ fIkowany klasyk programistyczny, czyli aplikacja Rello, World! Zmiana będzie pole­ gała na wprowadzeniu dynamicznie generowanej treści. Użytkownik wpisze w pole tekstowe swoje imię, a po kliknięciu przycisku zostanie przeniesiony do strony z napisem

Witaj, , na tym świecie!

20

Część I • Podstawy

Pracę rozpoczniemy od utworzenia szkieletu projektu. Uruchom Netbeans i wykonaj poniższe kroki: 1. Z menu File wybierz opcję New project.

2. Wybierz element Java Web, a następnie zaznacz opcję Web Application i kliknij Next. 3. Zmień nazwę aplikacji (pole Project Name) na Hel/o World. Kliknij Next. 4. Z listy

Server wybierz pozycję Tomcat 6. O. Upewnij się, że z listy Java EE Version została wybrana pozycja Java EE 5.

S. Kliknij przycisk Finish (rysunek

2.3 ). Nasza aplikacja nie będzie wykorzystywała dodatkowych frameworków ani bibliotek, dlatego krok 4. kreatora możemy pominąć.

Steps

Server and Setting:5

l. 2. 3. 4.

Ańrł tn Fntrorpric;P A�it"".:IJtitv1:

Choose Project Name and localion Server and Se:ttings

Frameworlcs

L..: I " ) ; out . pri ntl n ( " " ) ; out . pri ntl n ( " " ) ; out . pri ntl n ( "Servl et Hel l oServl et" ) ; out . pri ntl n ( "" ) ; out . pri ntl n ( " " ) ; out . pri ntl n ( "W i taj , " + l og i n + " na tym swi eci e l " ) ; out . pri ntl n ( "" ) ; out . pri ntl n ( " " ) ;

@Over r i de protected v o i d doGet ( HttpServl etRequest request , HttpServl etResponse respon s e ) throws Servl etExcepti on , IOExcepti on processRequest ( request , response ) ;

@Over r i de protected v o i d doPost ( HttpServl etRequest request , HttpServl etResponse response ) throws Servl etExcepti on , IOExcepti on processRequest ( request , response ) ;

@Over r i de publ i c Stri ng getServ l et lnfo ( ) return "Short descripti on" ;

6. Kliknij prawym przyciskiem na obiekcie Hello World z widoku Projects,

a następnie wybierz opcję Build. Po zakończeniu kompilacji z tego samego menu wybierz opcję Run.

Rozdział 2 . • Pierwsza apli kacja webowa

25

7. W trakcie uruchamiania aplikacji może pojawić się komunikat o konieczności

odblokowania serwera w ustawieniach Twojej zapory sieciowej . Efekt działania aplikacji webowej widać na rysunkach 2.7 i 2.8. Rysunek 2.7.

Widok działania strony index.html

� lODa supply a title

x

� loca lhost:8080/HelloWorld/

\

+ '--------------,

-c

I I C\.

Szukaj

Wprowadz imie:

IKrzysztof

Rysunek 2.8.

Widok działania serwletu HelloWorldjava

I�

� SElVlet HelloServlet

x

\

+

+ � localhost8080/HelloWorld/Hello.do

C

I I C\. Szukaj

I 'Ci !SI

..

»

Witaj , Krzysztof, na tym swiecie !

Po takiej dawce kodu i instrukcji niezbędne jest wyjaśnienie zaprezentowanych przed chwilą zagadnień. Jak już wspomniałem, serwlet to klasa, która dziedziczy po klasie j avax . serv l e t . http . HttpServl et. Co ciekawe, formalnie rzecz biorąc, nie ma żadnych wymagań dotyczących przesłanianych (adnotacja @Overr; de) metod. W praktyce cały sens działania serwletów opiera się na metodach do*, takich jak doGet ( )

czy doPost ( ) . Metody te s ą wywoływane wtedy, gdy dany serwlet otrzymuje adekwatne żądanie HTTP. Środowisko Netbeans ułatwia nieco programowanie, ponieważ niezależ­ nie od rodzaju żądania (GET lub POST) zawsze wywoływana jest metoda processRequest ( ) . Nie jest to jednak metoda wymuszana, czy nawet opisywana paez specyfikację technologii Java SelVlet - to tyłko udogodnienie wprowadzone paez twórców Netbeansa! Szcze­ góły dotyczące samych metod, parametrów i sposobów wywołania znajdą się w na­ stępnym rozdziale. Po wysłaniu formularza ze strony index. html trafiliśmy do serwIetu, a dokładnie do jego metody processRequest ( ) . Na początku określamy typ MIME nagłówka (rodzaj treści, jaką serwer przesyła do klienta, np. obrazek, film, dźwięk, tekst, aplikacja itd.),

26

Część I • Podstawy

a następnie pobieramy obiekt zapisujący klasy Pri ntWri ter. To właśnie za jego pomocą zapisujemy treść, która zostanie przesłana do klienta. Standardowy szablon serwletu zmodyfIkowaliśmy o dwie instrukcje. Skorzystaliśmy z dostarczonego nam obiektu klasy HttpServl etRequest, aby pobrać parametr przesłany w formularzu (parametr imie), a na­ stępnie przekazaliśmy go do strumienia. W ten sposób dynamicznie zmieniliśmy za­ wartość strony generowanej przez serwlet. Na zakończenie omawiania aplikacji muszę zasygnalizować jedno ważne zagadnienie. W jaki sposób serwer aplikacji skojarzył adres URL serwletu (Hel/o. do) z konkretną klasą go obsługującą (Hel/o World. class)? W przykładowym kodzie skorzystaliśmy z ad­ notacji @WebServl et, która wiąże klasę serwletu z wzorcem adresu URL. Istnieje również możliwość wprowadzenia deklaracji w kodzie pliku /WEB-INF/web.xml: Hel l oServl et pl . hel i on . j eeweb . Hel l oServl et Hel l oServl et< / servl et- name> /Hel l o . do

Powyższy fragment pliku web.xml jest odpowiedzialny za powiązanie adresu /Hel/o. do z serwletem Hel l oServl et. Dokładne wyjaśnienie tego fragmentu znajdziesz już w na­ stępnym rozdziale.

Rozdział 3.

Serwlet - na dobry początek Aplikacja z poprzedniego rozdziału wprowadziła kilka istotnych elementów, których omawianiem zajmiemy się w najbliższych trzech rozdziałach. Rozpoczniemy od pod­ stawy podstaw, czyli selWIetu, który jest wykorzystywany pośrednio lub bezpośrednio we wszystkich aplikacjach webowych. SelWlet, czyli klasa rozszerzaj ąca możliwości selWera aplikacji, może być traktowany jako pojęcie niesłychanie ogólne. Praktycznie jedynym istotnym wymaganiem stawianym selWIetomjest działanie w trybie żądanie - odpowiedź, czyli selWlet powinien generować treść odpowiedzi na podstawie tego, co otrzyma w żądaniu. W poprzednim rozdziale spotkałeś się z jednym z typowych zastosowań selWletów, czyli generowaniem kodu HTML. Nie jest to jednak w żadnym razie kres ich możliwości, gdyż nic nie stoi na przeszkodzie, aby za pomocą selWletów generować zarówno dane tekstowe (np. w forma­ cie XML), jak i dane binarne (np. pliki wykonywalne, obrazy itp.). Zanim jednak poznasz praktyczne przykłady (pielWszy z nich mogłeś przeanalizować w poprzednim rozdziale), należy Ci się krótkie wprowadzenie teoretyczne, z którego dowiesz się, jak selWlet współ­ pracuje z selWerem aplikacji, a także jakie podstawowe opcje związane z selWletami można ustawić w pliku web.xml.

Życie serwletu Gdy w swoim systemie operacyjnym uruchamiasz zwykłą aplikację (graficzną lub kon­ solową), możesz w większości przypadków określić precyzyjnie, kiedy jej działanie się rozpoczyna, a kiedy kończy. W przypadku popularnych technologii dynamicznych stron internetowych (np. PHP) pliki są inteIpretowane na bieżąco (aczkolwiek istnieje możliwość ich pośredniej kompilacji). Jak można opisać cykl życia selWIetu? Zacznij my od klasy w takiej postaci, jakąjuż znamy, czyli od nieskompilowanego kodu źródłowego. Zanim selWer zostanie uruchomiony, wszystkie pliki klas muszą zostać

28

Część I • Podstawy

poddane kompilacji. Powstałe pliki (o rozszerzeniu . class) są kopiowane do odpowied­ nich 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 8) 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 po prostu inicjalizacja samego serwera, a także niektórych ustawień całej aplikacji webowej . Sam serwlet pozostaje jednak nienaruszony. Cała zabawa zaczyna się wtedy, gdy dowolny użytkownik Twojej aplikacji po raz pierwszy spróbuje skorzystać z serwera, który wykonuje wówczas następujące czynności: •

załadowanie klasy serwIetu,



utworzenie instancji serwIetu,



wywołanie metody i n i t ( ) ,



wywołanie metody serv i ce O .

Gdy serwlet znajdzie się w trakcie wywoływania metody servi ce O , 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 O , np. G ET/doGet O , POST/do Post O itd. Sporo pracy, nieprawdaż? Na szczęście do obowiązków programisty nałeży obsługa wy­ branych metod ze słowem do w nazwie. Jeśli więc chcesz, aby serwlet obsługiwał tyłko żądanie GET, zadeklaruj jedynie metodę doGet ( ) . W przypadku klasy serwletu wygenerowanej 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 G ET i POST. Z tego względu w klasie serwletu są przesłaniane dwie metody doGet O i doPost O , które odwołują się do metody o nazwie proces s Request O . Z jednej strony ułatwia to pracę w większości sytuacji, z drugiej jednak mogą się zdarzyć sytuacje, w których inaczej chcemy zareagować w przypadku żądania G ET, a inaczej w przypadku POST. W takiej sytuacji należy usunąć wygenerowany mechanizm i napisać własne metody obsługi doGet O i/lub doPost O . -

Serwlet pod lupą Przed chwilą poznałeś przepływ sterowania w serwlecie; najwyższa więc pora, abyś za­ poznał 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 poja­ wiać także w dalszych przykładach, ilustrujących kolejne omawiane technologie. Jak już wspomniałem, serwIety, którymi zajmujemy się w niniejszej książce, dziedzi­ czą po klasie HttpServl et. Ponieważ serwlet z założenia jest konstrukcją niezwykle uniwersalną, w hierarchii dziedziczenia pojawiają się dodatkowe elementy, które ową

Rozdział 3 . • Serwlet - na dobry początek

29

uniwersalność wprowadzaj ą. Oto krótki opis elementów hierarchii dziedziczenia, po­ cząwszy od tych najbardziej ogólnych: •

Interfej s Servl et - określa najważniej sze metody, które muszą być implementowane przez wszystkie serwlety. Metody te są niezależne od stosowanych protokołów przesyłania danych i dotyczą głównie zarządzania cyklem życia serwletu ( i n i t ( ) , serv i ce ( ) , destroy () ).



Abstrakcyjna klasa Generi cServl et - podstawowa implementacja interfejsów Servl et i Servl etCon fi g, która daje dostęp do parametrów i ustawień serwIetu. Klasa ta zawiera proste implementacje metod obu interfejsów, dzięki czemu stanowi podstawę dla klasy HttpServl et i wszystkich innych klas serwletów.



Klasa HttpServl et - to właśnie ta klasa, po której będziesz dziedziczył, tworząc własne serwIety. Poza własnymi implementacjami metod ze wspomnianych wcześniej interfej sów klasa HttpServl et udostępnia metody do*, czyli doGet ( ) , doPost () itd. 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: G ET, POST, PUT, H EAD, OPTI ONS, TRACE, DE LETE, CONNECT. Serwlety mogą obsługiwać wszystkie metody na wyżej omówionej zasadzie. W praktyce jednak zdecydowanie najczęściej stosuje się metody GET i POST, dlatego to na nich skupimy się w dalszej części tego rozdziału.

Żądanie - odpowiedź Mimo niewątpliwie istotnej roli klasy HttpServl et w trakcie pracy z serwletami częściej przyjdzie Ci zapewne korzystać z interfejsów HttpServl etRequest/HttpServl etResponse. Reprezentują one odpowiednio obiekty żądania i odpowiedzi, przekazywane do me­ tod doGet ( ) , do Post ( ) itp. Pełny nagłówek metody doGet ( ) wygląda następująco : protected v o i d doGet (Ht tpServl etRequest req , HttpServl etResponse resp) t h rows �Servl e t Excep t i o n , j ava. i o . I OExcepti on

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. Zacznij my od interfejsu HttpServl etRequest to na jego podstawie będziemy w kolejnych przykładach generować odpowiedzi prze­ syłane za pomocą interfej su HttpServl etRespon se. Niemal wszystkie metody HttpServl etRequest są faktycznie przydatne, ale w tym miejscu omówimy jedynie metody najistotniej sze z punktu widzenia samych serwletów: •

Obj ect getParameter (St r i n g nazwa) - pobiera parametr o danej nazwie przesłany w żądaniu.



Enumerati on get ParameterNames ( ) - pobiera nazwy wszystkich parametrów znajdujących się w danym żądaniu.

30

Część I • Podstawy



String get RemoteUserO - zwraca login uwierzytelnionego użytkownika lub nul l w przypadku braku uwierzytelnienia.



Cooki e [J getCoo ki es O - zwraca tablicę ciasteczek, czyli specjalnych plików przechowywanych na komputerze użytkownika.



String getQuerySt r i ng O - zwraca łańcuch parametrów, przesłanych w adresie URL (za znakiem zapytania).



String get Header(String nazwa) - zwraca wartość nagłówka HTTP o podanej nazwie.



i nt get Int Header(String nazwa) - zwraca wartość nagłówka HTTP o podanej nazwie jako liczbę całkowitą.



l ong getDateHeader (St r i ng nazwa) - zwraca wartość nagłówka HTTP o podanej nazwie jako liczbę milisekund, począwszy od początku epoki ( l stycznia 1 970 roku). Wartość ta może być przekazana w konstruktorze klasy Date.



String getContext Path O - zwraca ścieżkę kontekstu aplikacji.



String getServl etPath O - zwraca ścieżkę dostępu do serwIetu.



String get Pat h l n fo O - zwraca dodatkowe informacje zawarte w ścieżce.

Trzy ostatnie metody są ze sobą związane, ponieważ zwracają kolejne elementy adre­ su URL, wykorzystanego do wykonania żądania. Przeanalizujmy poniższy przykład, prezentujący wiadomość o określonym identyfikatorze: h t t p : //l ocal hos t : 8080/MojaApl i kacja/serwl ety/ i nfo/235/

Pomijamy oczywiście nazwę protokołu (http) i nazwę serwera z portem (localhost:8080). Zostaje nam więc ciąg: /MojaApl i kacja/serwl ety/ i n fo/235/ Metoda getContextPath ( ) zwraca fragment adresu okreś l aj ący naszą apl i kacj ę : /MojaApl i kacja

Ś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 - wtedy zwracana wartość to łańcuch pusty. Fragment ten jest wspólny dla wszystkich plików wchodzących w skład tej aplikacji. Kolejny fragment adresu określa ścieżkę do serwIe­ tu. W naszym przypadku jest to fragment: /serwl et y /i n fo

Powyższy łańcuch znaków musi pasować do odpowiednich wzorców zdefiniowanych w deskryptorze wdrożenia (pamiętasz znacznik z poprzedniego roz­ działu?). Zasady określania odpowiednich ścieżek do serwletów omówimy w następ­ nym rozdziale; na razie niech wystarczy Ci informacja, że ten fragment adresu umoż­ liwia jednoznaczne zidentyfikowanie serwIetu.

Rozdział 3 . • Serwlet - na dobry początek

31

Ostatni fragment ścieżki (/23 5/) zostanie zwrócony przez metodę get Pat h l n fo O . Do­ kładnie rzecz ujmując, metoda getPath lnfo O zwraca fragment adresu URL od ścieżki selWletu 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. h t t p : //l oeal hos t : 8080/MojaApl i kaeja/serwl ety/ i nfo/235? param= l

Przesyłanie odpowiedzi Po przeanalizowaniu wszystkich możliwych atrybutów żądania musisz odesłać klientowi odpowiedź. Do tego celu służy obiekt interfejsu HttpServl etResponse. W jego przypadku otrzymujemy nieco mniej szy 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 wy­ korzystaniem strumieni i/lub obiektów klas Reader/Wri ter. Nie inaczej jest w tym przy­ padku: zanim prześlemy jakiekolwiek dane, musimy uzyskać odpowiednie obiekty zapisujące: •

Servl etOutputStream getOutputStream O binarnych,



Pri ntWr i ter getWr i ter O

-

-

zwraca strumień zapisu dla danych

zwraca obiekt zapisujący dla danych tekstowych.

W przypadku danych binarnych możemy skorzystać z obiektu klasy Servl etOutputStream. Jest to zwykły strumień zapisu, rozszerzony o możliwość zapisywania dowoillych danych typów prymitywnych, a także łańcuchów znaków (za pomocą metod print O i printl n ( ) ). Z tej klasy należy korzystać w przypadku przesyłania plików, jeśli selWer musi w dy­ namiczny sposób wygenerować treść takiego pliku.

Znacznie częściej przyjdzie Ci jednak korzystać z danych tekstowych. W tym przypadku zazwyczaj będziesz korzystać z obiektu klasy Pri ntWr i ter i j ego metody pri ntl n O .

N agłówki i ciasteczka O ile w przypadku żądania manly do czynienia z odczytem nagłówków i ciasteczek prze­ słanych przez klienta, o tyle w przypadku odpowiedzi występuje proces odwrotny. Aby dodać ciasteczko, wystarczy skorzystać z metody addCoo k i e O : vo i d addCoo k i e (Coo k i e ej

32

Część I • Podstawy

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) : vo i d addHeader (String nazwa , Stri ng wartość) voi d setHeade r ( Stri ng nazwa , Stri ng wartość )

Na czym polega różnica? Otóż metoda addHeader O doda podaną wartość do już istnie­ jącej zawartości nagłówka, natomiast metoda setHeader O zastąpi wartość, jeśli takowa już istnieje. Tak samo działają bliźniacze metody add lnt Header O , addDateHeader O , set I nt Header O i set DateHeade r O .

Kody odpowiedzi, błędy i przekierowania D o 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 O : vo i d setStatus ( i n t kod)

W ten sposób przekazujemy kody, które nie określają sytuacj i problematycznych. W przypadku błędów (np. 404 - brak zasobu) zaleca się zdecydowanie wykorzysty­ wanie metody sendError O : vo i d send Error ( i n t kod) voi d sendError ( i nt kod , Stri ng komuni kat )

Jak widać, istnieje możliwość przesłania dodatkowej informacji na temat samego błędu. Ostatnią funkcjonalnością związaną z kodami odpowiedzi jest przekierowanie. Chociaż z technicznego punktu widzenia przekierowanie też jest rodzajem kodu odpowiedzi, do przekierowania wykorzystuje się oddzielną metodę: vo i d sendRed i rect (String adre s )

Podczas korzystania z metod sendError O i sendRed i rect O należy pamiętać o subtel­ nych kwestiach związanych z fizycznym przesyłaniem danych do klienta. Przesyłanie komunikatów o błędach lub przekierowań wiąże się z dość brutalną ingerencją w proces wysyłania odpowiedzi. Proces ten jest natychmiast przerywany, a klient otrzymuje od­ powiedź z wybranym kodem odpowiedzi. Co się jednak stanie, gdy zdążymy wysłać do klientajakieś dane? Odpowiedź jest prosta - nastąpi błąd. Po wysłaniu danych nie możesz ingerować w treść nagłówków, nie możesz więc ustawić kodu odpowiedzi, a co za tym idzie także przekierowania. Czy oznacza to, że musisz uważać, gdzie wywołujesz metodę printl n O obiektu Pri ntWr i ter? 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 przypadku wywołania metody fl ush O tego obiektu. Dzięki temu poniższa konstrukcja (wewnątrz metody doGet ( ) ) nie spowoduje wygenerowania błędu:

Rozdział 3 . • Serwlet - na dobry początek

33

=

Pri ntWri ter out response . getWri ter ( ) ; out . pri ntl n ( "test" ) ; response . sendRedi rect ( "url /do/i nnego/ serwl etu" ) ;

Błąd natomiast wystąpi, jeśli przed wywołaniem metody sendRed i rect () wywołasz metodę out . fl ush ( ) . Zazwyczaj jednak takie wywołanie jest pomij ane, dzięki czemu problem występuje stosunkowo rzadko.

Om nom nom , czyli ciasteczka w pełnej krasie Twórcy aplikacji webowych mają (podobnie jak Ciasteczkowy Potwór) szczególny sen­ tyment do ciasteczek (ang. cookies). Są to niewielkie pliki przechowywane na kompute­ rach użytkowników aplikacji webowych. Dzięki nim jesteśmy w stanie zapamiętywać preferencje użytkowników, ich loginy, hasła itd. Metody operujące na ciasteczkach poznaliśmy w poprzednim podrozdziale, a teraz zaprezentujemy ich działanie w prak­ tycznym przykładzie (listing 3 . 1) : Listing 3.1. Przykład obsługi ciasteczek

protected voi d proces sRequest( HttpServl etRequest request , HttpServl etResponse respon s e ) throws Servl etExcepti on , IOExcept i on { response . setContentType ( "text!html ; charset=UTF - 8 " ) ; Pri ntWri ter out = response . getWri ter ( ) ; try { Cook i e l astVi s i t = nul l ; for ( Cook i e c : request . getCook i es ( ) ) i f ( c . getName ( ) . equal s ( " obecnosc " ) ) { l a stV i s i t = c ; break ;

}

i f ( l a stVi s i t 1 = nul l ) out . p r i ntl n ( "Twoja ostatni a wi zyta na stro n i e m i a ł a m i ej sce w dni u " + l astVi s i t . getVa l ue ( ) ) ; el se out . p r i nt l n ( "Do tej pory nie odwi edzi ł e ś / a ś naszej strony . Wstydź S i ę l " ) ; l astVi s i t = new Cooki e ( " obecnosc " , new Date ( ) . toStri ng ( ) ) ; response . addCoo k i e ( l a stVi si t ) ; } fi na l l y { out . cl os e ( ) ;

}

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 tu zwrócić uwagę na dwie kwestie. Po pierwsze, jeśli chcemy odczytać już istniejące ciasteczka, korzystamy z metody getCoo k i es ( ) znajdującej się w obiekcie request. Jeśli natomiast chcemy dodać ciasteczko, korzystamy z obiektu response. Nigdy odwrotnie! Sprawa druga, znacznie bardziej przykra - powyższy sposób dostępu do ciasteczek użytkownika

34

Część I • Podstawy

(pętla for . . i n) 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 Kolejnym nieodłącznym elementem niemal wszystkich aplikacji webowych jest obsłu­ ga sesji. W przypadku JEE interakcje z sesją możemy prowadzić na różne sposoby, także za pomocą poznanych już klas. W tym rozdziale poznamy sposób na dostęp do sesji za pomocą obiektu klasy HttpServl etRequest. Kluczową rolę odgrywa tu metoda getSess i on ( ) , występująca w dwóch wariantach: HttpSess i on getSe s s i on ( ) HttpSes s i on getSe s s i on (bool ean czyTworzy c )

Na wstępie zaznaczę, że pierwszy wariant tej metody jest równoważny drugiemu wy­ wołanemu z parametrem t rue. Natomiast drugi wariant w zależności od przekazanej wartości logicznej zachowuje się różnie: •

Jeśli parametr ma wartość t rue, metoda zwraca obiekt sesji lub tworzy nowy, jeśli ten nie istnieje .



Jeśli parametr ma wartość fal se, metoda zwraca obiekt sesji lub n ul l , jeśli ten nie istnieje.

Jak widać, jeśli chcesz po prostu uzyskać dostęp do sesji, powinieneś przekazać wartość true. Gdy natomiast chcesz sprawdzić, czy sesja istnieje, musisz zastosować wartość fal ­ se. Można skorzystać z tego mechanizmu, aby sprawdzić, czy to żą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 voi d proces sRequest( HttpServl etRequest request , HttpServl etResponse respon s e ) throws Servl etExcepti on , IOExcept i on { response . setContentType ( "text!html ; cha rset=UTF - 8 " ) ; Pri ntW r i ter out = response . getW r i ter ( ) ; try { i f ( request . getSes s i o n ( fa l s e ) ==nul l ) { out . p r i ntl n ( " W i taj na stron i e po raz p i e rwszy l " ) ; request . getSes s i o n ( ) ; el se out . p r i ntl n ( " W i taj na stron i e po raz kol ejny l " ) ; fi nal l y { out . cl os e ( ) ;

Rozdział 3 . • Serwlet - na dobry początek

35

Jeśli po utworzeniu sesji chcesz sprawdzić, czy sesja została dopiero co utworzona, skorzystaj z metody i sNew O : •

boo l ean i sNew O - 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: • •

Obj ect getAtt r i bute (St r i n g nazwa) - zwraca atrybut sesji o podanej nazwie. voi d setAttri bute (Stri ng nazwa . Obj ect wartość) - dodaje obiekt do sesji, przypisując mu podany klucz (nazwę). Jeśli jakiś obiekt o takiej samej nazwie już istnieje, zostanie on zastąpiony.

Wiemy już, jak utworzyć sesję, wiemy też, jak z niej skorzystać. Pozostało nam omówie­ nie, jakie są warunki zakończenia sesji. Może ono nastąpić poprzez: •

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ęcz­ ne zakończenie sesji wiąże się z wywołaniem metody : •

vo i d i nval i date O - kończy sesję.

Najciekawiej sytuacja wygląda w przypadku określania terminu ważności sesji. Ist­ nieją bowiem dwie możliwości określenia tej wartości - pierwsza z nich jest stoso­ wana w pliku konfiguracyjnym web.xml: lO

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 : •

vo i d setMaxl nact i ve l nterval ( i nt czas) - określa czas życia sesji w sekundach. Podanie wartości ujenmych lub zera powoduje, że sesja nigdy nie wygasa (do jej zakończenia jest więc konieczne wywołanie metody i nva l i date O lub zamknięcie okna przeglądarki przez użytkownika).

36

Część I • Podstawy

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 stalej/zmiennej . Z drugiej jednak strony zmiana takich ustawień wymagałaby rekompilacji całego projek­ tu, w najlepszym przypadku - jednej biblioteki.

Z tego względu przez długi czas standardem było umieszczanie różnego rodzaju ustawień w zewnętrznych źródłach danych - plikach binarnych, tekstowych, XML; rzadziej w ba­ zach danych. W przypadku aplikacji webowych JEE miejscem takim jest deskryptor wdrożenia, czyli plik web.xml. W pliku web.xml - poza licznymi ustawieniami zwią­ zanymi z funkcjonowaniem aplikacji jako takiej (część z nich już poznałeś) - możesz także zdefiniować parametry dla poszczególnych serwletów, a także całej aplikacji webo­ wej. Nie jest to jednak jedyna koncepcja umieszczania informacji konfiguracyjnych - z każdą kolejną wersją Javy coraz większy nacisk kładzie się na stosowanie adnotacji.

Parametry serwletów Parametry serwletów możesz określać za pomocą znacznika w następujący sposób : ParameterServl et pl . hel i on . j eeweb . ParameterServl et autor Krzysztof Rychl i ck i - K i ci or < / i n i t - param>

Po dwóch znanych już znacznikach (servl et-name i servl et-cl ass) następuje dowolna liczba znaczników i n i t -param. Każdy taki znacznik zawiera dwa kolejne, określaj ące nazwę i wartość parametru. Parametry serwletów można też dodawać w środowisku Netbeans podczas tworzenia serwletu (w ostatnim kroku kreatora). Analogiczny efekt można osiągnąć za pomocą adnotacji. W tym celu wobec klasy serwletu należy zastosować adnotację @WebServl et na przykład w następujący sposób : @WebServl et ( val ue= "jhel l o . do" , i ni tPa rams = { @Web lni tPa ram ( name= " Im i e " , val ue="Jan " ) , @Web lni tPa ram ( name= "Nazwi s k o " , val ue= " Kowal s k i " )

})

Rozdział

3. •

Serwlet - na dobry początek

37

W podobny sposób możemy przekazać wszystkie informacje, które dopuszcza deskIyptor wdrożenia. Atrybut va l ue pozwala na zdefiniowanie pojedynczego wzorca URL. Jeśli konieczne jest użycie kilku wzorców, można skorzystać z atrybutu url Pattern s. Pierwszy parametr zostal utworzony, najwyższa więc pora, aby odczytać go we wnętrzu serwIetu. Do zarządzania parametrami serwletów służy interfej s Servl etCon f i g, który jest implementowany przez znane nam klasy Generi cServl et i HttpServl et. Dwie metody tego interfej su, które interesują nas w tej chwili najbardziej , to: •

String get I n i t Parameter (St r i n g nazwa) - zwraca wartość parametru o podanej nazwie .



String [J get l n i t ParameterNames () - zwraca wszystkie nazwy parametrów danego serwIetu. protected v o i d processRequest ( HttpServl etRequest request , �HttpServl etResponse respon s e ) th rows Servl etExcept i on , IOExcepti on { response . setContentType ( "text!html ; cha rset=UTF - 8 " ) ; Pri ntWriter out = respons e . getWriter ( ) ; try { out . p r i ntl n ( "Autorem serwl etu j est " + �thi s . get l ni tPa rameter ( " auto r " ) ) ; fi na l ly { out . c l ose ( ) ;

Dzięki umieszczeniu konfiguracji w pliku XML odnieśliśmy wymierną korzyść. Zmiana 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). Poza dwiema poznanymi metodami interfej s Servl etCon f i g udostępnia metodę get �Servl etName () zwracającą nazwę serwIetu, a także metodę getServl etContext () . Zwra­ ca ona (a jakżeby inaczej) kontekst serwletów - j eden z najważniej szych obiektó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 za­ sobów znajdujących się w jej obrębie, zapisywać logi do serwerowego dziennika, a co najważniejsze z obecnego punktu widzenia - możesz korzystać z parametrów apli­ kacji webowej (kontekstu). Od parametrów serwletów różni je zasięg oddziaływania. Każdy parametr kontekstu jest widoczny we wszystkich serwletach i innych plikach. Parametry serwletu są określane w podobny sposób jak serwIety : tytul Java EE 6 . Tworzeni e apl i kacj i webowych

38

Część I • Podstawy



Również sposób wykorzystywania parametrów kontekstu przypomina ten znany z ser­ wletów: try { out . pri ntl n ( "Wszyst k i e przykł ady pochodzą z k s i ążki " + thi s . getServ l etContext ( ) . get lni tPa rameter ( "tytu l " ) ) : } fi na l ly { out . cl os e ( ) :

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, polegaj ąca na możliwości dynamicznego ustawiania parametrów kontekstu za pomocą metody set l n i t Parameter O . Wynika to z wprowadze­ nia dużej elastyczności - klasa Servl etContext w wersji 3.0 uzyskała wiele metod, takich jak addServl et O czy add F i l ter O , 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, a tymczasem nadszedł czas, aby zmierzyć się z przeciwnikiem o wiele ważniejszym od parametrów - mowa o atrybutach.

Trzech muszkieterów? Parametry, czy to serwletów, czy to aplikacji, mają swoje zastosowania i bywają niezwy­ kle przydatne. Mimo to głównym środkiem komunikacji między serwletami, kontene­ rem, sesją, użytkownikiem i obiektem żądania - czyli z grubsza między wszystkimi 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, a 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. Niezwykle 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 przedstawia zestawienie parametrów i atrybutów w poszczególnych zasięgach.

3. •

Rozdział

Tabela

39

Serwlet - na dobry początek

3.1. Możliwości zapisywania i odczytywania parametrów i atrybutów w poszczególnych zasięgach Parametry

Atrybuty

Zasięg Zapis

Odczyt

Zapis

Odczyt

Ż ądanie

me

Tak

tak

tak

Serwlet

me

Tak

brak

brak

Sesja

brak

Brak

tak

tak

Kontekst aplikacji

tak (od wersji 3 .0 )

Tak

tak

tak

Na podstawie tabeli widać 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 elementami aplikacji. W dalszej części rozdziału skupimy się tyłko na atrybutach. Ich ogronma przydatność ma bowiem pewne ograniczenia. Jedno z nich jest związane z najważniejszą chyba cechą odróżniającą aplikacje webowe od aplikacji typu standalone (uruchamianych w systemie operacyjnym użytkownika) - konieczność jednoczesnej 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żytkowników jednocześnie. Każde żądanie (HITP request) jest obsługiwane przez kontener w osobnym wątku. Istotną kwestią jest więc zapewnienie integralności operacji wykonywanych przez każdego z nich - nie może być tak, że operacje jednego użytkownika 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 realizowane przez jednego użytkownika; w dodatku pojedyncze żądanie nie wiąże się w żaden sposób z innymi żądaniami (nawet tego samego użytkownika), dlatego problem jednoczesne­ go 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, to po co miałby korzystać z atrybutów?

40

Część I • Podstawy

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 opera­ cje, np. pobranie danych z bazy, realizacja logiki biznesowej (choć w większych apli­ kacjach i te zadania są delegowane), a następnie przekazują sterowanie do pliku JSP. W takiej sytuacji konieczne jest przekazanie informacji między serwIetern a plikiem JSP. Voih't! Znaleźliśmy zastosowanie atrybutów żądania. Dokładne wyjaś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 kon­ kretnym użytkownikiem. Teoretycznie nie powinno więc być problemów. Ale użytkow­ nicy bywają okrutni - wyobraź sobie, co mogłoby się stać, gdyby użytkownik uru­ chomił Twoją aplikację webową w dwóch zakładkach i próbował jednocześnie ładować różne (lub te same) serwIety? Odpowiedź jest prosta: mogłoby dojść do jednoczesnego dostępu do sesji. Odczyt danych nie stanowiłby wprawdzie problemu, ale atrybuty sesyjne mogą być przecież również zapisywane. Taka sytuacja to potencjalny problem. Jak więc mu zaradzić? Powiem krótko : należy skorzystać ze standardowego mechanizmu Javy, czyli synchroni­ zacji. Chroni on dane przed zapisem przez wiele wątków jednocześnie. Teraz musimy dokładnie określić, co chcemy konkretnie synchronizować. Na początek odrzućmy obiekt żądania (klasy HttpServl etRequest). Jest to obiekt związa­ ny tylko z jednym, konkretnym żądaniem, więc zablokowanie dostępu do niego nie wpły­ nęłoby na inne obiekty żądań - nadal wszystkie one mogłyby korzystać bez skrępo­ wania z sesji. Nie ma sensu również blokada obiektu serwletu - dostęp do sesji mają różne serwIety, 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 (wstawiony we wszystkich serwletach) pozwoli na zliczenie wszystkich wywołań serwletów, które miały miej sce w aplikacji webowej dla danego użytkownika: =

HttpSess i on sesja reques t . getSes s i on ( ) ; synchroni zed ( sesja ) { i f ( sesj a . i sNew( ) ) sesja . setAttri bute ( " l i czni k " , l ) ; el se { i nt l i czni k Intege r . pa r s e l nt ( sesj a . getAttri bute ( " l i czni k " ) . toSt r i ng ( ) ) ; sesj a . setAttri bute ( " l i czni k " , l i czni k + l ) ; =

Dzięki temu 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ę.

Rozdział

3. •

Serwlet - na dobry początek

41

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 atrybutów (zwłaszcza zapisu) powinna być synchronizowana. Zasada dzialania jest taka sama jak w przypadku sesji. Teraz musimy jednak synchro­ nizować obiekt kontekstu. Kod synchronizujący przedstawia się następująco : =

Servl etContext sc t h i s . getServl etContext ( ) ; synchroni zed ( sc ) { Object l i czni k sc . getAttri bute ( " l i czni k " ) ; i f ( l i czni k nu 1 1 ) sC . setAttri bute ( " l i czni k " , l ) ; el se { l i czni k sc . getAttri bute ( " l i czni k " ) ; sC . setAttri bute ( " l i czni k " , Integer .parselnt ( l i czni k . toStri ng ( ) ) + l ) ; =

==

=

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 oraz z ich atrybutami wiążą się ważne klasy słuchaczy zdarzeń. Choć istnieje możliwość tworzenia całych aplikacji webowych bez świadomości istnienia tych klas, zdarzają się sytuacje, w których znajomość tego typu mechanizmów jest niezbędna.

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łucha­ czom zdarzeń spore pole do popisu. Klasy słuchaczy zdarzeń wymagają rzecz jasna niewielkiej konfiguracji. Mamy do dyspo­ zycji zarówno plik web.xml, jak i adnotacje. Zapis w pliku web.xml jest niezwykle prosty : pl . hel i on . j eeweb . S l uchaczZdarzenSesj i Http< / l i stenercl a s s>

Część I

42



Podstawy

Klasę słuchacza możemy również opatrzyć adnotacją @WebL i stener bez żadnych do­ datkowych atrybutów. W poniższych podrozdziałach przedstawię interfej sy słuchaczy zdarzeń przeznaczone do użycia w aplikacjach webowych. Nie jest to może naj ciekawszy 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 : context l n i t i al i zed () i contextDestroyed ( ) , które są wywoływane w momencie utwo­ rzenia/usunięcia kontekstu aplikacji, czyli w momencie startu i zakończenia aplikacji. Obydwie metody przyjmują parametr typu Servl etContextEvent, który umożliwia pobra­ nie kontekstu aplikacji za pomocą metody getServl etContext ( ) .

ServletContextAttributeListener Drugim słuchaczem zdarzeń związanym z kontekstem aplikacji jest słuchacz obserwujący kolekcję atrybutów kontekstu. Reaguje on na dodawanie (metoda att r i b uteAdded ( ) ), usuwanie (attri buteRemoved ( ) ) i zamianę (attri buteRepl aced ( ) ) atrybutów. Wszystkie trzy metody przyjmująjeden parametr typu Servl etContextAttri bute Event - umoż­ liwia on pobranie nazwy modyfikowanego atrybutu (getName ( ) ) oraz jego wartości (getVal ue ( ) ) .

ServletRequestAttributeListener i ServletRequestListener Obydwa interfej sy pełnią analogiczne funkcje, co ich "kontekstowi" koledzy, i nawet nazwy metod są podobne (w przypadku pierwszego interfejsu - identyczne, w przy­ padku drugiego są to metody request l n i t i al i zed () i requestDest royed ( ) ) . Jedyną realną zmianą jest wprowadzenie dodatkowej funkcjonalności do klas argumentów zdarzeń - Servl etRequestAttri bute Event i Servl etRequest Event . Udostępniają one metodę getServl etRequest ( ) . Pozwala ona na skorzystanie z obiektu żądania, którego zdarzenia dotyczą.

HttpSessionAttributeListener i HttpSessionListener Również słuchacze zdarzeń powiązane z sesjami zostały utworzone zgodnie z omó­ wionymi powyżej zasadami. Słuchacz HttpSes s i on L i stener jest wykorzystywany przy tworzeniu (metoda ses s i onCreated ( ) ) i kończeniu sesji (ses s i onDestroyed ( ) ). Przekazywany argument - obiekt klasy HttpSes s i on Event - udostępnia metodę getSess i on ( ) , która daje dostęp do utworzonej (zakończonej) sesji. W przypadku interfejsu

Rozdział

3. •

Serwlet - na dobry początek

43

HttpSes s i anAttri buteLi stener mamy do dyspozycji te same trzy metody, co w po­ przednich przypadkach Typ zdarzenia to HttpSes s i an B i n d i ngEvent. 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 mieli­ śmy do czynienia w ostatnich trzech podrozdziałach. Wszystkie trzy interfejsy z członem Attri buteLi stener w nazwie odpowiadały za informowanie o zmianach zachodzących w kolekcji atrybutów. Dla odmiany interfejs HttpSess i an B i nd i ngL i stener powinien być implementowany przez klasy, których obiekty będą umieszczane w sesji! Jeśli więc tworzysz własne klasy do przechowywania danych, które w momencie zapisu do se­ sji powinny być w jakiś sposób przetworzone, powinieneś skorzystać z tego interfejsu. Metody tego interfej su to val ueBaund () i val ueUnbaund ( ) , wywoływane odpowiednio w momencie dołączenia obiektu do sesji lub jego usunięcia. Klasa argumentu zdarzeń to znana z poprzedniego akapitu HttpSes s i an B i n d i ngEvent .

Sesja

+

wiele JVM

=

HttpSessionActivationListener

Java EE to uniwersalna technologia, która może być z powodzeniem stosowana w rozbu­ dowanych aplikacjach webowych i biznesowych. Jednym z ważnych aspektów tworzenia takich aplikacji jest skalowalność, czyli możliwość poprawnego i wydajnego działania aplikacji w dowolnych warunkach (zarówno niewielkiego, jak i bardzo dużego obcią­ żenia) bez konieczności zmiany samej aplikacji. Bardzo często ta sama aplikacja jest instalowana (w celu poprawy wydajności) na wielu komputerach połączonych w sieć, a specjalny serwer, będący bramą do świata zewnętrznego (czyli do klientów), kieruje " "ruchem 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 zawarte w deskryptorze wdrożenia. W tym podrozdziale zajmiemy się słuchaczem zdarzeń, który jest związany z dwoma istotnymi zagadnieniami: obsługą sesji i równoważeniem obciążenia. Gdy użytkownik tworzy obiekt sesji, jest on zapisywany (np. w postaci pliku) na kompu­ terze, który w danej chwili obsługiwał żądanie. Następne żądanie użytkownika może być jednak przydzielone do zupełnie innego komputera wewnątrz sieci - dlatego ko­ nieczne 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 pierwotnego i po przeniesieniu sesji na komputer docelowy. Służą do tego celu metody :

Część I

44 •



Podstawy

vo i d sess i onWi l l Pas s i vate ( HttpSes s i on Event hse) - metoda wywoływana tuż przed przesłaniem sesji do innego komputera;



vo i d sess i on D i dAet i vate (HttpSess i on Event hse) - metoda wywoływana tuż po otrzymaniu sesji od pierwszego komputera.

Filtry Do tej pory omawialiśmy wszystkie przykłady, korzystając z modelu obsługi żąda­ nia/odpowiedzi. Żądanie HTTP przesłane przez klienta powodowało utworzenie no­ wego wątku, wykorzystującego obiekt odpowiedniego serwIetu. Na podstawie metody żądania HTTP następował wybór odpowiedniej metody klasy HttpServl et (doGet ( ) , do Post () itd.). Model ten możnajednak rozszerzyć o dodatkowe elementy, jakimi są 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 serwIetu. Można zatem np. zapisać w dzienniku datę każdego żądania, jego parametry, a także sprawdzić, jakajest 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ść odpo­ wiedzi, 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ć, dodanie jednego znacznika pozwala na włączenie szyfrowania, kompresji lub kontroli dostępu dla całej grupy serwletów.

Techniczny aspekt filtrów Z technicznego punktu widzenia filtr jest klasą implementującą interfejs j avax. servl et . '-+Fi l ter. Interfejs ów składa się z trzech metod: •

vo i d i n i t ( F i l terCon fi g fe) - metoda wywoływana przy utworzeniu filtru. Pozwala na uzyskanie obiektu ustawień filtru, czyli obiektu interfejsu F i l terCon f i g.



vo i d doFi l ter (Servl etRequest reques t , Servl etResponse response , F i l terCha i n eha i n ) - metoda wywoływana w momencie nadej ścia żądania. Dokładny opis w dalszej części podrozdziału.



vo i d dest roy ( ) - metoda wywoływana przez serwer w momencie zakończenia działania filtru.

Największe znaczenie ma rzecz jasna metoda doFi l ter ( ) . Typowy sposób jej wyko­ rzystania przedstawiam poniżej :

Rozdział

3. •

45

Serwlet - na dobry początek

publ i c v o i d do F i l ter (Servl etRequest req , Servl etRespon se res , F i l terChai n cha i n ) { i f ( I req . i sSecure ( ) ) ( ( HttpServl etResponse ) res ) . sendError( HttpServl etResponse . SC_RESPONS E ) ; chai n . doFi l te r ( re q , res ) ; bool spakowany fa l se ; i f ( req . getPa rameter ( " format" ) I nu 1 1 && req . getPa rameter ( " format" ) . equa l s ( " spakowany" ) ) spa kowany true ; i f ( spa kowany ) { IIpobierz treść odpowiedzi, spakujją, a następnie II 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 proto­ kołu SSL. Jeśli nie, odsyłamy użytkownikowi kod błędu 403 , czyli forbidden (brak uprawnień do zrealizowania żądania). Najważniej szym fragmentem metody jest wywołanie do F i l te r O obiektu cha i n . Zbieżność nazw w tej sytuacji jest przypadkowa (klasy F i l terCha i n i Fi l ter nie s ą ze sobą w żaden formalny sposób związane). Zadaniem interfej su F i l terCha i n jest za­ pewnienie komunikacji między filtrem (lub grupą filtrów) a serwletem. W zależności od kolejności deklaracji filtrów w deskryptorze wdrożenia żądanie HTTP przechodzi przez kolejne filtry (dzięki wywołaniom metody do F i l te r O w każdym filtrze), aż w końcu dociera do serwIetu. Po zakończeniu obsługi przez serwlet sterowanie powraca do kolej­ nych filtrów (w naszym przypadku od deklaracji zmiennej spakowany aż do opuszczenia 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 pliku web.xml. Zasada działania filtrów przypomina tę znaną z serwletów, dlatego in­ formacje podawane w deskryptorze wdrożenia powinny Ci się wydać znajome: Pi erwszy fi l tr pl . hel i on . jeeweb . Fi l tr Pi erwszy fi l tr I*

Podobnie jak w przypadku serwletów, mamy do czynienia z dwiema głównymi sekcjami, czyli powiązaniem klasy filtru z abstrakcyjną nazwą (znacznik fi l ter) oraz połączeniem tej nazwy z adresem URL Gedno- lub wieloznacznym - znacznik fi l ter-mappi ng). Reguły podawania wartości (nazw klas, nazw abstrakcyjnych i wzorców URL) są takie same jak w przypadku serwletów.

Część I • Podstawy

46

Ciekawostkąjest możliwość podania w znaczniku fi l ter-mapp i ng znacznika d i spatcher. Pozwala on określić, czy filtr ma być stosowany także w przypadkach specjalnych. Domyślna wartość REQUEST określa, że filtr ma być stosowany tylko przy bezpośred­ nich żądaniach użytkownika. Pliki, które będą dynamicznie ładowane przez serwlety (za pomocą mechanizmu dołączania lub przekazywania), nie będą przekształcane przez filtr. Jeśli chcesz zmienić ustawienia domyślne, musisz podać wszystkie pożądane sytuacje za pomocą znaczników d i spatcher: Pi erwszy fi l tr /* REQUEST INCLUDE FORWARD ERROR

Ostatni z przypadków powoduje wywołanie filtru także przy przekierowaniach na strony błędów. Filtry można również konfigurować z poziomu adnotacji. Adnotacja @WebFi l ter przy­ pomina swą konstrukcją adnotację @WebServl et: @Web F i l t e r ( url Patterns i ni tParams

= =

"/hel l o . do" , @Web lni tPa ram ( name

=

"parametr" , val ue

=

"wartość " )

W tym przypadku filtr zostanie zastosowany dla wzorca /hello. do. Istnieje też możli­ wość powiązania filtru z serwletami o konkretnych nazwach: @WebF i l ter( servl etNames

=

( "Hel l oWorl dServl et " , " I nnyServl et " } )

Asynchroniczna obsługa żądań Nowością w SelVlet API 3 .0 jest możliwość asynchronicznej obsługi żądań. W klasycz­ nym modelu pojedyncze żądanie jest obsługiwane w ramach jednego wątku (czyli wywołanie metody doGet ( ) , doPost ( ) spowoduje utworzenie jednego nowego wątku), a dzięki asynchronicznej obsłudze żądań jesteśmy w stanie zmienić to podej ście na znacznie bardziej elastyczne. Oczywiście samo utworzenie nowego wątku w metodzie doGet ( ) nie miałoby sensu. Istotą asynchronicznej obsługi żądań jest delegowanie kluczowych zadań do mecha­ nizmów, które w dedykowany sposób zajmują się realizacją pewnej funkcjonalności. Dysponują one pulą wątków i dają znacznie większe możliwości w zakresie kontroli wykonywania operacji współbieżnie. Noszą one nazwę wykonawców (ang. executors).

Rozdział

3. •

47

Serwlet - na dobry początek

Najpierw musimy zadbać, aby klasa serwletu była odpowiednio skonfigurowana: =

=

@WebServl et ( url Pat terns { "jNewServl et " } , asyncSupported true) publ i c cl a s s NewServl et extends HttpServl et { pri vate sta t i c Executor executor = new ThreadPool Executor ( 5 , 10 , 10000 , TimeUn i t , M I L L ISECONDS , new Li nkedBl ocki ngQueue ( 10 ) ) ;

Dzięki atrybutowi asyncSupported włączamy możliwość asynchronicznej obsługi żą­ dań, W trzeciej instrukcji tworzymy obiekt wykonawcy o odpowiednich ustawie­ niach, Znaczenie poszczególnych parametrów: •

domyślny rozmiar puli wątków;



maksymalny rozmiar puli wątków;



czas nieaktywności, po jakim nadmiarowe wątki (wykraczające poza domyślny rozmiar puli) zostaną zakończone;



jednostka czasu nieaktywności;



kolejka zadań do wykonania,

Tak przygotowany wykonawca zostanie użyty w ramach znanej już metody p roce s s '-+Request ( ) , wygenerowanej przez NetBeans: protected voi d processRequest (HttpServl etRequest reques t , HttpServl etResponse response) throws Servl etExcepti on , IOExcepti on { fi nal AsyncContext ctx = request , sta rtAsync( request , respon s e ) ; executor , execute ( ( ) - > { try { Thread , s l eep (5000 ) ; catch ( I nterruptedExcepti on ex) Logger , getLogg e r ( N ewServl et , cl a ss , getName( ) ) , l og ( Level , SEVERE , 4nul l , ex ) ; HttpServl etResponse re sp = ( HttpServl etRespons e ) ctx , getRespon s e ( ) ; HttpServl etRequest req = ( HttpServl etReques t ) ctx , getRequest ( ) ; resp , setContentType ( "text!htm l ; cha rset=UTF - 8 " ) ; try ( P r i ntWri ter out = resp , getWri ter ( ) ) { out . p r i ntl n ( "< I DOCTYPE html > " ) ; out . p r i ntl n ( " " ) ; out , p r i ntl n ( " " ) ; out . p r i ntl n ( "Servl et NewServl et " ) ; out , p r i ntl n ( "" ) ; out . p r i ntl n ( " " ) ; out . p r i ntl n ( "Servl et NewServl et at " + req , getContextPath ( ) + 4" " ) ; out . p r i ntl n ( "" ) ; out , p r i ntl n ( "" ) ; catch ( IOExcepti on ex) { Logger , getLogg e r ( N ewServl et , cl a ss , getName( ) ) , l og ( Level , SEVERE , 4nul l , ex ) ; fi na l ly { ctx , comp l ete ( ) ;

j);

48

Część I • Podstawy

Aby obsłużyć żądanie w sposób asynchroniczny, musimy wywołać metodę startAsync ( ) , która zwraca obiekt kontekstu asynchronicznego. Następnie możemy dodać do wyko­ nawcy obiekt interfejsu Runnab l e, w którym za pośrednictwem kontekstu uzyskujemy dostęp do żądania i odpowiedzi. Zapis postaci: executor . execute ( ( ) ->

j): to przykład wprowadzonych w Javie SE 8 wyrażeń lambda. Jest to ekwiwalent następują­ cego zapisu: executor . execu t e ( new Runnabl e ( ) publ i c voi d run ( ) {

j):

j

Jak widać, dzięki wyrażeniom lambda uzyskujemy znacznie krótszy kod, a dzięki wyko­ nawcy i puli wątków jesteśmy w stanie oddelegować wykonanie czasochłonnych za­ dań do dedykowanych mechanizmów ich obsługi.

Rozdział 4.

JSP gdy out.println() nie wystarcza -

W poprzednich rozdziałach dogłębnie zajęliśmy się mechanizmami działania serwle­ tów, filtrów, a także strukturą deskryptora wdrożenia. Można powiedzieć, że poznałeś większość narzędzi, które są niezbędne do tworzenia aplikacji webowych. Jest tylko jeden problem. Narzędzia te są (z założenia) bardzo uniwersalne, przez co trudno jest zastosować je w najbardziej typowej sytuacji, czyli podczas tworzenia stron HTML. W przykładach z poprzedniego rozdziału nietrudno było zauważyć, że generowanie kodu HTML, stanowiącego istotę stron internetowych, było wykonywane za pomocą metody p r i n t l n ( ) obiektu klasy Pri ntWri ter: =

Pri ntWri ter out response . getWri ter ( ) ; out . pri ntl n ( "StronaTreść strony/p> 4" ) :

Powiedzmy wprost - w przykładach dłuższych niż jeden wiersz takie rozwiązanie to katastrofa. Dołączanie dynamicznych danych wewnątrz takich wywołań jeszcze bar­ dziej komplikuje kod. Należy powiedzieć sobie jasno i wyraźnie, że do generowania stron HTML nie należy wykorzystywać serwletów w poznanej przez nas postaci. W takiej sytuacji musiała powstać jakaś alternatywa. Owa alternatywa nosi nazw ę JSP, czyli JavaSelVer Pages. Technologia ta pozwala na wstawianie do zwykłego kodu HTML (lub do wszelkich innych plików tekstowych) przeróżnych konstrukcji - od zwykłe­ go kodu Javy (co nie jest obecnie zalecane), poprzez specjalne wyrażenia wykorzystują­ ce język EL (Expression Language - język wyrażeń), aż po specjalne znaczniki, przypominające te znane z języka HTML (JSTL - JavaServer Tag Libraries - bi­ blioteki znaczników JavaSelVer).

50

Część I • Podstawy

Zacznijmy od początku, czyli JSP w świecie serwletów Zanim przejdziemy do sposobów tworzenia stron JSP i omówienia najistotniej szych mechanizmów działających w obrębie tych stron, musimy omówić sposoby włączania stron JSP w normalny cykl życia aplikacji webowej . Jeśli chcesz umieścić plik JSP w aplikacji webowej , możesz skorzystać z jednej z dwóch metod: •

umieszczenie tego pliku w odpowiednim folderze aplikacji webowej ,



odwołanie się do pliku JSP w wybranym serwlecie za pomocą specjalnego mechanizmu.

Każdy z tych mechanizmów ma swoje wady i zalety - omówimy je w kolejnych dwóch podrozdziałach.

Pliki JSP dostępne bezpośrednio Ta metoda wykorzystywania plików JSP powinna kojarzyć się z używaniem zwy­ kłych plików stron internetowych, takich jak pliki HTML czy CSS. Zasada jest prosta - plik JSP musi być umieszczony w określonym folderze projektu. Oczywiście plik taki musi być widoczny dla użytkownika. Po umieszczeniu pliku nie trzeba wykonywać żadnych dodatkowych operacji - zostanie on załadowany i przygotowany do użycia automatycznie. Do zalet takiego rozwiązania zalicza się łatwość integrowania takich plików z aplikacją. Plik JSP jest w stanie wyświetlić i w prosty sposób wykorzystać dane z formularza. W bardziej zaawansowanych przypadkach trzebajednak skorzystać z innego rozwiązania.

Pliki JSP wywoływane z poziomu serwletów Wyobraźmy sobie, że chcielibyśmy wysłać formularz, z którego dane zostaną zapisa­ ne w bazie. Formularz może być utworzony jako zwykły plik HTML lub JSP dostęp­ ny bezpośrednio. Jak rozwiązać problem przetworzenia danych (zapisania ich w bazie), a następnie wyświetlenia strony z rezultatem operacji? Z jednej strony niezbędne jest zastosowanie funkcjonalności serwletów, z drugiej - przydałaby się wygoda wy­ świetlania plików JSP. Rozwiązaniem jest prosty mechanizm o nazwie dyspozytor żą­ dań (ang. request dispatcher). Jego zasada działania jest tylko nieco bardziej skomplikowana niż w poprzednim przy­ padku. Na początku swe działanie wykonuje serwlet (to do niego musi odwoływać się adres URL wprowadzony przez użytkownika lub podany w formularzu), który łączy się z bazą danych, aktualizuje ją itd. Po wykonaniu niezbędnych operacji obiekty żąda­ nia i odpowiedzi są przesyłane (za pomocą wysyłacza) do pliku JSP, który na podstawie

Rozdział

4.

• JSP

-

gdy out.printlnO nie wystarcza

51

atrybutów i parametrów żądania wyświetla odpowiednie informacje. W tym momencie stało się jasne, dlaczego atrybuty żądania (request . getAttri bute () , request . setAttri '-+bute () ) mają rację bytu.

Pochodzenie JSP - małe dochodzenie Już z a chwilę stworzymy pierwszą prostą aplikację webową, wykorzystującą obydwie omówione wyżej metody umieszczania stron JSP. Zanim to nastąpi, musimy odpowie­ dzieć na jedno zasadnicze pytanie - czym jest pojedyncza strona JSP? Czy za zwy­ kłym plikiem, który zawiera kod HTML i szereg dodatkowych funkcji, kryje się coś nowego? Obcego? Niestety (a może i " stety" - ale " niestety" brzmi ładniej), nie. Strona JSP to w istocie zakamuflowany . . . serwlet! Przypomnij sobie nasz pierwszy przykład opisany w niniej­ szej książce. W jednym z akapitów pisałem o tworzeniu pierwszego "tradycyjnego" serwletu - HelloWorld. Słowo "tradycyjny" zostało przeze mnie użyte świadomie, po­ nieważ rzeczywiście pierwszym serwIetern była utworzona chwilę wcześniej strona JSP! Ale niby w jaki sposób ze zwykłego kodu HTML (CSS, JavaScript lub dowolnego innego), połączonego z dodatkową funkcjonalnością JEE, można stworzyć serwlet, który jest wszak klasą tworzoną według ściśle określonych wytycznych? Jak się oka­ zuje, w skład platformy JEE wchodzą całkiem sprytne narzędzia. Jednym z nich jest translator stron JSP na serwIety. Jego zadaniem jest przekształcenie wszystkich ele­ mentów wchodzących w skład stron JSP na równoważne im mechanizmy, obowiązu­ jące wewnątrz serwletów. Oczywiście translacja nie zachodzi sama dla siebie - po wygenerowaniu klasy serwletu jest ona kompilowana, a następnie ładowana tak samo, jak ma to miejsce w przypadku zwykłych serwletów. Nieco więcej na temat translacji poszczególnych konstrukcji JSP powiemy sobie przy omawianiu tych konstrukcji. Najprościej wygląda proces translacji zwykłego tekstu (kodu HTML, CSS i innych). Fragmenty kodu są po prostu wklejane do wywołań metody printl n () , poznanej w poprzednich rozdziałach. Tym jakże odkrywczym akcen­ tem zakończymy (na razie) teorię w tym rozdziale, nadszedł bowiem czas na pierwszą prostą aplikację webową. Należy pam iętać, że obecnie odchodzi się od stosowania JSP jako technologii widoku na rzecz Faceletów, równ ież opisanych w niniejszej książce. Po co więc w ogóle za­ przątać sobie głowę JSP? Musimy pam iętać, że poza tworzen iem nowych aplikacj i o d podstaw zadaniem program isty jest często praca nad j u ż istniejącymi aplikacjam i . Nierzadko zdarza się, że takie rozwiązania opierają się na technologiach takich, jak JSP, d l atego warto wiedzieć, z czym program ista J ava EE może się spotkać w rze­ czywistości.

Część I

52



Podstawy

Pierwsze kroki w JSP Aby zaprezentować sposoby umieszczania plików JSP w praktyce, należy utworzyć naszą pierwszą stronę. W tym celu wykonaj poniższe kroki:

1. Kliknij prawym przyciskiem folder Web Pages w widoku projektu. 2. Wybierz opcję New/JSP

...

(lub Other, jeśli opcja JSP

...

nie jest widoczna).

3. Podaj nazwę strony (bez rozszerzenia), np. pierwsza i kliknij Finish . W tym momencie zostanie utworzona nowa strona, podobna do tej, z którą mieliśmy do czynienia w rozdziale drugim. Zawiera ona wyłącznie kod HTML, ale z jednym wyjątkiem, gdyż pierwszy wiersz strony (pomij ając komentarz) wygląda następująco:

Jest to jedna z kilku specjalnych konstrukcji, występujących w obrębie stron JSP. Nosi ona nazwę dyrektywy. Do jej zadań zalicza się określanie różnego rodzaju właściwości strony; w tym przypadku określamy typ MIME i kodowanie znaków użyte na stronie. Ta oraz inne specjalne konstrukcje zostaną omówione w dalszej części rozdziału. Zadaniem naszej przykładowej strony będzie wygenerowanie hasza (skrótu md5) dla wprowadzonych danych. Pierwsza strona JSP będzie zawierała formularz, składający się z jednego pola (do wprowadzenia danych) i przycisku (do wysłania formularza). Formularz zostanie przesłany do serwIetu, który po wygenerowaniu skrótu przekaże obsługę żądania do wynikowej strony JSP. Pierwsza strona nie będzie zawierać żadnych nowych elementów związanych z tech­ nologią JSP. Jedynym elementem charakterystycznym dla JSP będzie znana już dy­ rektywa page. Treść strony została przedstawiona na listingu 4 . 1 . Zwróć uwagę, że jest to typowy przykład strony, do której będziemy odwoływać się w sposób bezpo­ średni. Strona nie oczekuje od nas przygotowania jakichkolwiek danych wejściowych, dlatego nie musimy korzystać z pomocy serwIetu. Listing

4.1. Treść strony zawierającejformularz. < I OOCTYPE HTML PUB L I C " - I IW3CIIOTO HTML 4 . 01 Trans i t i onal l l E N " ''http : / /www . w3 . org/TR/htm 1 4 / 1 oose . dtd''> Haszowani e M05 Wprowadź dane : < /p>

Rozdział 4 . • JSP

-

gdy out.printlnO nie wystarcza

53

Zdecydowanie bardziej interesującym przykładem jest drugi element naszej aplikacji. Zadaniem serwIetu, określonego adresem md5.do, jest pobranie danych z formularza, obliczenie skrótu md5 i przekazanie go do pliku JSP. Moglibyśmy oczywiście wyge­ nerować stosowną treść HTML w samym serwlecie, jednak w tym przypadku byłoby to rozwiązanie wysoce nieeleganckie, zaprzeczające idei wzorca projektowego MVC (Model-View-Controller, o którym będzie jeszcze mowa w tej książce). Treść serwleta zawiera listing 4.2. Listing 4.2. Serwlet obsługującyformularz z listingu 4. 1

protected voi d proces sRequest ( HttpServl etRequest request , HttpServl etResponse respon s e ) throws Servl etExcepti on , IOExcept i on { response . setContentType ( "text!html ; cha rset=UTF - 8 " ) ; Pri ntW r i ter out = response . getW r i ter ( ) ; try { Stri ng dane = request . getParameter ( "daneOoZahaszowan i a " ) ; Stri ng hash = thi s . ha szuj ( dane ) ; request . setAttri bute ( "md5hash " , hash ) ; request . getRequestOi spatcher ( " /pokaz . j sp " ) . forward( request , respons e ) ; fi nal l y { out . cl os e ( ) ;

pri vate Stri ng ha szuj (Stri ng dane ) th rows Servl etExcepti on { try { MessageOigest m = MessageOigest . get lnstance ( "M05" ) ; byt e [ J bufor = dane . getBytes ( ) ; m . update( bufor , O , bufor . l ength ) ; B i g l nteger hash = new B i g l nteger ( l , m . digest ( ) ) ; return Str i ng . format( "%1$ 032X " , hash ) ; catch ( N oSuchAl gori thmExcepti on nsae) { throw new Servl etException( "Al gorytm szyfrowan i a n i e jest obsługiwany l " ) ;

Efekt działania serwletu musi być przekazany do strony JSP. To właśnie w tym momencie do gry wchodzą atrybuty żądań. W przeciwieństwie do atrybutów sesji czy kontekstu są one widoczne jedynie w obrębie realizowanego żądania, dzięki czemu nie musimy się obawiać konfliktów z innymi żądaniami. Wystarczy więc ustawić wybrany atrybut w serwlecie, a następnie odczytać go (i wyświetlić) na stronie JSP. Przejdźmy zatem do nowego i jednocześnie najbardziej interesującego elementu tej prostej aplikacji, czyli do strony JSP wywoływanej przez serwlet (listing 4.3). Listing 4.3. Strona JSP prezentująca efekt działania serwletu

< I OOCTYPE HTML PUB L I C " - / /W3C//OTO HTML 4 . 01 Trans i t i onal / / E N " ''http : / /www . w3 . org/TR/htm 1 4 / 1 oose . dtd''>

54

Część I



Podstawy

Wyni k haszowani a Wyni k ha szowa n i a to : Powrót

Poza znanymi już elementami strony JSP (czyli zwykłego kodu HTML i dyrektywy page) korzystamy z zupełnie nowej konstrukcji. Jest to wyrażenie zapisane w - nomen omen - języku wyrażeń (ang. Expression Language - EL). Język ten pozwala na eleganckie i zwięzłe odwoływanie się do najważniejszych danych istniejących w obrębie strony JSP, czyli atrybutów, parametrów, obiektów żądania i odpowiedzi itd.

Docenić wygodę , czyli jak to lat temu kilka bywało . . . Zrealizowany przed chwilą przykład, choć z calą pewnością nie oddaje pełni możliwo­ ści JEE w aplikacjach webowych, zawiera kilka ciekawych mechanizmów, które uprasz­ czają tworzenie aplikacji webowych i pozwalają zachować czytelny i zwięzły kod. Niestety, nie zawsze było tak dobrze. Aby się o tym przekonać, wystarczy zapoznać się z dowolną książką traktującą o JSP wydaną choćby cztery - pięć lat temu (lub jeszcze wcześniej). Istotne miejsce zajmo­ wal tam opis specjalnych konstrukcji, które mogły być umieszczane w kodzie strony JSP. Istniej e kilka rodzajów takich konstrukcji; na razie poznaliśmy pobieżnie jedną z nich - dyrektywę. W swoim czasie tego rodzaju mechanizmy były niezbędne do wykorzystywania typowo serwerowej funkcjonalności Gak korzystanie z sesji, baz da­ nych itd.), ale obecnie stosuje się zupełnie inne rozwiązania. Nie ma więc sensu po­ święcać zbyt dużo Twojego czasu (i zaśmiecać Ci niepotrzebnie pamięci) na dogłębne omawianie tych mechanizmów. Z drugiej strony jednak są trzy powody, dla których warto je poznać: • Praca programisty polega nie tylko na tworzeniu nowych aplikacji, ale również

na wprowadzaniu zmian w już istniejących - nigdy nie wiadomo, czy szef nie każe Ci zmodyfikować aplikacji napisanej , powiedzmy, w roku 2005. • Raz na sto (a może i tysiąc) przypadków może się zdarzyć, że użycie danego

(starszego) mechanizmu będzie najlepszym wyjściem z sytuacji. • Niektóre z omawianych mechanizmów (np. dyrektywy) nadal mają duże

znaczenie. Przy omawianiu poszczególnych mechanizmów będę zaznaczal, że konkretny mechanizm może być nadal wykorzystywany w aplikacjach bez nadmiernego poczucia winy.

W najbliższych kilku podrozdziałach przedstawię Ci więc wszystkie mechanizmy, z jakimi możesz mieć do czynienia.

Rozdział

4.

• JSP

-

gdy out.printlnO nie wystarcza

55

Skryptlet Skryptlet to ZŁO. Niegdyś - panaceum na wszystkie problemy, a obecnie - chyba najbardziej wyklęta ze wszystkich konstrukcji specjalnych. Skryptlet pozwala na bez­ pośrednie osadzanie kodu Java w plikach JSP. Brzmi ekscytująco, nieprawdaż? Nie­ stety w informatyce, a zwłaszcza w programowaniu, rzadko spotyka się rozwiązania proste, uniwersalne i jednocześnie rozwiązujące wszystkie potencjalne i rzeczywiste problemy. Podobnie sytuacja wygląda w przypadku skryptletów. W poniższym przy­ kładzie sprawdzamy, czy użytkownik jest zalogowany (czy obiekt użytkownika ist­ nieje w sesji), a jeśli tak, wyświetlamy stosowny komunikat (konieczne jest wykonanie rzutowania, ponieważ metoda getAttri bute O zwraca obiekt typu Obj eet). Witaj ,

Do pozornych zalet skryptletów możemy zaliczyć możliwość wykonania praktycznie dowolnego kodu. Oznacza to, że połączenia z bazą danych, manipulacje na atrybutach kontekstu, sesji lub żądania, a także operacje na zasobach aplikacji nie stanowią żadnego problemu. Co więcej , generowanie kodu HTML jest jeszcze prostsze niż w serwle­ tach, ponieważ nie trzeba pobierać referencji do obiektu zapisującego - wystarczy sko­ rzystać z domyślnego obiektu out i j ego metody pri ntl n O . Gdzie więc tkwi haczyk? Otóż opisana powyżej główna zaleta skryptletów staje się jedną z najpoważniejszych wad. Przypomnij my sobie, po co zostały utworzone strony JSP - główny powód sta­ nowiła chęć uniknięcia niewygodnego i skomplikowanego generowania kodu HTML i jednocześnie próba oddzielenia mechanizmów aplikacji webowej (czyli tzw . warstwy logiki aplikacji) od elementów odpowiedzialnych za wyświetlanie efektu działania aplikacji (czyli warstwy prezentacji, zwanej też widokiem). Dzięki takiej separacji praca w grupie nad jedną aplikacją staje się niewątpliwie prosta - projektanci stron mogą zająć się tworzeniem widoku (wyglądu) aplikacji, a programiści - implemento­ waniem reguł i procesów biznesowych, które muszą być zastosowane w aplikacji. Niestety skryptlety burzyły (i nadal burzą) ten logiczny podział.

Remedium? Nie istnieje niestety uniwersalny mechanizm, który pozwoliłby zastąpić skryptlet. Niektóre elementy języka Java (takie jak pętle, instrukcje warunkowe czy instrukcja sw i t ch) mogą być zastąpione mechanizmami rozszerzającymi technologię JSP mam na myśli np. bibliotekę JSTL. Czasami stosowny kod musi być przeniesiony do innego składnika aplikacji, np. do serwletu lub klasy przez ów serwlet wykorzystywa­ nej . Bez względu na okoliczności przesłanie jest jedno - skryptlety są złe i nie nale­ ży z nich korzystać, a istniejące skryptlety w miarę możliwości należy przerabiać na bardziej nowoczesne konstrukcje.

Deklaracja Możliwości skryptletów nie s ą nieograniczone. Ograniczenia wynikają głównie ze sposobu umieszczania treści skryptletu w kodzie serwIetu, ponieważ kod ten j est umieszczany bezpośrednio w treści metody obsługującej żądania użytkowników.

56

Część I



Podstawy

Czego nie można zrobić w kodzie metody? Chociażby zadeklarować pola oraz meto­ dy, które będą widoczne w obrębie całej klasy. Możliwość ta nie jest z pewnością często wykorzystywana, ale warto wiedzieć, że w ogóle takowa istnieje.

Remedium? Ze skryptletami Wlązą się ściśle deklaracje. Jeśli więc doprowadzisz do usumęCla skryptletu z danej strony, usunięcie deklaracji nie powinno już stanowić problemu.

Wyrażenie Szczególnym przypadkiem skryptletu jest wyrażenie (choć jest zapisywane w nieco inny sposób). Jeśli w danym skryptlecie chcesz jedynie wyświetlić jakąś wartość, po­ winieneś skorzystać z wyrażenia. Zamiast kodu:

wystarczy napisać tworzą znaczniki, wobec czego nie można ich tak po prostu umieszczać na stronie. Z kolei znak & (ampersand) pozwala na tworzenie encji, wobec czego jego samodzielne wy­ stąpienie w pliku XML nie jest możliwe. Znaki i " nie mają takich obostrzeń, ale są one ściśle związane z procesem tworzenia znaczników (bez nich nie byłoby atrybutów), wobec czego one również są poddawane zamianie. I

I

Wspomniane pięć znaków jest zamieniane na encje: &l t ; , > ; , & ; , ' ; i " ; . Jak widać, każda encja składa się ze znaku &, identyfikatora (którym może być kod Unicode lub identyfikator znakowy w przypadku naj popularniej szych znaków) i śred­ nika. Po napotkaniu takiego znaku przeglądarka wyświetla go i nie próbuje w żaden sposób interpretować. Dzięki temu, jeśli złośliwy cracker wpisze nawet najbardziej złośliwy kod, mechanizm zamiany znaków na encje spowoduje, że kod ten zostanie jedynie wyświetlony na stronie. Na zakończenie tej dość długiej (ale ważnej) dygresji prosty przykład:

Tak naprawdę atrybut es capeXml domyślnie przyjmuje wartość true. Warto jednak wiedzieć o jego istnieniu - jest to kolejny fakt przemawiający za używaniem znacz­ nika c : out. Kiedy zatem powinno stosować się wartość fal se dla tego atrybutu? Tylko wtedy, jeśli zadbasz o porządną walidację wprowadzanych danych (również po stro­ nie serwera! ), a jednocześnie chciałbyś pozwolić użytkownikowi na stosowanie naj­ prostszych znaczników do formatowania tekstu. Inaczej narażasz aplikację na łatwe zlokalizowanie oraz na niebezpieczeństwo.

Część I

76



Podstawy

Ale to już było, czyli c:set W poprzednim rozdziale poznaliśmy "cudowny" sposób na określanie wartości właści­ wości ziaren - mam na myśli akcję standardową j s p : setProperty. Akcja ta jest bar­ dzo przydatna, zwłaszcza w przypadku obsługi formularzy. Twórcy JSTL stwierdzili, że nie jest ona jednak doskonała, a ja w najbliższych kilku akapitach spróbuję Cię przekonać, że mieli rację. Podstawowym problemem związanym z akcją j sp : set Property jest ograniczenie jej działania tylko do ziaren, a mówiąc dokładniej - ich właściwości. Nie zawsze przecież atrybuty zasięgów (strony, żądania, sesji i kontekstu) są ziarnami. Ba, moim zdaniem częściej wykorzystuje się zwykłe wartości typów prymitywnych (tudzież łańcuchy znaków). Z tego właśnie względu twórcy biblioteki JSTL dodali znacznik, który potrafi ustawić wartości zarówno typów prymitywnych, jak i właściwości ziaren, a wszystko to w obrębie dowolnie wybranego zakresu. Ze względu na różnicę w działaniu znacznik c : set udostępnia dwa warianty obsługi. Elementem wspólnym obu wariantów są atrybuty: va l ue (za pomocą którego przeka­ zujemy wartość dla elementu) i s cope (określa zasięg ustawianego atrybutu). Należy pamiętać, że zasięg jest atrybutem opcjonalnym - jeśli nie będzie on podany, zostaną przeszukane wszystkie zasięgi (w kolejności strona, żądanie, sesja, kontekst), aż do znalezienia szukanego atrybutu. W tym momencie wspólne postępowanie się kończy. Dalej musimy się zdecydować, co właściwie chcemy ustawić - czy wartość prostą (prymitywną), czy właściwość ziarna. W przypadku wartości prostych musisz jedynie określić atrybut var. Powinieneś prze­ kazać w nim nazwę atrybutu, który chcesz ustawić. Musisz pamiętać, że w przypadku tego atrybutu niemożliwe jest dynamiczne określenie jego nazwy (za pomocą wyra­ żenia EL) :

W ten sposób wartość pola formularza o nazwie j ezyk zostanie skopiowana do atry­ butu sesji o nazwie pre ferowanyJezyk. Atrybut ten jest zwykłą zmienną typu Stri ng. Podobne przykłady (niezwiązane z ziarnami) można by nmożyć. Akcję c : set można wykorzystać także w celu modyfIkowania bardziej złożonych obiek­ tów. Mam na myśli nie tylko ziarna, ale i mapy - nic nie stoi na przeszkodzie, aby modyfikować zarówno właściwości ziaren, jak i wybrane elementy mapy. Składnia akcji jest następująca: < c : set target= " $ { sessi onScope . uzytkown i k } " property="pesel " val ue= " $ { param . pesel } " j>

W tej akcji zmieniamy właściwość pesel przechowywanego w sesji obiektu uzytkow­ n i k. Korzystamy przy tym z pola formularza pese l . Zwróć uwagę, że przy określaniu obiektu, który chcemy modyfikować, przekazujemy bezpośrednio (za pomocą wyra­ żenia EL) ten obiekt, a nie jego nazwę. Taka sama sytuacja ma miej sce w przypadku przekazywania mapy. Pozostałe dwie właściwości działają w sposób znany nam z po­ przednio omawianych znaczników.

Rozdział 5 .



JSTL - wisienka na torcie JSP

77

Znacznik c : set pozwala na podanie wartości w podobny sposób jak znacznik c : out - atrybut val ue może być zastąpiony znacznikami otwierającym i zamykającym: ${ param . j ezyk}

Czwarty muszkieter Do tej pory poznaliśmy kilka sposobów na wyświetlanie (odczytywanie) danych, dwa sposoby na ich zmianę, a także jeden na ich tworzenie. Z czterech fundamentalnych operacji na danych (tworzenie, odczytywanie, aktualizowanie, usuwanie) brakuje nam tylko usuwania. Co prawda, można by zrealizować tę funkcję jako ustawianie wybrane­ go atrybutu na wartość nul l , ale z punktu widzenia logiki korzystanie z akcji c : set powinno prowadzić nie do usunięcia elementu, tylko do jego zmiany. Do usuwania zdecydowanie lepiej jest korzystać z akcji c : remove, której składnia jest prosta i przej ­ rzysta. Przykład:

Atrybut s cope można rzecz jasna pominąć - w takiej sytuacji usuwanie odbędzie się zgodnie ze znanąjuż kolejnością przeszukiwania zasięgów.

Kontrola sterowania Po dość długim omawianiu wszelkich akcji związanych z manipulowaniem danymi nadszedł czas, abyśmy pomanipulowali trochę przepływem sterowaniem na stronach JSP. Do tej pory wszystkie tworzone i omawiane przez nas strony wyświetlały infor­ macje od początku strony do jej końca. Dzięki JSTL w bardzo prosty sposób można np. uzależnić wyświetlanie wybranego znacznika (znaczników) od wartości określo­ nego wyrażenia. Panie i Panowie ! Nadszedł czas na i f, switch i for w wydaniu JSTL.

c:if Akcja c : i f stanowi bezpośredni odpowiednik instrukcji warunkowej i f znanej z języka Java. Przyjmuje ona tylko jeden obowiązkowy atrybut - test, który powinien zawierać wyrażenie EL, oczywiście zwracające wartość logiczną (truejfal se). Jeśli wyrażenie to zwróci wartość t rue, to wszystkie znaczniki zawarte wewnątrz akcji c : i f zostaną wy­ konane (technicznie rzecz ujmując, zostanie wykonany fragment kodu serwIetu, który powstał w wyniku translacji znaczników znajdujących się wewnątrz instrukcji c : i f). Akcja c : i f nie udostępnia klauzuli e l se; jeśli chcesz osiągnąć taką funkcjonalność, zapoznaj się szczegółowo z akcją c : choose. W obrębie akcji c : i f istnieją także dwa atrybuty: var i s cope, które pozwalają na zapi­ sanie wyniku warunku (z atrybutu test) do określonego atrybutu (var) w określonym zasięgu (scope). W ten sposób raz uzyskane wyrażenie możesz wykorzystać w innych sytuacjach. Przykład:

78

Część I • Podstawy

Wi taj . $ { se s s i onScope . za l ogowany . l og i n } c/c : i f>

c:choose W przeciwieństwie do akcji c : i f, która nie odzwierciedla całkowicie możliwości tra­ dycyjnej instrukcji warunkowej, akcja c : choose jest kompletnym odpowiednikiem in­ strukcji sw i tch. Co więcej, to właśnie ta instrukcja realizuje tradycyjną funkcjonalność instrukcji i f . . e l se, a dzięki różnorodności wyrażeń EL również poszczególne przy­ padki akcji c : choose mogą być znacznie bardziej rozbudowane niż w przypadku in­ strukcji switch. Składnia tej akcji jest następująca: Twój koszyk n i e i st n i ej e l Twój koszyk j est pusty l Twój koszyk n i e j est pusty l

Wewnątrz akcji c : choose (która sama w sobie nie przyjmuje żadnych atrybutów) mo­ żemy umieszczać tylko dwa rodzaje akcji c : when i c : otherw i se. Kontener JSP sprawdza kolejne warunki umieszczone w atrybutach test aż do znalezienia warunku prawdziwego. W takiej sytuacji kod znajdujący się wewnątrz spełniającego warunek znacznika c : when jest wykonywany, a pozostałe znaczniki c : when są pomij ane. Zasadę tę mogliśmy wykorzystać w powyższym przykładzie. Jak już mówiliśmy, operator empty zwraca true, gdy argument jest pusty lub ma wartość nul l . Dzięki temu, że w pierwszym warunku c : when sprawdzamy koszyk jedynie pod kątem wartości n ul l , w drugim wa­ runku możemy założyć, że koszyk nie ma wartości n ul l , więc operator empty zwróci wartość t rue tylko wtedy, gdy w koszyku faktycznie nie będzie żadnych elementów. -

W powyższym przykładzie nie został wykorzystany wspomniany przeze mnie znacz­ nik c : ot herw i se. Znacznik ten nie przyjmuje żadnych atrybutów. Umieszczona w nim treść zostanie wykonana tylko wtedy, jeśli żaden z warunków umieszczonych w akcjach c : when nie zostanie spełniony. Jest to więc odpowiednik słowa kluczowego defaul t . W przeciwieństwie do tego słowa znacznik c : otherw i se jest objęty większymi restryk­ cjami. Znacznik ten musi być umieszczony na samym końcu treści znacznika c : choose, za wszystkimi warunkami c : when. Znacznik c: otherwi se bardzo często wykorzystywany jest do realizacji klauzuli i f . . e l se. Wystarczy bowiem skorzystać z następującej konstrukcji: Twój pesel to $ { se s s i onScope . uzytkowni k . pesel }

Rozdział 5 . • JSTL - wisienka na torcie JSP

79

Twój pesel ni e został zdefi ni owany I

Powyższy kod nie wymaga chyba komentarza. Połączenie funkcjonalności klauzul sw; tch, ; f e l se z uniwersalnością wyrażeń EL czyni z akcji c : choose potężne na­ rzędzie. . .

Pętelka do kom pletu Czym byłyby aplikacje webowe, gdyby nie możliwość wyświetlania dynamicznie zmie­ niających się danych? Prawdopodobnie nadal tkwilibyśmy gdzieś w drugiej połowie lat 90. i na pewno powstawanie książek takich jak ta nie miałoby miejsca. Z tego względu obsługa danych zajmuje ważne miejsce w tej publikacji (temu zagadnieniu poświęcam drugą część książki). Istotnym zagadnieniem jest też wyświetlanie danych. Na poziomie strony JSP nie ma znaczenia, czy dane pochodzą z bazy danych, pliku XML czy z pamięci. Mamy do czynienia z jakąś kolekcją (mapą, tablicą, listą) i każdy z elementów tej kolekcji musi być wyświetlony według określonego schematu. W tym momencie wkracza do akcji kolejna (nomen omen) akcja c : forEach. -

Akcja c: forEach stanowi bezpośredni odpowiednik pętli for (Obj ect o : kol e kcj a) . Akcja ta przyjmuje dowolny rodzaj kolekcji, a następnie wykonuje zawarty w swoim wnętrzu kod tyle razy, ile elementów jest w danej kolekcji. W kolejnych przykładach rozszerzymy nieco ideę naszego, dość abstrakcyjnego do tej pory koszyka. koszyk jest listą obiektów typu Produkt. Produkt z kolei to klasa-ziarno, zawierająca właściwości nazwa, cena, op; s i l ; czba (liczba sztuk produktu). $ { p . nazwa } $ { p . op i s } Li czba sztuk : $ {p . l i czba } , cena : $ { p . cena } , koszt całkow i ty : $ { p . l i czba * "'p . cen a }

Dwa kluczowe atrybuty znacznika c : forEach to var i i tems. W atrybucie i tems należy umieścić wyrażenie EL, zwracające kolekcję danych. O tym, jakie kolekcje mogą być przekazywane w atrybucie i tems, pomówimy za chwilę. Atrybut var określa nazwę zmiennej, jaką będziemy wykorzystywać wewnątrz akcji c : forEach do reprezentowania elementu kolekcji. Oczywiście zmienna p może być wykorzystywana tylko wewnątrz tej akcji c : forEach . Nieco inaczej wygląda sytuacja w przypadku map. Często się zdarza, że klucze danej mapy mają równie istotne znaczenie, co jej wartości. Z tego względu iteracja po ele­ mentach mapy ma nieco inny przebieg: $ { e l ement . key} : $ { el ement . val ue}

W ten sposób wyświetlisz klucze i wartości znajdujące się w dowolnej mapie.

80

Część I



Podstawy

Jeszcze więcej kontroli . . . Bardzo często przy wyświetlaniu list różnego rodzaju elementów wykorzystuje się liczby porządkowe, które pozwalają na ponumerowanie wszystkich elementów. Do tego celu służy atrybut status. Należy określić w nim nazwę zmiennej, z której będziemy korzystać wewnątrz bloku c : forEach: $ { i nfo . count } . $ { p . nazwa }

Powyższy kod będzie wyświetlał nazwy porządkowe (z kropką) w jednej kolumnie, a na­ zwy produktów w następnej . Właściwość count przechowuje numer aktualnie przecho­ wywanego elementu (liczony od l ! ). Czasami się zdarza, że nie chcesz iterować po kolekcji, a jedynie po liczbach (wg określonych zasad). Akcja c : forEach pozwala również i na to. Wystarczy skorzystać z dodatkowych atrybutów, a pominąć te, które poznaliśmy przed chwilą: Li czba : $ { numer }

Wyjątki

+

JSP

=

...

Przy omawianiu dyrektywy page była mowa o zabezpieczaniu stron przed wyjątkami - jak doprowadzić do przekierowania przeglądarki do strony błędu. Czasami pojawiają się jednak błędy innej natury, wobec których nie chcielibyśmy postępować aż tak dra­ stycznie - wystarczyłoby wyświetlenie informacji o błędzie. Powstaje pytanie, czy istnieje możliwość obsługi błędu lokalnie (bez konieczności przekierowania na inną stronę)· Możliwość taka oczywiście istnieje pod postacią akcji : $ { 3 /" nonsens " } Został wygenerowany następujący wyjątek : $ {wyjatek }

Akcja c : catch przechwytuje wszystkie wyjątki (a ściśle mówiąc, obiekty klas dzie­ dziczących po klasie Throwabl e), które powstaną wewnątrz niej . Jeśli zostanie określony atrybut var, to w atrybucie strony o podanej nazwie zostanie umieszczony obiekt wy­ generowanego wyjątku.

Rozdział 5 . • JSTL - wisienka na torcie JSP

Adresy URL

-

81

same kłopoty

Na stronach internetowych adresy internetowe mogą pojawić się wszędzie. Większość osób niezwiązanych z branżą IT pojęcie takie kojarzy z adresami postaci www.helion.pl. google.pl, czasami dodając jeszcze nazwę protokołu przed nazwą domenową (http://www.helion.pl). Niestety,jest to zaledwie wierzchołek góry lodowej. Najwięk­ sze problemy, z jakimi przyjdzie nam się uporać, wynikają z zupełnie innych ele­ mentów wchodzących w skład adresów URL. Na początek przypomnij sobie, z czego składa się ścieżka do serwletu (strony JSP), ewentualnie zerknij do rozdziału 3 . Początkowym elementem takiej ścieżki jest kon­ tekst aplikacji,czyli część adresu,która jest wspólna dla wszystkich elementów aplikacji. Następnie mamy do czynienia ze ścieżką do konkretnego serwIetuJstrony JSP; później występują fragmenty opcjonalne: dodatkowe informacje adresu URL (pat h l n fo) i pa­ rametry żądania HTTP (querySt r i ng). Adresy URL w obrębie aplikacji są najczęściej relatywne. Adres /MojSklep/strony/ stronajsp jest thnnaczony przez przeglądarkę na pełny adres URL: http://serwer. com/ MojSklep/strony/stronajsp. W tym przypadku ścieżka kontekstu ma postać !MojSklep. Problem w tej sytuacji może się pojawić, jeśli w przyszłości (na życzenie klienta lub szefa) nazwa kontekstu musiałaby ulec zmianie. O ile nikt raczej nie ingeruje w we­ wnętrzną strukturę adresów, o tyle zmiana nazwy kontekstu (np. na /ProfesjonalnySklep lub po prostu na I) jest całkiem prawdopodobna. Czy zatem konieczna jest zamiana wszystkich adresów URL? Na szczęście nie. Zamiast bezpośrednio podawać adresy URL w znacznikach , i podobnych,skorzystaj z następującej konstrukcji: Strona główna

Akcja c : url odpowiada za przetwarzanie podanych adresów URL pod różnymi wzglę­ dami. Przede wszystkim akcja ta pozwala na podawanie adresów relatywnych B EZ ścieżki kontekstu! Ś cieżka ta jest dynamicznie pobierana z ustawień aplikacji i dołą­ czana do powstałego adresu URL. Po wygenerowaniu adres jest umieszczany w atry­ bucie strony adresGl ownej . W tym momencie wystarczy umieścić adres w atrybucie href znacznika a. Znacznik c : out zadba o odpowiednie zakodowanie znaków specjal­ nych. W atrybucie href korzystamy z cudzysłowów pojedynczych z uwagi na koniecz­ ność zastosowania cudzysłowów podwójnych w akcji c : out.

Adresy URL bez tajemnic Jednym z największych problemów w przypadku tworzenia adresów URL jest umiesz­ czanie parametrów żądania. Są one umieszczane za właściwą częścią adresu URL, która zawiera ścieżkę do zasobu internetowego, np.

http://serwer.com!MojSklep/strony/stronajsp?imie=jan&nazwisko =kowalski

82

Część I



Podstawy

Problemy pojawiają się wtedy, gdy parametry (a zwłaszcza ich wartości) zawierają nietypowe (niealfanumeryczne) znaki. O ile przeglądarki na ogół dobrze sobie radzą z przekształcaniem takich znaków na odpowiadające im sekwencje cyfr i liter, o tyle ręczne wprowadzanie takich ciągów znaków z reguły powoduje problemy. Na szczęście akcja c : url udostępnia mechanizm eleganckiego deklarowania parametrów dla podanego adresu URL . Powyższy adres zapisany z użyciem akcji c : url wyglądałby następująco: < c : url val ue= "jstronyjstrona . j sp" var= " adresGl ownej "> < a h ref= ' ' >Strona główna

Znaczniki c : param umieszczane wewnątrz akcji c : url pozwalają na dodanie parame­ trów żądania. Przekazywane wartości mogą zawierać wyrażenia URL, dzięki czemu same adresy URL mogą być tworzone praktycznie w dowolny sposób.

Tajemnica sesji. . . Muszę Ci się, Drogi Czytelniku, do czegoś przyznać. Przy omawianiu sesji pominą­ łem z premedytacją dość istotny szczegół. Jeśli wykorzystywałeś intensywnie sesje, tworząc aplikacje w innych technologiach selVer-side, być może zwróciłeś na to uwagę - w każdym razie winien Ci jestem w tym momencie wyjaśnienie. Otóż w rozdziale 3 . niejako a priori założyliśmy, że sesje działają zawsze i wszędzie. Z punktu widzenia twórcy aplikacji webowych na ogół istotne jest, jak obsługuje się sesję, a nie jak klient jest w stanie z niej korzystać. Warto jednak wiedzieć, z czym mamy do czynienia. Otóż do poprawnego identyfikowania sesji klienta (która działa na przestrzeni wielu żądań HTTP) konieczne jest posiadanie przez niego specjalnego identyfikatora (prze­ syłanego w żądaniu), na podstawie którego kontener będzie mógł przydzielić odpo­ wiadający mu obiekt sesji. Cały problem (i związana z nim tajemnica) polega na spo­ sobie przekazywania tego identyfikatora przez klienta. W znacznej większości przypadków klient ma włączoną obsługę ciasteczek, dzięki czemu identyfikator może być zapisany właśnie w ciasteczku i wszyscy są szczęśliwi. Ty - bo nie musisz dbać o przekazywanie identyfikatora sesji, klient - bo aplikacja funkcjonuje poprawnie. Niestety, są wśród użytkowników ludzie (chciałoby się rzec, cytując klasyka, "to nie ludzie - to wilki ! "), którzy wyłączają obsługę ciasteczek. I ten niecny czyn powoduje, że trzeba przygotować alternatywę dla identyfIkatora przesy­ łanego w ciasteczku. Alternatywę stanowią adresy URL. Istotą tego rozwiązania jest dołączanie identyfIkatora sesji do wszelkiej maści adresów URL. Dzięki temu kontener wie, że żądania są powiązane z danym klientem. W prze­ ciwieństwie jednak do wariantu ciasteczkowego musimy tu podjąć dodatkowe czyn­ ności. Konieczne jest spreparowanie adresów URL występujących w naszej aplikacji w taki sposób, aby zawierały one identyfikator sesji.

Rozdział 5 . • JSTL - wisienka na torcie JSP

83

Można to osiągnąć na dwa sposoby. Korzystając z metody encodeURL O interfejsu Http '-+Servl etResponse (która z uwagi na rzadko spotykane generowanie treści HTML w ko­ dzie serwletów jest stosowana bardzo rzadko) lub . . . używając akcji c : url ! Jeśli akcja c : url przypadła Ci do gustu (i będziesz z niej korzystał w swoich stronach JSP), to nie musisz nic zmieniać ! W przeciwnym razie właśnie otrzymałeś kolejny argument przemawiaj ący za stosowaniem akcji c : url wszędzie tam, gdzie tylko się da. Pamiętaj , że korzystanie z akcji c : url nie zawsze powoduje dołączanie identyfikatora sesji do adresu URL. Mechanizm ten jest wykorzystywany jako ostateczność - tylko wtedy, gdy użytkownik wyłączy obsługę ciasteczek w swojej przeglądarce.

Trzech tenorów Po dość rozwlekłym i teoretycznym opisie powracamy do praktyki. Już dwa razy w trak­ cie omawiania technologii JSP zetknęliśmy się z różnymi metodami dołączania ze­ wnętrznych plików do stron JSP. Omówione zostało podej ście statyczne (dyrektywa @ i ncl ude) i dynamiczne (akcje j s p : i ncl ude, j s p : forward). Twórcy biblioteki JSTL po raz kolejny uznali, że to jednak za mało. Dzięki ich staraniom biblioteka JSTL zawiera własną akcję do importowania zewnętrznych plików. Jest to nomen omen akcja c : i mport. Akcja ta pozwala na umieszczanie na stronie JSP treści pochodzących z zewnętrznych źródeł w podobny sposób jak akcja j s p : i n c l ude. Najprostsza składnia tej akcji jest następuj ąca:

Dla odmiany atrybut page z akcji j sp : i n cl ude zostal zastąpiony atrybutem url . Uzasad­ nienie zmiany nazwy atrybutu wynika bezpośrednio z różnic dzielących akcje j s p : i ncl ude i c : i mport. Przede wszystkim akcja c: import pozwala na importowanie zarówno elementów wcho­ dzących w skład tej samej aplikacji webowej , jak i dowolnych innych elementów, któ­ re są dostępne za pomocą adresu URL (co niemożliwe jest do osiągnięcia za pomocą akcji j sp : i n cl ude)! Poniższe odwołanie jest zatem poprawne :

Możliwe jest również przekazanie parametrów za pomocą znaczników c : pa ram:

Na tym jednak możliwości akcji c: import się nie kończą. W niektórych sytuacjach znacz­ nie ważniej sza może okazać się możliwość większej kontroli nad importowaną tre­ ścią. Przypomnij my - domyślnie (we wszystkich trzech przypadkach dołączania tre­ ści) pobrana treść jest wstawiana bezpośrednio do treści strony. Jeśli intencją autora jest scalenie kilku różnych plików JSP w jedną całość, takie zachowanie jest na ogół po­ żądane. Nietrudno jednak o inny przykład - załóżmy, że chcemy pobrać z zewnętrznego

84

Część I



Podstawy

źródła (z innego serwera) specyficzne dane zawarte w pliku tekstowym (np. notowa­ nia giełdowe), lecz przed wyświetleniem chcemy je zweryfikować za pomocą wyra­ żenia EL lub przekształcić za pomocą jednej z funkcji JSTL. W takiej sytuacji ko­ nieczne jest przechowanie importowanej treści w jakimś miej scu. Akcja c : i mport pozwala na takie rozwiązanie.

Czytnik vs łańcuch Czytnik i łańcuch stanowią dwa media, które pozwalają na tymczasowe przechowanie treści pochodzącej z zaimportowanego zasobu. Wariant łańcuchowy polega na zapisaniu treści do atrybutu o podanej nazwie i podanym zasięgu:

Nawet jeśli nie chcesz w żaden sposób przetwarzać pobranych danych, możesz w powyż­ szy sposób zapisać je w atrybucie, i to nie tylko strony czy żądania, ale także sesji, a nawet kontekstu! Zamiast z łańcucha znaków możesz skorzystać także z czytnika (obiekt klasy pocho­ dzącej od klasy Reader), czyli strumienia znaków. Czytnik dzialajednak nieco inaczej :

Przede wszystkim czytnik może być wykorzystywany jedynie wewnątrz akcji c : i mport. Trudno jest wykorzystywać czytniki bezpośrednio w kodzie JSP, niemniej taki obiekt stanowi idealny środek dla różnego rodzaju znaczników, które do poprawnego działa­ nia wymagają podania właśnie czytników.

Na deser: funkcje! W tym rozdziale omówiliśmy najistotniej sze funkcje JSTL należące do przestrzeni nazw core. Warto jednak poznać także przestrzeń nazw funkcji, które pozwoliłyby na dynamiczne generowanie istotnych informacji. Cała biblioteka, oznaczana zwyczajo­ wo prefIksem fIl, zawiera 16 funkcji, z których większość jest związana z łańcuchami znaków. W tym miejscu omówię najważniej sze z nich. Pozo stale są dokładnie opisane w dokumentacji, poza tym przypominają metody znane z klasy j ava . l ang . St r i ng.

Przez kolekcje do serca Chociaż większość funkcji z tej biblioteki dotyczy stricte łańcuchów znaków, to naj ­ ważniej sza funkcja ma zastosowanie bardzo uniwersalne - dotyczy zarówno kolek­ cji, jak i tekstów. Mowa o funkcji l ength ( ) , która zwraca liczbę elementów kolekcji tudzież długość łańcucha. Dzięki tej funkcji bez problemu poinformujesz użytkownika, ile produktów znajduje się w jego koszyku czy też ile wiadomości pojawiło się w serwisie społecznościowym.

Rozdział 5 . • JSTL - wisienka na torcie JSP

85

Sposób wykorzystania tej (i innych) funkcji jest następujący: Li czba produ któw w koszyku : $ ( fn : l ength ( se s s i onScope . koszyk) }

Podstawową różnicą pomiędzy funkcjami i akcjami JSTL jest sposób umieszczania. Akcje są wstawiane jako zwykłe znaczniki zgodne ze standardem XML, funkcje zaś wykorzystują wyrażenia języka EL. Wewnątrz samego wyrażenia należy podać pełną nazwę funkcji (tutaj składnia jest taka sama: pre fi ks : nazwa), a następnie (w nawiasach) - parametry funkcji.

Funkcje łańcuchowe Spośród wielu funkcji łańcuchowych zdecydowałem się opisać jedynie kilka najważ­ niejszych. Pozostałe mają mniej sze znaczenie, mogą być zastąpione specyficznymi wywołaniami poniższych funkcji lub stanowią ich odmiany.

fn: indexOf Funkcja ta zwraca indeks wystąpienia jednego łańcucha znaków w innym. Jeśli podłań­ cuch (tzw. igła) nie wystąpi w łańcuchu (w tzw. stogu siana), zwracana jest wartość O. W przeciwnym razie zwracany indeks określa pierwszy znak podłańcucha. Przykład: $ { ( se s s i onScope . uzyt kown i k . im i e ! =nul l and �fn : i ndexO f ( appl i cati onScope . l ancuch l m i en i nowy , s e s s i onScope . uzyt kown i k . i m i e ) �gt - l ) ? "Wszyst k i ego naj l epszego z o kazj i i m i e n i n ! " : " " }

Jeśli wewnątrz atrybutu kontekstu znajduje się łańcuch zawierający imiona użytkow­ ników obchodzących w danym dniu imieniny i wśród tych imion znajduje się imię użytkownika, to wyrażenie wyświetli życzenia. Zwróć uwagę na pierwszą część wa­ runku - jeśli imię użytkownika będzie miało wartość nul l , zostanie sprowadzone do pustego łańcucha, co w konsekwencji spowoduje zwrócenie przez funkcję wartości O , które także może stanowić poprawną wartość. Musimy więc zabezpieczyć się przed tą ewentualnością ·

fn:join Funkcja fn : j o i n łączy elementy tablicy tekstów w jeden tekst z wykorzystaniem okre­ ślonych znaków (separatora). Zwróć uwagę, że nie możesz skorzystać z dowolnej kolekcji (a szkoda); dozwolona jest jedynie tablica, i to tablica łańcuchów znaków: Aktual n i e zal ogowani użyt kown i cy $ ( fn : j o i n ( s e s s i onScope . uzyt kown i cy , " , " ) }

fn:replace Zadaniem tej funkcji jest zamiana wystąpień jednego tekstu innym tekstem w wej ścio­ wym łańcuchu znaków. Wynik tej operacji nie wpływa na zawartość łańcucha wej­ ściowego, nowy łańcuch jest zwracany przez funkcję: $ ( fn : repl ace ( " kotekot " , " kot " , " p i es " ) }

W jej wyniku zostanie wyświetlony napis piesepies.

86

Część I



Podstawy

fn: substring Funkcja zwraca wybrany fragment wejściowego łańcucha znaków. Pierwszy parametr to łańcuch znaków, dwa pozostałe określaj ą indeks pierwszego znaku do pobrania (włącznie) i indeks ostatniego znaku (wyłącznie): $ ( fn : substr i n g ( "Hel l o , Worl d ! " , 0, 4) )

Po wywołaniu funkcji wyświetli się napis Heli. Istnieją dwa warianty tej funkcji (substri ngBefore O i subst ringAfter ( ) ), które zwra­ cają odpowiednio część łańcucha od początku do wystąpienia szukanego podlańcucha lub część łańcucha od wystąpienia szukanego podłańcucha do jego końca.

Podsumowanie Na ty m kończymy omawianie funkcjonalności biblioteki JSTL. Nie zostały rzecz jasna omówione wszystkie jej funkcje i akcje. Pominęliśmy m.in. biblioteki zaj mujące się obsługą języka baz danych SQL, XML czy formatowania, ale stosowne mechanizmy obsługi (moim zdaniem bardziej odpowiednie do tego typu języków w porównaniu z akcjami JSTL) zostaną omówione w dalszej części książki.

Część

II

Frameworki webowe

88

Java EE 6. Programowanie apl i kacji WWW

Rozdział 6.

JavaServer Faces W pierwszej części poznaliśmy podstawowe mechanizmy aplikacji webowych dzia­ łających na platformie Java Enterprise Edition. Dla niektórych doświadczonych pro­ gramistów rozdziały te mogły się wydać nieco zbędne, ponieważ obecnie rzadko two­ rzy się aplikacje webowe za pomocą takich technologii, jak JSP czy serwIety. Moim zdaniem jednak, zanim zacznie się tworzyć rozbudowane i skomplikowane aplikacje webowe w JEE, warto poznać podstawy - przede wszystkim dlatego, że spora część nowszych i bardziej funkcjonalnych technologii w gruncie rzeczy opiera się na po­ dobnych (ale i udoskonalonych rzecz jasna) zasadach. Poza tym cały czas w interne­ cie funkcjonują witryny wykorzystujące tradycyjne serwIety, a zwłaszcza JSP. Było­ by dziwne, gdyby znawca technologii JSF, Facelets czy JPA musiał się tłumaczyć przed pracodawcą, dlaczego nie może sobie poradzić z przeróbką prostego serwIetu.

Frameworki - kolejny dowód na lenistwo człowieka Do tej pory w niniejszej książce nie przedstawiałem zbyt wielu skomplikowanych przykładów aplikacji webowych. Powód jest prosty - tworzenie takich aplikacji wyłącznie za pomocą serwletów czy JSP jest dość czasochłonne. Załóżmy, że otrzymaliśmy od szefa tradycyjne zadanie - utworzenie sklepu internetowe­ go w JEE. Typowe procesy, które wchodzą w skład działania takiego sklepu, to : • zarządzanie użytkownikami (logowanie, rejestracja, edycja profilu), • przeglądanie oferty sklepu (wg kategorii, wyszukiwanie, zestawienia

popularnych produktów), • koszyk zakupów (przeglądanie i edycja zawartości), • finalizacja zamówienia (podanie adresu, formy płatności itp.), • administrowanie sklepem (zarządzanie produktami, realizacja zamówień,

dodatkowa obsługa użytkowników).

90

Część II • Frameworki webowe

Powyższy wykaz zawiera jedynie najważniej sze (i bardzo ogólne) procesy quasi­ biznesowe, jednak analizując je pod kątem implementacji (w postaci znanych nam serwletów i stron JSP), moglibyśmy wymienić kilkadziesiąt (sic !) możliwych serwletów/ stron JSP, które byłyby konieczne do obsługi takiego sklepu. Według poznanych przez nas schematów każdy serwlet odpowiadałby za realizację logiki biznesowej (z opcjonal­ nym wydzieleniem operacji na bazie danych), a następnie przekazywałby odpowied­ nie dane do wyświetlenia na stronie JSP. Czy nie zauważasz w tym momencie czegoś niedobrego? Taka konstrukcja wymaga tworzenia zbędnego kodu. Chociaż każdy z wyżej wymienio­ nych procesów realizuje swoje zadania, można znaleźć wiele elementów wspólnych: • konieczność zidentyfikowania odpowiedniej akcji na podstawie żądania

użytkownika (adres URL + parametry), • realizacja obsługi danych (bazy danych), • wyświetlenie uzyskanych danych w obrębie strony internetowej , • zarządzanie danymi w obrębie różnych zasięgów (sesja, kontekst aplikacji itp.).

Chęć ujednolicenia działania powyższych i różnych innych mechanizmów prowadzi do tworzenia rozmaitych frameworków. Framework (w ujęciu aplikacji webowych) to zbiór bibliotek i mechanizmów, który pozwala na projektowanie aplikacji webowych w sposób kompleksowy, jednocześnie upraszczając i skracaj ąc ten proces.

JSF - kanonu ciąg dalszy o ile w przypadku podstawowych technologii webowych mamy do czynienia z dość

ograniczonym wyborem (serwletów i JSP nie da się uniknąć), o tyle frameworków we­ bowych mamy do wyboru całe multum. Ponownie jednak rozpoczniemy od standardu - technologii ciągle żywej, używanej w wielu firmach i projektach. Mowa o JavaSelVer Faces. W obrębie Javy EE istnieje tzw. implementacja podstawowa - Mojarra, i to właśnie z niej będziemy korzystać w niniejszej książce. Oprócz tego istnieją imple­ mentacje takie, jak MyFaces czy ICEfaces, które rozszerzają nieco możliwości stan­ dardowego JSF (np. o technologię AJAX). Ale dość gadania - czas umiejscowić JSF pośród innych technologii internetowych w obrębie JEE. Nie jest to zadanie łatwe, gdyż JSF to cały framework do tworzenia apli­ kacji webowych, działający zgodnie z ideą wzorca projektowego MVC (ang. Model View Controller) Wobec tego JSF można określić jako zarządcę różnych współpra­ cujących ze sobą technologii, któremu najbliżej do literki C - kontrolera, ma jednak także wiele wspólnego z V - czyli widokiem. ­

-

-

.

Rozdział 6 . • JavaServer Faces

JSF, czyli MVC

W

91

praktyce

Żeby dobrze zrozumieć zasadę działania frameworka JavaServer Faces (ale nie tylko jego), konieczne jest poznanie idei wzorca projektowego MVC. Wzorce projektowe stanowią niezależne od języka programowania i teclmologii rozwiązania typowych problemów, które można napotkać podczas tworzenia aplikacji. Jednym z pierwszych, dość precyzyjnie określonych wzorców jest MVC, czyli model, widok (View), kon­ troler (Controller). Główna idea wzorca MVC sprowadza się do podzielenia aplikacji na maksymalnie trzy (na ile jest to możliwe) niezależne od siebie warstwy: • Model - reprezentuje rodzaje obiektów-danych wykorzystywanych w aplikacji

(np. Pracown i k, Samochód, Rezerwacj a). Często warstwa ta jest również odpowiedzialna za obsługę mechanizmu dostępu do danych, czyli np. za kontrolę i obsługę połączenia z bazą danych. Trzebajednak przyznać, że w zaawansowanych aplikacjach sam mechanizm bazodanowy powinien być wydzielony . Pozostałe warstwy nie powinny wiedzieć, że za pobranie wszystkich pracowników odpowiedzialne jest zapytanie do bazy danych MySQL SELECT * from Pracown i cy. W modelu dochodzi także do implementacji logiki biznesowej . • Kontroler - łączy pozostałe dwie warstwy i zarządza nimi.

O ile więc pobranie wszystkich pracowników do kolekcji L i st zgodnie z określonymi wytycznymi i ograniczeniami jest zadaniem modelu, o tyle przekazanie tej listy do odpowiedniego widoku należy do zadań kontrolera.

• Widok - odpowiada za prezentację wyników działania kontrolera, a także

przyjmowanie poleceń wydawanych przez użytkownika. Widok nie powinien wiedzieć, ani w jaki sposób dane są pobierane, ani przetwarzane. Jego zadaniem jest tworzenie interfejsu graficznego (najczęściej na podstawie predefiniowanych szablonów, np. stron HTML/JSP) i wypełnianie go danymi pobranymi z pozostałych warstw. Z drugiej strony widok powinien pobierać dane od użytkownika w taki sposób, aby kontroler mógł korzystać bezpośrednio z obiektów określonych wcześniej klas. Powyższy opis nie stanowi jedynej słusznej definicji wzorca MVC. Prawdę mówiąc, z pewnością wiele osób może nie zgodzić się z różnymi fragmentami tej definicji, po­ nieważ wzorzec MVC bywa wykorzystywany w różnych sytuacjach w różny sposób. Czasami zdarza się, że proste aplikacje webowe stanowią jedynie atrakcyjny interfej s dostępu do wybranych tabel z bazy danych - w takiej sytuacji warstwa kontrolera jest uproszczona do minimum. Inaczej wzorzec MVC będzie funkcjonował w aplikacji desktopowej, inaczej w aplikacjach rozproszonych, a jeszcze inaczej w aplikacjach webowych. Nas oczywiście będzie interesować to ostatnie podej ście, zwłaszcza pod kątem JSF.

92

Część II • Frameworki webowe

Kontroler - uniwersalny spawacz Nadszedł czas, aby powyższą definicję wzorca MVC zweryfikować w praktyce, porów­ nując ją z realiami funkcj onowania frameworka JSF. Przede wszystkim framework JSF jako taki nie odpowiada za funkcj onowanie wszystkich trzech warstw wzorca MVC. Naj lepszym tego przykładem jest warstwa widoku. JSF wprowadza swój wła­ sny model komponentów graficznych, obsługi zdarzeń czy walidacji, j ednak istotna

1.0 - 1 .2 obsługa 2.0 zaszczyt ten przy­

rola przypada też jednej z technologii zewnętrznych. W wersjach JSF widoku domyślnie była realizowana przez JSP, a od wersji JSF

padł w udziale frameworkowi Facelets. Nie oznacza to j ednak, że tylko te technologie mogą być wykorzystywane w warstwie prezentacj i - można skorzystać też np. z fra­ meworka XUL. W przypadku stron internetowych ogronrną rolę samą w sobie poza prezentacj ą danych odgrywa pobieranie danych za pomocą różnego rodzaju kontrolek. Od

kilku lat nieomal

standardem w tej kwestii jest wykorzystywanie technologii Aj ax. Obsługa danych wej ściowych wprowadzanych za pomocą stron internetowych jest realizowana bezpo­ średnio przez JSF. Służy do tego celu szereg technik, z których część poznasz w niniej ­ szej książce. Naj mniej szą kontrolę JSF sprawuje nad modelem. Można by wręcz rzec, że framework JSF nie jest zbytnio tym aspektem aplikacj i zainteresowany . Oczywiście nie sposób całkowicie modelu pominąć - najwięcej do czynienia z tą warstwą wzorca MYC mają

ziarna zarządzane (ang. managed/backing beans). Są to specjalne ziarna, które pozwalają na wykorzystywanie danych z warstwy modelu głównie w widoku, dzięki czemu np. przedstawienie danych pracowników jest realizowane w maksymalnie zwięzły sposób.

Małe zanurzenie Naj lepszym sposobem na poznanie najważniej szych aspektów frameworka JSF będzie utworzenie prostej przykładowej aplikacji. W warstwie widoku skorzystamy z możli­ wości faceletów, które obecnie są zalecaną technologią prezentacji we frameworku JSF. Najpierw utworzymy nowy proj ekt w środowisku Netbeans. Modyfikacj a istniej ących proj ektów jest z pewnością bardziej skomplikowana z uwagi na znaczną ingerencję JSF w strukturę proj ektu aplikacj i webowej . Wykonaj te same czynności, co przy okazj i tworzenia zwyklej aplikacj i webowej , ale z j ednym wyjątkiem. W kroku 4. kreatora (Frameworks) zaznacz opcj ę JavaServer Faces. Z listy Registered Libraries wybierz opcję JSF 2.2. Po zamknięciu kreatora Twoim oczom ukaże się domyślna strona powitalna (wygenerowana w technologii Facelets). Zanim przej dziemy do kodu, przeanalizujemy strukturę projektu i zmiany, j akie za­ szły w porównaniu ze standardowym proj ektem aplikacj i webowej . Pierwsza zmiana jest widoczna od razu Gest też najbardziej oczywista) - zamiast stro­ ny JSP otrzymuj emy stronę utworzoną w technologii Facelet. Poza tym standardowo są

Rozdział 6 . • JavaServer Faces

umieszczane dwa pliki konfiguracyjne:

93

context.xml w

podkatalogu META-INF (określa

on ścieżkę kontekstu dla aplikacji) i standardowy deskryptor wdrożenia web.xml. Warto zwrócić uwagę na zawartość tego pliku, bo to właśnie w nim kryje się kilka istotnych rozwiązań pozwalaj ących na poprawne działanie aplikacji JSF. Z naszego punktu widzenia największe znaczenie w pliku

web.xml

ma następujący

fragment:

Faces Servl et javax . faces . webapp . FacesServl et l Faces Servl et /faces/* Jak widać, aplikacj e JSF nie działaj ą w żaden magiczny sposób. Wybrany fragment przestrzeni adresów

FacesServl et.

URL (/facesl*) jest skojarzony ze specjalnym serwletem - klasą

Klasa ta pełni funkcję kontrolera aplikacji JSF i odpowiada za sterowanie

poszczególnymi elementami aplikacji. Oczywiście tak skomplikowany proces nie dzieje się w pełni automatycznie - konieczne jest poinstruowanie kontrolera, w jaki spo­ sób ma się odbywać sterowanie. Do tego celu wykorzystuj e się specjalny plik konfi­ guracyjny faces-config.xml - wystarczy skorzystać z odpowiedniego kreatora:

1.

Kliknij prawym przyciskiem nazwę aplikacj i i wybierz opcj ę

2.

Na liście Categories zaznacz opcję JavaServer Faces, - JSF Faces Configuration.

New/Other.

a na liście File

Types

3. Kliknij przycisk Finish.

W katalogu WEB-INF (domyślnie) zostanie utworzony

plikfaces-config.xml.

Plik ten

zawiera wszelkiego rodzaju ustawienia aplikacji webowej związane z elementami frame­ worka JSF, które nie są określane w pliku

web.xml.

W praktyce w

plikufaces-config.xml

będziemy umieszczali dwie grupy elementów : reguły nawigacji i ziarna zarządzane (zagadnienia te omówimy szczegółowo nieco później ) .

Pierwsze przykłady W tradycyj nych aplikacj ach webowych realizacj a żądania użytkownika przebiegała w sposób j asny i czytelny : • dopasowanie adresu URL żądania do j ednego z serwletów (ew. do plików JSP,

które w praktyce również są serwletami); • wykonanie jednej z metod doGet ( ) ,

do Post ( )

itd. serwIetu;

• w przypadku dużej części serwletów po wykonaniu zadań logiki biznesowej

sterowanie przekazywaliśmy do strony JSP (pamiętasz obiekt typu

RequestDi spatcher?), a wygenerowany kod HTML był przesyłany do użytkownika.

94

Część II • Frameworki webowe

Oczywiście powyższy schemat można rozbudować o interakcj e między dodatkowymi klasami, j ednak jest to ty łko rozszerzenie powyższego schematu. Tak też dziej e się w przypadku JSF - działanie tego frameworka opiera się wszak na kontrolerze

FacesServl et,

dlatego poniższy schemat stanowi niejako rozszerzenie poprzedniego :

• Dopasowanie adresu URL do serwletu - można tak skonfigurować deskryptor

wdrożenia, aby wszystkie żądania aplikacj i trafiały do serwletu

FacesServl et.

• Przekierowanie sterowania do odpowiedniego pliku JSF zgodnie z podanym

adresem URL lub regulami nawigacji. • Przetworzenie wybranej strony JSF (zrealizowanej w technologii

Facelets/JSP/itd.). Jak łatwo zauważyć, schemat j est niby podobny, ale jednak widać znaczące różnice. Z niektórych operacji (dotyczących zwłaszcza przepływu sterowania z serwletu do poszczególnych stron) zostaliśmy bezpośrednio zwolnieni, konieczne jednak staj e się zdefiniowanie w niektórych przypadkach reguł nawigacj i. Gdzie j ednak w tym całym schemacie miej sce na realizacj ę logiki, czyli zadań biznesowych? Wyjaśni się to już w najbliższym przykładzie.

Aplikacja notowania giełdowe Nasza prosta, przykładowa aplikacj a, którą będziemy rozwij ać na przestrzeni najbliż­ szych rozdziałów, pozwoli na pobranie i zaprezentowanie przykładowych notowań giełdowych. Skupimy się głównie na tych aspektach aplikacj i, które wiążą się z JSF; mniej miej sca natomiast poświęcimy obsłudze danych, gdyż temat ten jest dokładnie omówiony w części trzeciej . Z punktu widzenia funkcj onalności użytkownik po wej ściu na stronę główną powi­ nien otrzymać informacj e o aktualnych notowaniach wszystkich dostępnych spółek. Drugi widok zawierać będzie informacj e na temat przeszłych notowań wybranej spółki. Ostatni widok pozwoli na wyświetlenie średniej wartości spółek z wybranego sektora. To zadanie jest ciekawe również z perspektywy modelu (warstwy danych). Na razie nie będziemy jednak koncentrować się na tym aspekcie aplikacj i, ponieważ niezbędne metody zamiast korzystać z rzeczywistego źródła danych, będą zwracały zainicjali­ zowane wartości z odpowiednich list/map. Cała reszta aplikacji będzie kompletna i funkcjonalna. W tej aplikacji możesz skorzystać z utworzonego przed chwilą proj ektu. Tworzenie aplikacji rozpoczniemy od analizy pierwszego widoku, czyli listy wszystkich noto­ wań giełdowych. Musimy określić informacje, które maj ą być wyświetlone na stronie głównej . Chociaż informacj e można przedstawić na różne sposoby, strukturę ziaren zaproj ektuj emy z myślą o później szych interakcj ach z prawdziwą bazą danych.

Rozdział

6. •

95

JavaServer Faces

Z pewnością możemy powiedzieć, że prezentowany będzie zbiór (lista) informacj i te­ go samego typu, czyli notowań giełdowych. Na giełdzie działają różne spółki. Każda spółka zawiera różne właściwości - dla uproszczenia będziemy więc przechowywać nazwę, symbol (unikalny identyfikator) i sektor, w którym działa spółka. Dodatkowo każda spółka zawierać będzie zestaw notowań, czyli informacj e na temat codziennych zmian kursów akcji. Każde notowanie będzie zawierać cenę akcj i, zmianę procento­ wą, dzień notowania i oczywiście odwołanie do swojego rodzica, czyli spółki.



Uwaga

W tym m iejscu muszę wspomn ieć o pewnym mało zauważalnym, ale istotnym drobia­ zgu . Z uwagi na n iedoskonałości związane z przechowywaniem wartości liczbowych w zmiennych typów zmiennoprzecinkowych w normalnych aplikacjach wszelkie opera­ cje związane z kwotami pieniędzy powinny być wykonywane na liczbach całkowitych (reprezentujących l iczbę groszy, centów lub innych naj m n iejszych możl iwych jedno­ stek pien iężnych). Konwersja na typ zmiennoprzecinkowy powinna zachodzić dopiero w momencie wyświetla n i a i tyl ko w tym ce lu wartości zm iennoprzecin kowe powin­ ny być wykorzystywane. Wspomniany przeze mnie problem n ie jest wymyślony - sam musiałem kiedyś u porać się z sytuacj ą , w której wartość zmiennoprzecin kowa 4 . 6 pomnożona przez 100 dała w wyn i ku 459 (aczko lwiek m i ało to m iejsce w apli kacj i tworzonej nie w J avie) . M i m o to d l a u proszczenia działa n i a apli kacj i postanowiłem kursy akcj i przedstawić w postaci zmiennych typu double.

Klasa Spol ka zawiera jedynie opisane powyżej prywatne pola i stosowne metody­ akcesory. Jej kod jest zawarty na listingu

6. 1 . Każda spółka poza kolekcj ą wszystkich

znanych notowań ma bezpośredni dostęp do ostatniego notowania. W tym momencie jedynym powodem wprowadzenia takiego mechanizmu jest uproszczenie kodu, jed­ nak gdy wprowadzimy prawdziwą obsługę danych, rozwiązanie takie stanie się nie­ mal oczywiste. Listing

6.1. Kod klasy-ziarna Spolka package pl . hel i on . j eeweb . notowa n i a g i e l dowe . enti ty : i mport java . uti l .ArrayLi st : i mport java . uti l . Date : i mport java . uti l . Li st : publ i c cl a s s Spol ka { pri vate Stri ng nazwa : pri vate Stri ng symbol : pri vate Stri ng sektor : pri vate Li st notowani a : pri vate Notowa n i e ostatni e : publ i c Spol ka ( ) { } publ i c Spol k a ( Stri ng nazwa , Str i ng symbol , Str i ng sektor) { thi s . nazwa nazwa : th i s . symbol symbol : thi s . sektor sektor : } publ i c Spol k a ( Stri ng nazwa , Str i ng symbol , Str i ng sekto r , doubl e cena , doubl e �zmi ana , Date dzi e n ) { thi s ( nazwa , symbol , sektor ) : =

=

=

96

Część II • Frameworki webowe

=

thi s . notowani a new ArrayLi st ( ) ; thi s . notowani a . add( new Notowani e ( cena , dzi en , zmi ana , th i s ) ) ; publ i c Stri ng getNazwa ( ) return nazwa ; publ i c v o i d setNazwa ( St r i ng nazwa ) thi s . nazwa nazwa ;

}

=

publ i c Stri ng getSymbol ( ) return symbol ; publ i c v o i d setSymbol (Stri ng symbol ) th i s . symbol symbol ;

}

=

publ i c Stri ng getSektor ( ) return sektor ; publ i c v o i d setSektor (Stri ng sektor) thi s . sektor sektor ; =

publ i c Li st getNotowani a ( ) return notowani a ; publ i c v o i d setNotowani a ( Li st notowani a ) thi s . notowani a notowani a ; =

}

publ i c Notowani e getOstatni e ( ) { i f (thi s . ostatni e nul l ) i f ( th i s . getNotowani a ( ) 1 nul l && I th i s . getNotowani a ( ) . i s Empty ( ) ) thi s . ostatni e thi s . getNotowani a ( ) .get ( th i s . getNotowani a ( ) . si ze ( ) return thi s . ostatni e ; ==

=

=

l) ;

publ i c v o i d setOstatni e ( Notowan i e ostatni e ) thi s . ostatni e ostatni e ; =

Jest to z pewnością najciekawsza spośród wszystkich klas. Udostępniamy w niej aż trzy konstruktory - domyślny, wymagany przez Javę do uznania klasy Spol

ka

za

ziarno, trój argumentowy, który pozwala na zainicj alizowanie wszystkich prostych pól tej klasy, i wreszcie sześcioargumentowy, dzięki któremu możemy zainicjalizować kolekcj ę notowań, dodając do niej poj edyncze notowanie . Pozwala to wygodnie za­ inicjalizować kolekcję spółek w jednej z następnych klas. Duże znaczenie ma także pole ostatnie. Jego ogronme znaczenie ujawni się wtedy, gdy w grze poj awi się obsługa prawdziwych danych. Teraz w trakcie wywołania akcesora

get

sprawdzamy, czy pole

to zostało zainicjalizowane, a jeśli nie, przypisujemy mu wartość ostatniego notowa­ nia z listy (mechanizm ten w przyszłości ulegnie istotnym zmianom). Do poprawnego wyrażenia struktury aplikacj i konieczna jest j eszcze j edna klasa ziar­ na, reprezentuj ąca poj edyncze notowanie. Klasa ta nie zawiera żadnych innowacji, niemniej dla porządku prezentuj emy j ą na listingu

6.2.

Rozdział 6 . • JavaServer Faces

97

Listing 6.2. Kod klasy-ziarna Notowanie

package pl . hel i on . j eeweb . notowa n i a g i e l dowe . enti ty : i mport java . uti l . Date : publ i c cl a s s Notowani e { pri vate doub l e cena : pri vate Date dzi en : pri vate doub l e zmi ana : pri vate Spol ka spol ka : publ i c Notowani e ( ) { } publ i c Notowani e ( doubl e cena , Date dzi en , doub l e zmi ana , Spol ka spol ka ) { thi s . cena cena : thi s . dz i en dzi en : thi s . zmi ana zmi ana : th i s . spol ka spol ka : =

=

= =

publ i c doubl e getCena ( ) return cena : } publ i c v o i d setCena ( doub l e cena ) thi s . cena cena : =

publ i c Date getDz i en ( ) return dzi en : publ i c v o i d setDz i en ( Date dzi e n ) thi s . dz i en dzi en : =

publ i c doubl e getZmi ana ( ) return zmi ana : publ i c v o i d setZm i ana ( doubl e zmi an a ) thi s . zmi ana zmi ana : =

publ i c Spol ka getSpol ka ( ) return spol ka : } publ i c v o i d setSpol ka (Spol ka spol k a ) { th i s . spol ka spol ka : =

W tym momencie przygotowaliśmy ziarna niezbędne na tym etapie prac. Do szczę­ ścia potrzebuj emy j eszcze tylko ziarna-menedżera, które będzie zarządzać innymi ziarnami. Z menedżera będziemy korzystać na stronie głównej . Na razie jego zadaniem będzie przygotowanie tymczasowej kolekcji danych (która normalnie będzie pobierana z bazy danych) i udostępnianie j ej w widokach (listing Listing 6.3. Kod klasy-ziarna Menedzer

package pl . hel i on . j eeweb . notowa n i a g i e l dowe . enti ty : i mport java . uti l . * : publ i c cl a s s Menedzer pri vate Li st spol k i :

6.3).

98

Część I I • Frameworki webowe

pub l i c Menedzer ( ) { spol ki = new ArrayLi st( 3 ) ; Oate d = new Oate( ) ; spo l ki . add( new Spo l ka ( "Koci a Spo l ka Weg l owa " , " KSW" , "energetyczny" , 20 . O , 4ll . 1 . d ) ) ; spol ki . add( new Spol ka ( "KitKompOotCom" , "KKOC" , " i nformatyczny" , 90 . 0 , 3 . 4 , d) ) ; spol ki . add( new Spol ka ( "Koci e Dance Studi o" , "KOS" , " rozrywkowy" , 15 . 0 , - ll . 7 , d) ) ; } publ i c Li st getSpol ki ( ) return spo l ki ; } publ i c v o i d setSpol k i ( L i st spol k i ) th i s . spol k i = spol k i ;

W naszej aplikacji nie będziemy zbytnio komplikować kodu staną przedstawione w postaci naj zwyklej szej tabelki

HTML, dlatego dane zo­

« tabl e» .

W tym rozdziale nie

będziemy się zbytnio zagłębiać w tajniki działania Faceletów - wyjaśnię jedynie ten kod, który zostanie wykorzystany bezpośrednio w naszej aplikacji. Przede wszystkim musimy odpowiednio rozplanować interfej s . Każdy wiersz tabeli

« t r»

będzie przedstawiał informacje o jednej spółce. W kolejnych komórkach umie­

ścimy informacj e z ziaren klasy

Spol ka wraz z powiązanym ziarnem klasy Notowan i e.

Naczelnym źródłem danych (z którego pozyskamy dane o poszczególnych spółkach) będzie egzemplarz klasy biblioteki

Menedzer. Dzięki specjalnym znacznikom wchodzącym w skład Face/ets będziemy mogli iterować po naszej kolekcj i notowań. Listing 6.4

przedstawia najistotniej szą część naszej aplikacji: Listing 6.4. Strona index.xhtml przedstawiająca notowania spółek

< I OOCTYPE html PUB L I C " - / /W3C//OTO XHTML 1 . 0 Trans i t i onal / / E N " '' http : / /www . w3 . org/TR/xhtm l l/OTO/xhtm 1 1- tra n s i t i o n a l . dtd"> Facel et Titl e Notowa n i a g i e l dowe NazwaSymbol CenaZmi ana ( % ) #{ spol ka . nazwa } #{ spol ka . symbol } #{ spol ka . ostatni e . cena } #{ spol ka . ostatni e . zmi ana}

Rozdział 6 . • JavaServer Faces

99

Zgodnie z rozszerzeniem plik

index.xhtml

stanowi poprawny dokument j ęzyka XML .

Po prologu, który występuj e we wszystkich dokumentach XML, następuje deklaracj a typu dokumentu (instrukcja

< ! DOCTYPE» ,

a następnie główny znacznik strony interneto­

wej . Warto zwrócić uwagę na zastosowane w dokumencie przestrzenie nazw . Domyślna przestrzeń nazw wskazuje na przestrzeń j ęzyka XHTML (dzięki czemu ze zwykłych znaczników tego języka możemy korzystać bez prefiksu), jednak w tym przypadku korzystamy też z dwóch innych przestrzeni nazw - z technologii JSF i z Facelets. Już na pierwszy rzut oka widać obydwa znaczniki w nowym wydaniu Reprezentują one znaczniki znane z naj zwyklej szego j ęzyka HTML

h : head i h : body. - head i body.

Można więc zapytać - po co ta cala szopka? Otóż w bardziej zaawansowanych zastoso­ waniach taki mechanizm pozwala na lepszą kontrolę umieszczania treści (np. zewnętrz­ nych skryptów czy arkuszy stylów). Jest to zresztą jedynie różnica dwóch znaków różnica nieznaczna, a możliwości, j akby nie patrzeć, większe. Zarówno wewnątrz znacznika

h : head,

jak i

h : body

większość kodu stanowią zwykle

znaczniki języka (X)HTML. Jak już wspomnialem, główną część naszej bardzo skompli­ kowanej strony (wszak jest j eszcze nagłówek ! ) zaj muje tabela. Pierwszy wiersz tabeli definiuje jej nagłówek. Najciekawszy dla nas fragment jest zawarty wewnątrz znacznika

u i : repeat.

Znacznik ten jest jednym ze standardowych znaczników biblioteki Facelets.

Można potraktować go jak faceletowy odpowiednik znanej nam już akcj i Atrybut

val ue

c : forEach. var

przyj muje kolekcj ę danych, które chcemy wyświetlać, zaś atrybut

definiuj e nazwę, pod jaką będzie widoczny element kolekcj i wewnątrz znacznika

u i : repeat. Jak widać, wewnątrz pętli mamy dostęp do pojedynczych ziaren klasy No­ towan i e i po prostu wyświetlamy odpowiednie wartości, tak jak można było to robić w tradycyjnej technologii JSP. Nadszedł czas, aby wyjaśnić największą zagadkę jakim cudem, ot tak sobie, w pliku XHTML uzyskaliśmy dostęp do uprzednio przy­ gotowanej w pliku Javy kolekcji danych?

Tajemniczy zapis Na listingu

-

# vs $

6.4 jedną z największych innowacji stanowi znacznik u i : repeat, a zwłaszcza

wartość jego atrybutu

va l ue.

Innowacyjność wynika z dwóch wprowadzonych w tym

przykładzie nowości: • Nietrudno się domyślić, że zapis

menedzer. notowan i a oznacza pobranie właściwości

notowania z ziarna menedzer. Pozostaj e tylko pytanie - gdzie to ziarno zostało zadeklarowane? • Zapis

# { menedzer. notowan i a }

do złudzenia przypomina j ęzyk EL znany z JSP

- różnica tkwi w j ednym znaku i, co ważne, ta różnica ma znaczenie. Na stronach JSP przed skorzystaniem z ziarna należy zadeklarować akcję

j sp : useBean.

W przypadku stron JSF sytuacja wygląda nieco inaczej . Wszelkie niezbędne informacje

Część II • Frameworki webowe

100

związane z ziamami są deklarowane w plikufaces-config.xml. Listing 6.5 zawiera aktual­ nie wykorzystywaną w aplikacji konfigurację. Listing 6 . 5. Zawartość pliku konfiguracyjnego JavaServer Faces

< I DOCTYPE compo s i t i on PUB L I C " - I IW3C//DTD XHTML 1 . 0 Trans i t i onal l l E N " '' http : / /www . w3 . org/TR/xhtm l l/DTD/xhtm 1 1- tra n s i t i o n a l . dtd"> H i storyczne notowani a spol k i #{menedzer . a ktywnaSpol ka . nazwa } DzienWartoscZmi ana #{notowani e . cena } #{notowani e . zmi ana}

Część II • Frameworki webowe

106

Jak widać, w ziarnie klasy Menedzer zadeklarowaliśmy właściwość a ktywnaSpol ka (klasy Spol ka) . Przechowuje ona informacje o spółce wybranej za pomocą odsyła­ cza na stronie index.xhtml (szczegóły omówimy już za chwilę) . Informacje pobrane z ziarna klasy Spol ka nie powinny zaskakiwać. Najciekawiej prezentuje się znacznik f : convert DateT ime. Pozwala on na zmodyfikowanie sposobu wyświetlania tekstu ­ traktuje wyświetlaną wartość jako datę i wyświetla ją w formacie short. Powyższa strona sarna z siebie nie zadziała. Konieczne jest dodanie właściwości aktywna '-+Spol ka w ziarnie klasy Menedzer, a także skorzystanie z nowego mechanizmu w pliku faces-config.xml. Na początek - plik XML . W pliku faces-config.xml niezbędne jest rozszerzenie definicji ziarna zarządzanego, tak aby wartość przesyłana w parametrze GET o nazwie symbol (pamiętasz adres umiesz­ czany w odsyłaczach?) była automatycznie kopiowana do wybranej właściwości ziarna. Oto kod: menedzer pl . hel i on . jeeweb . notowani ag i el dowe . enti ty . �Menedzer request symbo l AktywnejSpol k i #{ param . symbol }

Dzięki znacznikowi managed-property możemy określić, z jakiej właściwości ziarna chcemy skorzystać, a także jaką wartość chcemy przypisać. Małe przypomnienie: kon­ strukcja # { param. symbol } oznacza parametr żądania (nie mylić z atrybutami!) o nazwie symbol . Dzięki temu, gdy tylko ziarno menedzer zostanie utworzone, nastąpi próba pobra­ nia wartości parametru symbol i umieszczenia jej we właściwości symbol Aktywnej Spo l ki . W przypadku pliku index.xhtml nie będzie to miało praktycznego znaczenia, natomiast w przypadku pliku historia.xhtml - wręcz przeciwnie. Pozostaje nam więc zapoznać się ze zmodyfIkowaną klasą Menedzer. Wzbogacona o dwie właściwości klasa ta jest w stanie obsłużyć oba widoki aplikacji (listing 6. 1 1). Listing 6. 11. Klasa Menedzerpo wprowadzeniu zmian

publ i c cl a s s Menedzer { pri vate Li st spol k i ; pri vate Stri ng symbol AktywnejSpol k i ; pub l i c Menedzer ( ) { spol ki new ArrayLi st ( 3 ) ; =

/* kod tworzący spółki */

} publ i c Li st getSpol ki ( ) return spo l ki ; publ i c v o i d setSpol k i ( L i st spol k i ) th i s . spol k i spol k i ; =

Rozdział 6 . • JavaServer Faces

107

publ i c Spol ka getAktywnaSpol ka ( ) { for ( Spol ka s : thi s . getSpol k i ( ) ) i f ( s . getSymbol ( ) . equa l s ( thi s . getSymbo l AktywnejSpo l k i ( ) ) ) return s : return nul l : publ i c Stri ng getSymbolAktywnejSpol k i ( ) return symbol AktywnejSpol k i : publ i c v o i d setSymbol AktywnejSpol k i ( Stri ng symbol AktywnejSpol ki ) thi s . symbol AktywnejSpol ki symbol AktywnejSpol k i : =

Właściwość aktywnaSpol ka wyróżnia się na dwa sposoby - jest to jedyna właściwość tylko do odczytu (brak metody set), a ponadto metoda ta jako jedyna nie korzysta z fi­ zycznie istniejącego pola obiektu. Zamiast tego metoda, korzystając z symbolu aktyw­ nej spółki (przekazanego za pomocą parametru G ET), wyszukuje spółkę i zwracają! W ten prosty sposób, łącząc możliwości ziaren z prostotą plików konfiguracyjnych, możemy korzystać z bardzo przejrzystych i wygodnych mechanizmów bezpośrednio w widoku aplikacji. Do zrobienia pozostał namjeszcze tylko jeden widok. Po wykonaniu tego zadania przejdziemy do omówienia zasad leżących u podstaw frameworka JSF.

Czas na obliczenia! Ostatni widok nie przynosi tak licznych innowacji jak poprzedni. Tym razem zapre­ zentujemy sumę cen akcji we wszystkich sektorach, jakie są reprezentowane na gieł­ dzie. Mimo licznych podobieństw do poprzednich widoków będziemy musieli roz­ wiązać kilka problemów.

Uwaga

J a ko osoba m ająca w swoim zyclu pewne doświadczenia z giełdą zdaję sobie sprawę, że sens obl iczania sumy cen akcj i dla s półek z określonego sektora nie jest duży . Nie m a jed nak żadnych przeszkód, aby do ziarna Notowan i e dodać atry­ but cenaZysk (i loraz ceny akcji do zysku przypadaj ącego na jedną akcję), który dużo le­ piej odzwierciedla kondycję zarówno poszczególnych spółek, jak i sektorów. Z progra­ m istycznego p u n ktu widzen i a n ie m a różn icy m iędzy obl iczan iem sumy ceny akcj i a współczyn n i ka cena/zysk.

Informacje na naszej stronie zostaną zaprezentowane w formie listy (wykazu). Każdy element wykazu zawierać będzie nazwę sektora i średnią cenę akcji spółek należących do tego sektora. Zaczniemy od utworzenia widoku, a następnie dopasujemy do niego nowe ziarno i ustawienia w pliku konfiguracyjnym. Na początek dodaj nowego klienta szablonu (opcja Facelets Template Clżent ), tak jak w przypadku strony hżstorża.xhtml. Nową stronę nazwij sektory.xhtml. Skorzystaj rzecz jasna z tego samego szablonu - uklad.xhtml. Po utworzeniu klienta szablonu po­ dajemy tytuł odpowiadający treści, a następnie tworzymy główną treść (listing 6. 12). ...

Część II • Frameworki webowe

108

Listing 6.12. Treść pliku sektory.xhtml

< I DOCTYPE compo s i t i on PUB L I C " - I IW3C//DTD XHTML 1 . 0 Trans i t i onal l l E N " '' http : / /www . w3 . org/TR/xhtm l l/DTD/xhtm 1 1- tra n s i t i o n a l . dtd"> Sredn i a cena spol ek w sektorach #{ sekto r } : #{ sektorl nfo . ceny[ sektor J }

Znacznik ui : repeat wykorzystuje nowe ziarno - sektor l n fo. Ziarno to udostępnia dwie kolekcje - sektory (zawiera nazwy sektorów) oraz ceny. Kolekcja ceny zawiera mapę odwzorowującą nazwy sektorów na średnie ceny akcji. !terując po kolekcji nazw sektorów, możemy wewnątrz pętli wyświetlić zarówno nazwę, jak i wartość liczbową. Dużo ciekawiej prezentuje się budowa ziarna sektor l n fo. Z uwagi na stosunkowo duży stopień skomplikowania (w porównaniu do pozostałych funkcjonalności za­ wartych w naszej aplikacji) postanowiłem wydzielić funkcje sektorów do osobnego ziarna. Z punktu widzenia widoku niezbędne jest zdefiniowanie dwóch atrybutów se ktory i ceny. Co więcej , ziarno to musi mieć dostęp do danych z menedżera - ina­ czej nie mogłoby wyłuskać nazw sektorów ani średnich cen (listing 6. 13). Listing 6.13. Treść klasy-ziarna Sektorlnfo

package pl . hel i on . j eeweb . notowa n i a g i e l dowe . enti ty ; i mport java . uti l . * ; publ i c cl a s s Sekto r lnfo { pri vate Menedzer menedzer ; pri vate Map ceny ; publ i c Menedzer getMenedzer ( ) return menedzer ; publ i c v o i d setMenedzer ( Menedzer menedzer) thi s . menedzer = menedzer ; } publ i c Map getCeny ( ) ceny = new HashMap( ) ; for ( Spol ka s : thi s . getMenedzer ( ) . getSpol ki ( ) ) Doub l e wa rtosc = ceny . get ( s . getSektor ( ) ) ; i f (wartosc == nul l ) wa rtosc = 0 . 0 ; ceny . put ( s . getSekto r ( ) , wartosc + s . getOstatni e ( ) . getCena ( ) ) ;

Rozdział 6 . • JavaServer Faces

}

109

retu rn ceny ;

publ i c Object [ ] getSektory ( ) { Map mapa thi s . getCeny ( ) ; return mapa . keySet ( ) . toArray ( ) ; =

Przede wszystkim musimy przechować referencję do obiektu menedżera. Z punktu widzenia tej klasy jest to zwykła właściwość, którą wykorzystujemy także w innych metodach tej klasy. Pozostaje pytanie, w jaki sposób obiekt ziarna menedżera zostanie przekazany do obiektu klasy Sektorl n fo? Zostanie to wyjaśnione za chwilę podczas omawiania pliku konfiguracyjnego. Na razie musimy zagłębić się w metody pobiera­ jące ceny i spółki. Podstawę stanowi metoda getCeny ( ) . Wykorzystując dane o spółkach z menedżera (powinniśmy sprawdzić, czy wyrażenie th i s . getMenedzer() nie zwróci nul l ), wypeł­ niamy mapę zawierającą klucze typu String (nazwy sektorów) i wartości typu doub1 e (sumy cen akcji z danego sektora). Jeśli badana aktualnie spółka należy do sektora, który nie został dodany do mapy, musimy ręcznie ustawić wartość na O i dopiero wtedy dodać cenę akcji danej spółki. Zwróć uwagę, że mapa zawiera tak naprawdę wartości typu Doub1 e, a nie doub1 e. Typ Doub1 e to tzw . wrapper - opakowanie dla typu prymi­ tywnego doub1 e. Dzięki temu wartości prymitywne można traktować tak jak obiekty. Niestety, czasami prowadzi to do uzyskania wartości nu 1 1 , tak jak w tym przypadku. Metoda getSektory ( ) jest znacznie prostsza - pobiera ona klucze z mapy i konwertuje je do tablicy obiektów. Konwersja tajest konieczna, ponieważ znacznik u i : repeat nie radzi sobie z obsługą map ani zbiorów (takich jak zbiór kluczy), natomiast tablica obiektów nie stanowi dla niego żadnego problemu.

Mały zastrzyk Ostatnim zadaniem naszej aplikacji jest powiązanie ze sobą ziaren menedzer i sektorlnfo. Wiemy już, że ziarno sektor l n fo ma możliwość przechowywania referencji do me­ nedzera; pozostaje tylko pytanie, w jaki sposób powinniśmy przekazać referencję jed­ nego ziarna do drugiego. Z pomocą przychodzi nam mechanizm wstrzykiwania zależ­ ności. Sposób jego wykonania zostanie zaprezentowany na przykładzie. Aby nasza aplikacja (a dokładnie widok sektory) działała poprawnie, musisz do pliku

faces-config,xml dodać następujący kod: sektor l nfo pl . hel i on . j eeweb . notowa n i a g i e l dowe . enti ty . Sektor lnfo request menedzer

110

Część I I • Frameworki webowe

#{menedzer}

Kod ten należy wstawić za deklaracją pierwszego ziarna zarządzanego, a przed koń­ cowym znacznikiem faees -eon f i g. Tuż za deklaracją trzech standardowych znaczni­ ków (name, el ass i s eope) znajduje się deklaracja z pozom zwykłej właściwości za­ rządzanej . Tym, co różni ją chociażby od poprzedniego przykładu, jest zawartość. Powyższy kod spowoduje "wstrzyknięcie" do atrybutu menedzer klasy Sekt o r l n fo re­ ferencji do ziarna menedzer klasy Menedzer! Wszystko to dzieje się automatycznie nie musimy się martwić ani o kolejność wykonywanych operacji, ani o samo przepro­ wadzenie operacji kopiowania. Aby zakończyć pracę nad tym widokiem (i tym samym całą aplikacją), proponuję dodać odnośnik do tego widoku, np. na stronie głównej : < h : output L i n k val ue= " facesjsektory . xh tml " > I n formacje o sektorach

Tym samym kończymy pracę nad tą aplikacją (oczywiście na jakiś czas). Teraz przej­ dziemy do teoretycznego omówienia mechanizmów, które poznaliśmy w tym rozdziale.

JSF komponenty, komponenty, komponenty! -

Podstawowym pojęciem związanym z całym frameworkiem JSF jest komponent. Komponent to element wchodzący w skład graficznych aplikacji webowych, mający swój stan i zachowanie. Dzięki komponentom możemy wej ść na wyższy poziom abs­ trakcji w tworzeniu aplikacji webowych. Nie musimy koncentrować się na tworzeniu znaczników HTML - możemy stosować bardziej zaawansowane konstrukcje, repre­ zentujące zarówno typowe elementy języka HTML (j ak poznane znaczniki h : output L i n k - odnośnik), jak i elementy niespotykane w standardzie j ęzyka HTML (np. kalendarz). Z technicznego punktu widzenia komponent to obiekt klasy, która dziedziczy po klasie j avax. faees . eomponent . U I Component. Jest to klasa abstrakcyjna, która udostępnia wiele (oczywiście abstrakcyjnych) metod. Dzięki tej klasie możemy mieć pewność, że wszystkie komponenty (poza swoimi specyficznymi funkcjami) udostępniają nam spójny zestaw podstawowych możliwości. Klasa U I Component ma tylko jedną podklasę - U I ComponentBase. Zgodnie z nazwą stano­ wi ona bazę dla wszystkich innych komponentów - zarówno standardowych, jak i two­ rzonych przez innych programistów. Zanim przejdziemy do omówienia metod z wyżej wspomnianych klas (wspólnych dla wszystkich komponentów), przedstawię kilka typowych, najczęściej spotykanych kon­ trolek. Niektóre z nich zostały zaprezentowane w aplikacji notowania giełdowe.

Rozdział 6 . • JavaServer Faces

111

Output - ( prawie) wszystko, czego do szczęścia potrzeba Na początek muszę zaznaczyć jeden istotny fakt - my tu sobie gawędzimy o klasach, a w kodzie giełdowej aplikacji (w stronach XHTML) roi się od przeróżnych znaczników. Jak się mają owe znaczniki do klas, które za chwilę omówimy? Są to rzecz jasna dwa różne spojrzenia na te same konstrukcje - znaczniki stanowią de facto inny sposób reprezentacji omawianych przez nas komponentów. Większość znanych z języka HTML podstawowych znaczników, które można wyko­ rzystywać w formie komponentów także w JSF, wywodzi się z klasy U l Output. Wszystkie komponenty wywiedzione z klasy U IOutput łączy (zgodnie z nazwą) fakt wyświetlaniaja­ kiejś informacji (statycznej lub dynamicznej) bez możliwości jej edycji. Co ciekawe, wśród " dzieci" klasy U l Output znajduje się także klasa Ul l nput - baza dla wszyst­ kich komponentów umożliwiających nie tylko wyświetlanie, ale i pobieranie wartości od użytkownika! Reasumując, klasa U l Output pośrednio i bezpośrednio odpowiada za całkiem sporą grupę komponentów, z którymi mieliśmy bądź będziemy mieli do czy­ nienia w niniejszej książce. Na razie na nasz użytek wystarczy świadomość istnienia metod getjsetVa l ue ( ) , które pozwalają określić wyświetlaną wartość. Czas na przykłady wzięte z życia - na po­ czątek Html OutputText vel h : o utputText.

h : outputText ( HtmIOutputText) Komponent klasy Html OutputText wykorzystuje wartość właściwości val ue. Tekst jest wyświetlany tak, jak został podany, bez żadnych znaczników, chyba że określono je­ den (lub więcej) z towarzyszących komponentowi atrybutów (przy omawianiu kolej ­ nych klas nie będziemy omawiać powtarzających się atrybutów, o ile nie spowodują one istotnej różnicy w zachowaniu). W takiej sytuacji tekst zostanie ujęty w znacznik span, a określone atrybuty staną się atrybutami tego znacznika. Nazwy atrybutów po­ daję zgodnie ze sposobem zapisu w znacznikach na stronach Facelets. W przypadku korzystania z atrybutów w kodzie Javy należy przekształcić nazwy zgodnie z kon­ wencją ziaren j ęzyka Java. Oto lista atrybutów komponentu Html OutputText : •

d i r - kierunek wyświetlania tekstu (dopuszczalne wartości: L TR - od lewej do prawej i RTL - odwrotnie).



l ang - język stosowany przy generowaniu znacznika dla tego komponentu.



styl e - styl zastosowany dla generowanego tekstu.



styl eCl ass - klasy stylów (może być ich wiele; zapisuje się je po przecinku), które mają być zastosowane do tego komponentu.



t i t l e - dodatkowe informacje na temat wyświetlanego znacznika.



es cape - ten atrybut określa, czy znaki specjalne wchodzące w skład wyświetlanego tekstu « , >, I , , \) mają być zamieniane na encje języka XML (np. < - &l t ; , > - > ;). "

112

Część I I • Frameworki webowe

Przykłady :

HtmlOutputLink (vel h : outputLink) Ten komponent reprezentuje zwykły odsyłacz, czyli znacznik a. Nie należy mylić go z elementem h : commandL i n k, za którym kryją się o wiele bardziej skomplikowane me­ chanizmy. Podobnie jak w przypadku komponentu h : o utputText, właściwości tej kla­ sy pokrywają się niemal całkowicie z atrybutami znacznika znanymi z języka HTML. Istnieje tylko jeden, ale za to istotny wyjątek - klasa Html Output L i n k nie udostępnia metody getjsetHre f ( ) . Adres, pod który odsyła hiperłącze, określa się za pomocą atrybutu val ue. Treść odsyłacza należy umieścić wewnątrz znacznika Html Output L i n ko Przykład: Wydawn i ctwo Hel i on

Ullnput - teraz do szczęścia nie potrzeba już nic Klasa U l l nput pozwala na wprowadzanie i wyświetlanie danych przez użytkownika. Choć z punktu widzenia programowania obiektowego może wydawać się to dziwne, klasa U l l nput dziedziczy bezpośrednio po klasie U l Output. Wprowadza ona jednak dużo interesujących możliwości. Nic dziwnego - w końcu wprowadzanie danych wiąże się ze znacznie większym nakładem pracy. Klasa U l Output wprowadzała mechanizm konwersji danych; z klasą U l l nput wiąże się natomiast walidacja wprowadzonych przez użytkownika danych. Zarówno konwerte­ rom, jak i walidatorom poświęcony jest osobny rozdział w niniej szej książce. Na razie zapamiętaj , że to właśnie w klasie U l l nput znajdują się podstawowe mechanizmy od­ powiadające za proces walidacji.

Pola tekstowe JSF udostępnia trzy rodzaje pól tekstowych: h : i nputText (Html l nputText), h : i nputSecret (Html l nputSecret) i h : i nputTextArea (Html l nputTextArea). Znaczniki te reprezentuj ą trzy znane z języka HTML znaczniki pól tekstowych (odpowiednio): , < i nput type= " pas sword " j> i . Jak zwykle metody tych klas ograniczają się niemal wyłącznie do ustawiania atrybutów znaczników w języku HTML. Poniżej wymienię tylko te najbardziej istotne metody : •

autocompl ete (Html l nputText, Html l nputSecret) - określa, czy jest włączone autouzupełnianie dla pola, czyli czy przeglądarka ma wyświetlać przy danym polu wartości pobrane z historii przeglądania. Wartości on (uzupełnianie włączone) lub o ff.



max l ength (Html lnputText, Html l n putSecret) - określa maksymalną długość wprowadzanego tekstu.



readon l y (wszystkie klasy) - określa, czy pole jest tylko do odczytu (dozwolone zaznaczanie i kopiowanie tekstu).

Rozdział 6 . • JavaServer Faces

113



d i sabl ed (jw.) - określa, czy pole jest wyłączone (pole jest widoczne, ale nie wchodzi w żadną interakcję z użytkownikiem).



red i spl ay (Html I nputSecret) - określa, czy po przesłaniu formularza wartość z tego pola powinna zostać przekazana z powrotem do klienta. Przesyłanie z powrotem zawartości pól i nputSecret nie zawsze jest pożądane - może się to wiązać z umieszczaniem w kodzie HTML wpisanych przez użytkowników haseł. Ten atrybut pozwala kontrolować tego typu zachowanie.



col s, rows (Html InputTextArea) - określa liczbę kolunm i wierszy zawartych w polu i n putTextArea.

Oczywiście we wszystkich trzech przypadkach wartość komponentu jest przechowy­ wana w atrybucie val ue.

Wybór za wyborem . . . Omówione dotychczas komponenty nie były zbyt skomplikowane. Można je w zasa­ dzie nazwać klonami znaczników znanych z języka HTML. Jeśli jednak sądzisz, że taka sielanka utrzyma się do końca tego rozdziału, muszę Cię zmartwić. Pozostałe komponenty graficzne JSF wznoszą się na zdecydowanie wyższy poziom abstrakcji, dlatego w mniej szym stopniu będą nam się one kojarzyć z konkretnymi znacznikami języka HTML. Dobrym przykładem takiego działania są komponenty z rodziny U I Sel ectOne. Kom­ ponenty te pozwalają na wybór jednej z kilku opcji. Pod względem składni na stronie JSF komponenty te są niemal identyczne, jednak generowany przez nie kod HTML jest bardzo różnorodny. Wszystkie komponenty odpowiedzialne za wybór jednego (lub wielu) elementów mają cechy wspólne, dlatego na przykładzie pierwszego z kompo­ nentów zaprezentuję działanie całego mechanizmu. Zaczniemy od komponentu Html Sel ectOneRad i o (h : sel ectOneRad i o). Reprezentuje on listę przycisków typu radio « i nput type= " rad i o " l»�. Zgodnie z defmicją w jednej gru­ pie przycisków rad i o tylko jeden z nich może być zaznaczony. Sam komponent nie jest odpowiedzialny za definiowanie elementów wchodzących w jego skład - ele­ menty są reprezentowane za pomocą znaczników f : sel ect I tem lub f : sel ect I tems. Pierwszy z tych wariantów powinien być wykorzystywany w przypadku deklarowania ze­ stawu stałych elementów, np. wyboru województwa w ankiecie. Taki przykład można by przedstawić w kodzie JSF następująco : < f : sel ectItem i temVa l ue=" l " i temLabe l = " łódz k i e " /> < f : sel ectItem i temVal ue= " 2 " i temLab e l = "mazowi eck i e " /> < 1--

itd.

-->



Każdy z elementów sel ect I tem określa swoją wartość ( i temVal ue) i opis ( i temLabel ). Dzięki temu możemy oddzielić aspekt prezentacji danych (w formie czytelnej dla użyt­ kownika) od ich obsługi (identyfikatory w formie liczbowej są z pewnością bardziej przyjazne dla programistów).

114

Część I I • Frameworki webowe

Oczywiście nic nie stoi na przeszkodzie, aby w ramach atrybutów val ue, i ternVal ue i i ternLabel pojawiały się wartości dynamiczne, pobierane z ziaren zarządzanych. Jednak zwykłe elementy f : se l ect l tern nie pozwalają na dynamiczne określenie liczby elementów - do tego celu służy znacznik f : se l ect l terns:

Wyrażenie podane w atrybucie va l ue elementu f: se l ect l terns musi zwrócić kolekcję obiektów typu U l Sel ect Itern (czyli de facto kolekcję znaczników f : sel ect Itern). Z tego względu w atrybucie val ue komponentu h : sel ectOneRadio odwołujemy się do atrybutu i t ernVal ue właściwości wybraneWoj ewodztwo, zakładając, że właściwość tajest również typu U l Sel ectI tern. Teraz, gdy już wiesz, jak działa mechanizm obsługi danych w komponentach-listach, możemy skupić się na omówieniu konkretnych komponentów. W przypadku kompo­ nentu Htrnl Sel ectOneRad i o istotne znaczenie ma właściwość l ayout. Wartość page '-+D i rect i on wymusi pionowe ustawianie elementów Geden pod drugim), a l i neD i rect i on - poziome.

h : selectOneRadio to nie wszystko - dla każdego coś miłego Wprawdzie zasady działania komponentów-list omówiliśmy na przykładzie klasy Htrn l Se l ectOneRad i o, ale w bardzo podobny sposób można korzystać także z innych list zarówno jednokrotnego, jak i wielokrotnego wyboru. Komponentem bliźniaczo podob­ nym do h : se l ectOneRad i o jest h : se l ectOneMenu. Z punktu widzenia projektanta kompo­ nenty te różnią się jedynie nazwą. Jednak pod względem generowanego kodu HTML różnica jest ogromna - komponent h : se l ectOneRad i o generuje grupę znaczników i nput typu rad i o, podczas gdy h : se l ectOneMenu tworzy znacznik se l ect z możliwością wyboru pojedynczego (ekskluzywnego) znacznika. Poza samą zasadą działania obu rodzajów komponentów różnica tkwi także w ilości zaj mowanego miej sca h : se l ectOneMenu jest komponentem bardziej oszczędnym w tej kwestii. Trio kontrolek pojedynczego wyboru uzupełnia komponent h : se l ectOn e L i stbox. W wynikujego działania otrzymujemy również znacznik sel ect, jednak w postaci listy, a nie rozwijanego pola kombo. Korzystając z atrybutu s i ze, możemy określić liczbę elementów, jaka ma być widoczna jednocześnie na ekranie Geśli lista zawiera więcej elementów, wyświetlany jest suwak). Składnia obu komponentów jest identyczna jak w przypadku kontrolki h : se l ectOneRad i o, dlatego nie będziemy zaj mować się przykładami różniącymi się jedynie nazwą kom­ ponentu.

h : selectMany* - krok dalej w ewol ucji komponentów Kolejną grupę komponentów dziedziczących po klasie Ul l nput stanowią kontrolki wielokrotnego wyboru, dziedziczące bezpośrednio po klasie U l Sel ectMany (a pośrednio oczywiście po U l l nput). Na początek informacja przyjemna - do dyspozycji mamy

Rozdział 6 . • JavaServer Faces

115

trzy komponenty stanowiące pod względem ogólnej koncepcji działania bezpośrednie odpowiedniki komponentów z grupy poprzedniej . Mamy więc do wyboru komponenty h : sel ectManyMenu, h : sel ectManyLi stbox i h : sel ectManyChec kbox (stanowiący odpo­ wiednik kontrolki h : se l ectOneRad i o). Na tym jednak przyjenmości się kończą. Pod względem działania, a także renderowa­ nego kodu HTML można wykazać istotne różnice pomiędzy kontrolkami z obu grup. Przede wszystkim z uwagi na sposób działania w przypadku tych kontrolek nie powin­ no się wykorzystywać standardowych metod getjsetVal ue O do korzystania z wartości! Ponieważ w przypadku kontrolek wielokrotnego wyboru możliwe jest wybranie kilku elementów, wobec tego mamy do czynienia z kolekcją wybranych wartości, a nie z poje­ dynczą wartością. Chociaż czysto teoretycznie można korzystać z metod getjsetVa l ue O , nie jest to zalecane. Metody niezbędne do obsługi kolekcji wartości udostępnia klasa U I Sel ectMany, wobec czego sposób obsługi kolekcji wartości jest identyczny we wszystkich trzech komponentach. W tej grupie mechanizm działania kolekcji wartości zaprezentuję na przykładzie klasy Html Sel ectManyChec kbox (h : sel ectManyChec kbox). Mimo niezbyt podobnej nazwy kom­ ponent ten stanowi bezpośredni odpowiednik klasy Html Se l ectOneRad i o . Wynika to z faktu, że w języku HTML przycisk pojedynczego wyboru jest reprezentowany przez znacznik , natomiast przycisk wielokrotnego wyboru - przez znacznik . Z grupy przycisków rad i o o tej samej nazwie można zaznaczyć tylko jeden element, natomiast w analogicznej sytuacji w przy­ padku przycisków checkbox można zaznaczyć dowolną liczbę elementów (także zero). Etymologię mamy z głowy, czas więc przej ść do działania. Zamiast metod getjset '-+Va l ue O możemy skorzystać z metod getjsetSel ectedVal ues O z klasy U I Sel ectMany. Oto składnia tych metod: •

pub l i c Obj ect [] getSe l ectedVa l ues ( ) - zwraca tablicę zawierającą wartości zaznaczonych elementów lub nul l , jeśli żaden element nie został zaznaczony .



publ i c vo i d setSel ectedVal ues (Obj ect [] tab) - zaznacza elementy o wartościach przekazanych w tablicy. W przypadku przekazania wartości nul l nie jest zaznaczana żadna wartość.

Pisząc o wartościach zaznaczonych elementów, mam na myśli wartości atrybutów i t emVa l ue poszczególnych elementów. Oto przykład:

Zasada działania jest więc podobna - zamiast z atrybutu se l ectedVa l ues można sko­ rzystać z atrybutu val ue, niemniej (zwłaszcza w kodzie Javy) powinno się korzystać z właściwości sel ectedVal ues - jest to po prostu mniej błędogenne. Dwie pozostałe listy wielokrotnego wyboru - h : se l ectManyL i stbox i h : se l ectManyMenu - reprezentują ten sam znacznik - sel ect. Różnica między nimi polega na tym, że kontrolka h : se l ectManyL i stbox pozwala na jednoczesne wyświetlanie wielu elementów,

116

Część I I • Frameworki webowe

natomiast komponent h : se l ectManyMen u wyświetla tyłko jeden element w danej chwili (co nie przeszkadza mu w zaznaczaniu wielu elementów). Można powiedzieć, że komponent h : se l ectManyMenu to lista h : se l ectManyL i stbox ograniczona do wyświetlania maksymalnie jednego elementu w danej chwili.

Wisienka na torcie Do omówienia pozostały nam dwa istotne komponenty, reprezentujące ważne funk­ cjonalności aplikacji webowych. Pierwszy z nich - Html Sel ectBool eanChec kbox odpowiada za wyświetlenie jednego pola typu checkbox. W przeciwieństwie do swo­ jego grupowego odpowiednika ten komponent pozwala na kontrolę stanu wyświetla­ nego checkboksa za pomocą metod i sSe l ected ( ) / setSe l ected ( ) . Tego typu kompo­ nent może być stosowany np. do akceptacji regulaminu strony internetowej . Zamiast tworzyć całą listę pól typu chec kbox i analizować ją pod kątem tylko jednej wartości, możemy skorzystać z osobnego, wyspecjalizowanego rozwiązania. Warto wspomnieć, że klasa Html Sel ectBoo l eanChec kbox dziedziczy po klasie U l Sel ectBoo l ean, która uogólnia pojęcie komponentu przechowującego wartość logiczną. W praktyce kompo­ nent Html Se l ectBool eanCheckbox jest jedyną implementacją komponentu logicznego, wobec czego z klasy U l Sel ectBool ean nie korzysta się zbyt często. Na zakończenie wspomnę o ostatniej z klas - Html l nput H i dden, odpowiadającej za przechowywanie i renderowanie kodu elementów ukrytych - . Co ciekawe, klasa ta nie zawiera żadnych nowych metod ani pól - jej istnienie jest związane wyłącznie z odmiennym (w porównaniu do innych komponentów) sposobem renderowania kodu HTML w przypadku pól ukrytych.

Powrót do szarej rzeczywistości . . . Po omówieniu efektywnych i efektownych kontrolek graficznych musimy powrócić do komponentów, które choć nie mają takiej prezencji, pełnią nierzadko ważne funkcje w aplikacjach webowych Warto zwrócić uwagę na cechę wspólną wszystkich kompo­ nentów, która będzie szczególnie widoczna przy omawianiu kolej nych komponentów - bezpośrednimi potomkami klasy U lComponentBase są komponenty generyczne, okre­ ślające ogólną funkcjonalność określonego typu komponentów. Mieliśmy więc do czynienia z generycznymi listami pojedynczego (U l Se l ectOne) i wielokrotnego (U l Se l ect '-+Many) wyboru, z prostymi komponentami pobierającymi dane wej ściowe (U l l nput) itd. Z reguły istnienie tych klas miało sens, ponieważ niemal w każdym przypadku dziedziczyło po nich wiele konkretnych klas-komponentów. Już jednak w przypadku klasy U l Sel ectBool ean, a także klas, które omówimy za chwilę, tak nie jest - poto­ mek jest tylko jeden. Po co więc zawracać sobie głowę tworzeniem dodatkowych klas U l *? Wyjaśnienie jest proste - Java zawsze stawia na rozszerzalność, także z myślą o przyszłości. Aktualnie jedyną w pełni obsługiwaną technologią renderowania jest HTML, ale nic stoi na przeszkodzie, aby w przyszłości dołączyły do niej inne. Jeśli tak się stanie, zmiany we frameworku będą polegały na dodaniu kolejnych klas­ potomków, takich jak Html Se l ectManyChec kbox czy Html I nputH i dden. Pozostała struktura klas nie będzie wymagała zmian. Po tym przydługawym nieco wstępie mo­ żemy przej ść do omówienia pozostałych komponentów.

Rozdział 6 . • JavaServer Faces

117

U I Form i HtmlForm Klasa U l Form i jej jedyny potomek komponent Html Form reprezentują formularz rende­ rowany na stronie internetowej . To właśnie w ramach tego komponentu powinny być umieszczane wszystkie komponenty pobierające dane wej ściowe (dziedziczące po klasie Ul l nput ) , a także komponenty poleceń (o nich za chwilę). Dobrym zwyczajem jest nadawanie formularzowi identyfikatora. Chociaż rzadko korzysta się z niego tak, jak ma to miej sce w przypadku zwykłych pól, czasami istnieje jednak konieczność umieszczenia kilku formularzy w obrębie jednej strony internetowej i wtedy jawnie określony identyfikator okazuje się bardzo przydatny. Pozostałe metody formularza (zadeklarowane w klasie U l Form) nie mają istotnego znaczenia. Podobnie jak w przypadku pozostałych komponentów funkcjonujących w języku HTML, tak i w przypadku komponentu Html Form możemy określić wszyst­ kie atrybuty tego znacznika za pomocą metod-akcesorów.

U I Command - potężne narzędzie w skromnej formie Do tej pory zapoznaliśmy się z całkiem sporym arsenałem komponentów w JSF. Bra­ kuje nam jednak jednej istotnej rzeczy - czegoś, co pozwoliłoby na przesłanie for­ mularza i realizację żądania. W aplikacji giełdowej nie mieliśmy takiego problemu, ponieważ w żadnym momencie nie wprowadzaliśmy danych za pomocą formularza. W kolejnych przykładach nie unikniemy już formularzy, dlatego zapoznanie się z tytu­ łowym komponentem U lCommand jest konieczne. Komponenty pochodzące od klasy U l Command pozwalają na wywołanie akcji. W praktyce oznacza to możliwość przesłania zawartości formularza do serwera, a przy okazji wywo­ łania dodatkowego kodu. Istnieją dwa rodzaje komponentów akcji - przycisk i hiperłącze. Przyciski (zarówno , jak i Fazy apl i kacj i JSF L i czba l : Li czba 2 :

120

Część II • Frameworki webowe

Pierwsza część kodu strony

index.xhtml nie

zawiera żadnych nowych mechanizmów.

Prawdziwa zabawa zaczyna się wewnątrz formularza

( h : form) . Na tej stronie musieli­

śmy wprowadzić formularz, ponieważ będziemy chcieli wysyłać dane . Mamy tu po raz pierwszy do czynienia w praktyce z polami tekstowymi. Rola identyfikatora jest oczywista - dzięki niemu komponent

h : outputLabel

jest w stanie wyświetlić etykietę

powiązaną z danym polem tekstowym. Zwróć uwagę na identyfikator, z którego ko­ rzystamy wewnątrz komponentów

mul arza : i dPol a.

h : output Label .

Jest to identyfikator postaci

Wynika to z faktu, że w komponencie

h : output Label

i d For­

(który repre­

zentuj e znacznik
Java EE 6. Programowanie aplikacji WWW. Wydanie II - Krzysztof Rychlicki-Kicior (2015)

Related documents

392 Pages • 61,918 Words • PDF • 17.9 MB

6 Pages • 3,815 Words • PDF • 349.6 KB

2 Pages • 23 Words • PDF • 69.7 KB

323 Pages • 182,626 Words • PDF • 20 MB

204 Pages • 41,894 Words • PDF • 2.3 MB

33 Pages • 10,655 Words • PDF • 689.3 KB

50 Pages • 4,610 Words • PDF • 1.7 MB

120 Pages • 35,342 Words • PDF • 11 MB

6 Pages • PDF • 398.7 KB

1 Pages • 216 Words • PDF • 207.2 KB