Geary D. Cay S. Horstmann - Core JavaServer Faces. Wydanie II

323 Pages • 182,626 Words • PDF • 20 MB
Uploaded at 2021-09-24 17:46

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.


Tytuł oryginału: CORE JAVASERVER FACES, Second Edition Tłumaczenie: Mikołaj Szczepaniak ISBN: 978-83-246-1354-0 Authorized translation from the English language edition, entitled: CORE JAVASERVER FACES, Second Edition, ISBN 0131738860, by David Geary, and Cay S. Horstmann, published by Pearson Education, Inc, publishing as Prentice Hall, Copyright © 2007. Polish language edition published by Helion S.A., Copyright © 2008. All rights reserved. No part of this book may be reproduced or transmitted in any form or by any means, electronic or mechanical, including photocopying, recording or by any information storage retrieval system, without pemiission from Pearson Education, Inc. Wszelkie prawa zastrzeżone. Nieautoryzowane rozpowszechnianie całości lub fragmentu niniejszej publikacji w jakiejkolwiek postaci jest zabronione. Wykonywanie kopii metodą kserograficzną, fotograficzną, a także kopiowanie książki na nośniku filmowym, magnetycznym lub innym powoduje naruszenie praw autorskich niniejszej publikacji. Wszystkie znaki występujące w tekście są zastrzeżonymi znakami firmowymi bądź towarowymi ich właścicieli. Autor oraz Wydawnictwo HELION dołożyli wszelkich starań, by zawarte w tej książce informacje były kompletne i rzetelne. Nie biorąjednak żadnej odpowiedzialności ani za ich wykorzystanie, ani za związane z tym ewentualne naruszenie praw patentowych lub autorskich. Autor oraz Wydawnictwo HELION nie ponoszą również żadnej odpowiedzialności za ewentualne szkody wynikłe z wykorzystania informacji zawartych w książce. Wydawnictwo HELION ul. Kościuszki lc, 44-100 GLIWICE tel. 032 231 22 19, 032 230 98 63 e-mail: [email protected] WWW: http://helion.pl (księgarnia internetowa, katalog książek) Drogi Czytelniku! Jeżeli chcesz ocenić tę książkę, zajrzyj pod adres http://helion.pl/user/opinie7javfac Możesz tam wpisać swoje uwagi, spostrzeżenia, recenzję. Pliki z przykładami omawianymi w książce można znaleźć pod adresem: ftp://ftp.helion.pl/przyklady/javfac.zip Printed in Poland.

Spis treści Przedmowa_______________________________________________________________________ 11 Rozdziali. Wprowadzenie.........................................................................................................................15 Dlaczego wybieramy technologię JavaServer F a c e s ? ................................................................................ 1 5 Instalacja oprogram owania ..................................................................................................................................1 6 Prosty przykład ...................................................... 18 Elem enty składowe .......................................................................................................................................... 2 0 Struktura katalogów ......................................................................................................................................... 2 1 Kompilacja przykładowej aplikacji ..............................................................................................................2 2 Analiza przykładowej aplikacji ..............................................................................................................................2 3 Kom ponenty .........................................................................................................................................................2 3 Strony technologii JSF .....................................................................................................................................2 4 N a w ig a c ja .............................................................................................................................................................. 2 7 Konfiguracja serwletu ...................................................................................................................................... 2 9 Strona p o w ita ln a ................................................................................................................................................ 3 0 Środowiska wytwarzania dla J S F ........................................................................................................................3 1 Zintegrowane środowiska programowania .............................................................................................3 1 Narzędzia projektowania wizualnego ........................................................................................................ 3 2 Autom atyzacja procesu kompilacji za pomocą narzędzia Ant ........................................................3 4 Usługi fram eworka J S F ............................................................................................................................................3 7 M echanizm y w e w n ę trz n e ........................................................................................................................................3 9 W izualizacja stron ............................................................................................................................................. 4 0 Dekodowanie żądań .........................................................................................................................................4 1 Cykl życia aplikacji JSF ................................................................................................................................... 4 2

Rozdział 2. Komponenty zarządzane........................................................................................................45 Definicja kom ponentu ............................................................................................................................................. 4 5 W łaściwości kom ponentu ............................................................................................................................. 47 W yrażenia reprezentujące wartości ...........................................................................................................4 9 Pakiety kom unikatów ...............................................................................................................................................5 0 Komunikaty obejm ujące z m ie n n e ............................................................................................................... 5 1 Konfigurowanie ustawień regionalnych aplikacji ........................................................ Przykładowa aplikacja ............................................................................................................................................. 5 4

4

JavaServer Faces Komponenty wspierające ......................................................................................................................................60 Zasięg k o m p o n e n tó w .............................................................................................................................................. 6 1 Komponenty obejm ujące zasięgiem sesję ............................................................................................ 6 1 Komponenty obejm ujące zasięgiem aplikację ..................................................................................... 6 2 Komponenty obejmujące zasięgiem żądanie ........................................................................................ 6 2 Adnotacje cyklu ż y c ia ....................................................................................................................................... 6 3 Konfigurowanie kom ponentów ..................................................................................................... 64 Ustawianie wartości właściwości .......................................................................................... 65 Inicjalizacja list i map ......................................................................................................................................6 5 W iązanie definicji komponentów ................................................................................................................ 66 Konwersje łańcuchów ...................................................................................................................................... 68 Składnia wyrażeń reprezentujących wartości ............................................................................................... 6 9 Stosow anie nawiasów kwadratowych .......................................................................................................7 1 Wyrażenia odwołujące się do map i list .................................................................................................. 7 1 Rozwiązywanie wyrazu początkowego .......................................................................................................7 2 W yrażenia z ło ż o n e ............................................................................................................................................. 7 4 Wyrażenia odwołujące się do m etod ........................................................................................................ 7 5

Rozdział 3. Nawigacja.............................................................................................................................. 77 Nawigacja statyczna ................................................................................................................................................7 7 Nawigacja dynamiczna ..................................................................................... ■.................................................... 7 9 Zaaw ansow ane techniki n a w ig a c ji..................................................................................................................... 88 Przekierowanie .................................................................................................................................................. 8 9 Symbole wieloznaczne .................................................................................................................................... 90 Stosowanie elem entu fro m -a c tio n ..............................................................................................................9 1 Algorytm n aw ig a cji................................................................................-........................................................... 9 2

Rozdział 4. Znaczniki standardowe JS F................................................................................................. 95 Przegląd podstawowych znaczników J S F ........................................................................................................9 6 Przegląd znaczników JSF reprezentujących znaczniki HTML (JSF HTML) ......................................... 9 8 Atrybuty wspólne ......................................... -........................................................... 1 0 ° 106 F o rm u larze...................................... Elementy formularzy i skrypty języka JavaScript ................................................................................ 1 0 8 Jedno- i wielowierszowe pola tekstow e ................................................................................................1 1 1 Pola ukryte ........................................................................................................................................................ 1 1 4 Stosowanie jedno- i wielowierszowych póltekstowych ................................................................... 1 1 4 W yświetlanie tekstu i o b ra z ó w ............................................................ 118 Przyciski i łącza ....................................................................................................................................................-1 2 0 Stosow anie przycisków poleceń ..............................................................................................................1 2 1 Stosow anie łączy poleceń .......................................................................................................................... 1 2 6 Znaczniki selekcji ............................................................................................................. 180 Pola wyboru i przyciski opcji ...................................................................................................................... 1 8 3 Menu i listy ............................................................................................................................................. 185 E le m e n ty .............................................................................................................................................................1 8 8 Komunikaty ..............................................................................................................................................................1 5 4 P a n e le ......................................................................................................................................................................... 1 5 9

Rozdział 5. Tabele danych___________________________________________________________165 Znacznik tabeli danych — h:dataTable ........................................................................................................ 1 6 5 Prosta tabela ........................................................................................................................................................... 1 6 6 Atrybuty znacznika h:dataTable ............................................................................................................... 1 6 9 Atrybuty znacznika h:column .............................................. 170

Nagłówki, stopki i p o d p is y ..................................................................................................................................1 7 1 Kom ponenty JSF .................................................................................................................................................... 1 7 4 Edycja kom órek t a b e li.......................................... 177 Style ............................................................................................................................................................................ 1 8 0 Style stosowane dla kolumn .....................................................................................................................1 8 1 182 Style stosow ane dla wierszy ............................ Tabele bazy danych .............................................................................................................................................. 1 8 3 Obiekty wyników biblioteki JSTL kontra zbiory w y n iko w e ............................................................... 1 8 7 M odele tabel ........................................................................................................................................................... 1 8 7 Edycja modeli tabel ....................................................................................................................................... 1 8 8 Sortowanie i filtrowanie ...............................................................................................................................1 9 2 Techniki przewijania .............................................................................................................................................2 0 1 Przewijanie z użyciem paska przewijania .............................................................................................2 0 1 Przewijanie za pomocą dodatkowych łą c z y ..........................................................................................2 0 2

Rozdział 6. Konwersja i weryfikacja poprawności danych................................................................205 Przegląd procesu konwersji i weryfikacji poprawności .......................................................................... 2 0 5 Stosow anie konwerterów stan d ard o w ych ....................................................................................................2 0 7 Konwersja liczb i dat .....................................................................................................................................2 0 7 Błędy konwersji ......................................................................................................... 211 Kom pletny przykład konwertera ................................................. 216 218 Stosow anie standardowych m echanizm ów weryfikujących ................. W eryfikacja długości łańcuchów i przedziałów liczbowych ............................................................ 2 1 9 W eryfikacja wartości wymaganych ..........................................................................................................2 2 0 W yświetlanie kom unikatów o błędach weryfikacji ............................................................................2 2 1 Pom ijanie procesu w e ry fik a c ji....................................................................................................................2 2 1 Kompletny przykład m echanizm u weryfikacji ......................................................................................2 2 3 Program ow anie z wykorzystaniem niestandardowych konwerterów i m echanizm ów weryfikujących ..................................................................................................................... 2 2 4 Im plem entacja klas konwerterów niestandardow ych ...................................................................... 2 2 5 Im plem entacja klas niestandardowych m echanizm ów weryfikacji ........................................... 2 3 7 R ejestrowanie własnych m echanizm ów weryfikacji .........................................................................2 4 0 W eryfikacja danych wejściowych za pomocą metod kom ponentów JavaBeans ................. 2 4 2 Przekazywanie konwerterom a try b u tó w ................................................................................................. 2 4 2 Relacje weryfikacji łączące wiele kom ponentów ...............................................................................2 4 3

Rozdział 7. Obsługa zdarzeń.................................................................................................................. 249 Zdarzenia cyklu życia ............................................................................................................................................2 5 0 Zdarzenia zm iany wartości ................................................................................................................................ 2 5 1 Zdarzenia akcji .........................................................................................................................................................2 5 6 Znaczniki m etod nasłuchujących z d a rz e ń ....................................................................................................2 6 3 Znaczniki f:actionListener i f:valueChangeListener ......................................................................... 2 6 4 Kom ponenty bezpośrednie ................................................................................................................................ 2 6 5 Stosow anie bezpośrednich kom ponentów wejściowych ...............................................................2 6 6 Stosow anie bezpośrednich kom ponentów poleceń .........................................................................2 6 8 Przekazywanie danych z interfejsu użytkownika na s e r w e r ..................................................................2 6 9 Znacznik f:param ............................................................................................................................................2 7 0 Znacznik f.attribute ........................................................................................................................................2 7 1 Znacznik f:setPropertyActionUstener ................................................................. 272 Zdarzenia f a z y .......................................................................................................................................................... 2 7 3 Podsum owanie całego m ateriału w jednym m ie js c u ...............................................................................2 8 1

6

JavaServer Faces Rozdział 8. Podwidoki i pakiet Apache Tiles...................................................-...................................... 291 Typowe rozmieszczenia ....................................................................................................................................... 2 9 1 Przeglądarka książek i biblioteka ................................................................................................................... 2 9 2 Przeglądarka książek ............................................................................................................................................2 9 4 Monolityczne strony JSF ............................................................................................................................2 9 5 Dołączanie wspólnej treści ....................................................................................................................... 3 0 0 Dołączanie treści w aplikacjach zbudowanych na bazie technologii J S P ..............................3 0 0 Dołączanie treści w kontekście aplikacji J S F ......................................................................................3 0 1 Dołączanie treści w ramach aplikacji przeglądarki książek ................................................................ 3 0 2 Prezentacja pakietu Apache Tiles ...........................................................................................................3 0 5 Instalacja pakietu T ile s .............................................................................................................................. 3 0 5 Stosowanie pakietu Tiles w ramach aplikacji przeglądarki k s ią ż e k ......................................... 3 0 6 Parametryzacja kafelków ............................................................................................................................3 0 8 Rozszerzanie k a fe lk ó w ..................................................................................................................................3 0 9 Biblioteka ........................................................................... -.....................................................................................3 1 2 Kafelki zagnieżdżone .................................................................................................................................... 3 1 3 Kontroler kafelków .........................................................................................................................................3 1 3

Rozdział 9. Niestandardowe komponenty, konwertery i mechanizmy weryfikujące...................... 323 Klasy im plem entujące kom ponenty niestandardow e .............................................................................3 2 5 Znaczniki i komponenty J a v a B e a n s ........................................................................................................3 2 7 Zestaw narzędzi twórcy kom ponentów niestandardowych ...........................................................3 2 8 Kodowanie: generowanie znaczników ...........................................................................................................3 3 0 Dekodowanie: przetwarzanie wartości żądania ........................................................................................ 3 3 4 Stosowanie konwerterów ............................................................................................................................3 3 7 Im plem entacja znaczników kom ponentów niestandardow ych ............................................................3 3 9 Plik deskryptora TLD .................................................................................................................................... 3 4 0 Klasa obsługująca znacznik ...................................................................................................................... 3 4 3 Aplikacja zbudowana z wykorzystaniem kontrolki datownika ...................................................... 3 4 6 Definiowanie klas obsługujących znaczniki w technologii JSF 1 .1 ...........................................3 4 9 Doskonalenie komponentu datownika ......................................................................................................... 3 5 4 Stosow anie zewnętrznych m echanizm ów wizualizacji ........ 354 Wywoływanie konwerterów z poziomu zewnętrznych m echanizm ów wizualizacji ................3 5 9 Obsługa metod nasłuchujących zmian wartości ........................................................................... 3 6 0 Obsługa wyrażeń wskazujących na metody ........................................................................................ 3 6 1 Przykładowa a p lik a c ja ...................................................................................................................................3 6 3 Użycie skryptu JavaScript do ograniczenia komunikacji z serwerem ..............................................3 6 9 Stosow anie kom ponentów i facet potomnych ..........................................................................................3 7 2 Przetwarzanie znaczników potomnych typu S e le c tlte m ..................................................................3 7 5 Przetwarzanie facet ....................................................................................................................................... 3 7 6 Kodowanie stylów CSS ................................................................................................................................ 3 7 7 Stosowanie pól ukrytych ..............................................................................................-............................. 3 7 8 Zapisywanie i przywracanie stanu ...........................................................................................................3 7 9 Generowanie zdarzeń akcji ................................... 382 Stosow anie komponentu panelu podzielonego na zakładki ............... 387 Im plem entacja niestandardowych konwerterów i m echanizm ów weryfikacji ............................... 3 9 3 Znaczniki konwerterów niestandardowych ..........................................................................................3 9 3 Znaczniki niestandardowych m echanizm ów weryfikacji ................................................................ 4 0 1

Spis treści Rozdziano. Usługi zewnętrzne..........................

7 409

Dostęp do bazy danych za pośrednictwem interfejsu JDBC ........................................ 409 Wykonywanie wyrażeń języka S Q L ...........................................................................................................4 0 9 Zarządzanie połączeniam i ...........................................................................................................................4 1 1 Elim inowanie wycieków połączeń ............................................................................................................ 4 1 1 Stosow anie gotowych w y ra ż e ń ..................................................................................................................4 1 3 Konfigurowanie źródła danych ......................................................................................................................... 4 1 4 Konfigurowanie zasobów baz danych w ramach serwera G la s s F is h ........................................ 4 1 5 Konfigurowanie zasobów baz danych w ramach serwera Tom cat ............................................ 4 1 7 Uzyskiwanie dostępu do zasobów zarządzanych przez kontener ..............................................4 1 9 Kompletny przykład użycia bazy danych ............................................................................................... 4 2 0 W prowadzenie do technologii L D A P ............................................................................................................... 4 2 8 Katalogi LDAP ................................................................................................... 428 Konfigurowanie serwera LDAP .................................................................................................................. 430 Uzyskiwanie dostępu do informacji składowanych w katalogach LDAP .................................4 3 3 Zarządzanie danymi konfiguracyjnymi ...........................................................................................................4 3 8 Konfigurowanie kom ponentu ..................................................................................................................... 438 Konfigurowanie kontekstu zewnętrznego .............................................................................................440 Konfigurowanie zasobu zarządzanego przez k o n te n e r................................................................... 4 4 1 Tworzenie aplikacji LDAP ............................ 445 Uwierzytelnianie i autoryzacja zarządzana przez k o n te n e r................................................................... 455 Stosow anie usług sieciowych .......................................................................................................................... 4 6 4

Rozdziani. AJAX

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

475

Podstawy techniki A JA X ....................................................................................................................................... 476 Biblioteki języka JavaScript ............................................................................................................................... 478 Biblioteka Prototype .............................................................................. 479 Biblioteka Fade Anything Technique .......................................................................................................479 Uzupełnianie danych fo rm u la rz y ...................................................................................................................... 48O W eryfikacja w czasie rzeczywistym .................................................................................................................4 8 2 Propagowanie stanu widoku po stronie k lie n ta .........................................................................................4 8 7 Biblioteka Direct W eb Remoting ..................................................................................................................... 488 Kom ponenty AJAX .................................................................. 490 Kom ponenty hybrydowe ......................................................................................................... 491 Izolowanie kodu języka JavaScript od m echanizm ów wizualizacji ............................................ 495 Przekazywanie atrybutów znaczników JSP do kodu języka J a v a S c rip t..................................... 4 9 6 A jax4jsf ..................................................................................................................................................................... ... Im plem entow anie m echanizm u uzupełniania danych formularzy z wykorzystaniem fram eworku Ajax4jsf ............................................................................................. 498 Im plem entacja m echanizm u weryfikacji w czasie rzeczywistym z wykorzystaniem fram eworku A jax4jsf ............................................................................................. 502

Rozdział 12. Frameworki open-source................................................................................................... 511 Przepływ stron WWW — pakiet Shale ........................................................................................................... 512 Konfiguracja dialogu ........................................................................................................................................ Inicjowanie dialogu ........................................................................................................................................... Nawigacja w ram ach dialogu ................................................................................................................... ... Zasięg dialogu ............................................................. 5j_g Zależność od kontekstu dialogu .............................................................................................................. 520 Poddialogi ........................................................................................................................................................ ... Alternatywne technologie widoku — F a c e le ts .......................................................................... 524 widoki x h t m l .................................................................................................................... i!!!!!'.!!!!!!!!!!” ! 525 Zastępow anie znaczników kom ponentam i JSF — atrybut j s f c ....................................................5 2 6

8

JavaServer Faces Stosowanie znaczników technologii JSF ..............................................................................................5 2 9 Kompozycje stron złożonych z szablonów ........................................................................................... 5 3 1 Znaczniki niestandardowe technologii Facelets ................................................................................5 3 3 Integracja z technologią EJB — Seani ......................................................................................................... 5 3 5 Książka adresowa ......... 535 Konfiguracja ..................................................................................................................................................... 5 3 9 Komponenty encyjne .................................................................................................................................... 5 4 0 Stanowe komponenty sesyjne ..................................................................................................................5 4 1 Integracja z modelem danych technologii JSF ...................................................................................5 4 4 Zasięg ko n w e rs ac ji.........................................................................................................................................5 4 5

RozdziaM3. Jak to zrobić?.....................................................................................................

547

Projektowanie interfejsu użytkownika aplikacji internetowej ...............................................................5 4 7 Gdzie należy szukać dodatkowych kom ponentów? .........................................................................5 4 7 Jak zaim plem entować obsługę wysyłania plików na serwer? .....................................................5 5 0 Jak wyświetlać m apę obrazów? ............................................................................................................... 5 5 8 Jak dołączać aplet do naszej strony? ....................................................................................................5 5 9 Jak generować dane binarne w ramach stron J S F ? .........................................................................5 6 1 Jak prezentować ogromne zbiory danych podzielone na m niejsze strony? ..........................5 7 0 Jak generować wyskakujące okna? ........................................................................................................5 7 4 Jak selektywnie prezentować i ukrywać k o m p o n e n ty ? ................................................................... 5 8 2 Jak dostosowywać wygląd stron o błędach? ...................................... .............................................. 5 8 3 W eryfikacja danych ................................................................................................................................................5 8 7 Jak utworzyć własny, niestandardowy znacznik weryfikacji po stronie klienta? ................. 5 8 7 Jak weryfikować dane po stronie klienta za pomocą mechanizm u Shale V a lid a to r? 593 Jak weryfikować relacje pomiędzy k o m p o n e n ta m i? .........................................................................5 9 5 Programowanie .......................................................................................................................................................5 9 6 Jak tworzyć aplikacje JSF w środowisku Eclipse? ............................................................................5 9 6 Jak zm ieniać lokalizację plików konfiguracyjnych? .......................................................................... 5 9 9 Jak komponenty JSF mogą uzyskiwać dostęp do zasobów dostępnych w pliku JA R ? 600 Jak spakować zbiór znaczników w ramach pliku JAR? ................................................................... 6 0 4 Jak uzyskać identyfikator form ularza niezbędny do wygenerowania struktury docum ent.form s[id] w języku J a v aS c rip t? ........................................................................................ 6 0 4 Jak sprawić, by funkcja języka JavaScript była definiowana tylko raz dla danej strony? ..........6 0 5 Jak realizować zadania związane z inicjalizacją i przywracaniem oryginalnego stanu ś ro d o w is k a ? ...........................................................................................................6 0 5 Jak składować kom ponent zarządzany poza zasięgiem żądania, ale w czasie krótszym od okresu istnienia zasięgu sesji? ...................................................... 6 0 6 Jak rozszerzyć język wyrażeń technologii J S F ? ...................................................................................6 0 7 Diagnostyka i rejestrowanie zdarzeń ............................................................................................................ 6 1 1 Jak rozszyfrować ślad stosu? ................................................................................................................... 6 1 1 Jak unikać „śladów stosu z piekła rodem "? ..................................................................................... 6 1 3 Jak wdrażać aplikacje „na gorąco”? ...................................................................................................... 6 1 4 Jak um ieścić w komentarzu wybrany fragm ent kodu strony JSF? ............................................ 6 1 5 Gdzie należy szukać plików d z ie n n ik a ? .................................................................................................6 1 6 Jak sprawdzić, które param etry przekazano za pośrednictwem naszej s tro n y ? ................. 6 1 7 Jak włączyć tryb rejestrowania zdarzeń związanych z pracą kontenera JSF? .......................6 2 0 Jak diagnozować stronę, na której zatrzymała się nasza aplikacja? .......................................6 2 2 Gdzie należy szukać kodu źródłowego biblioteki? ............................................................................6 2 4

Skorowidz...............................................................................................................................................625

Podziękowania W pierwszej kolejności chcielibyśmy podziękować Gregowi Doenchowi, redaktorowi z ramie­ nia wydawnictwa Prentice Hall, który był naszym przewodnikiem w pracach nad tym projektem i nigdy nie dał po sobie poznać zniechęcenia niezliczonymi opóźnieniami i komplikacjami. Jesteśmy bardzo wdzięczni korektorom, którzy spisali się na medal, znajdując sporo błędów i sugerując poprawki na wszystkich etapach tworzenia tej publikacji. Są to następujące osoby: ■ Gaił Anderson, Anderson Software Group, Inc.; ■ Larry Brown, LMBrown.com, Inc.; ■ Frank Cohen, PushToTest; ■ Brian Goetz, Sun Microsystems, Inc.; ■ Rob Gordon, Crooked Furrow Farm; ■ Marty Hall, autor książki pt. Core Java Servlets and JavaServer Pages', ■ Charlie Flunt, Sun Microsystems, Inc.; ■ Je ff Langr, Langr Software Solutions; ■ B ill Lewis, Tufts University; ■ Je ff Markham, Markham Software Company; ■ Angus McIntyre, IB M Corporation; ■ John Muchow, autor książki pt. Core J2ME ; ■ Dan Shellman, BearingPoint; ■ Sergei Smirnov, główny architekt modułu Exadel JS F Studio; ■ Roman Smolgovsky, Flytecomm; ■ Stephen Stelting, Sun Microsystems, Inc.; ■ Christopher Taylor, Nanshu Densetsu;

10

JavaServer Faces ■ Kim Topley, Keyboard Edge Limited; ■ Michael Yuan, współautor książki pt. JBoss Seam: Simplicity and Power Beyond

Java EE. I wreszcie chcielibyśmy wyrazić wdzięczność naszym rodzinom i przyjaciołom, którzy wspie­ rali nas przez cały czas trwania tego projektu i stale wykazywali wiarę w jego powodzenie.

Przedmowa Kiedy po raz pierwszy usłyszeliśmy o technologii JavaServer Faces (JS F ) na konferencji JavaOne w roku 2002, byliśmy bardzo podekscytowani. Obaj mieliśmy spore doświadczenie w pracy nad klienckimi aplikacjami Javy i dzieliliśmy się swoimi spostrzeżeniami ze spo­ łecznością programistów — David w książce Graphie Java™; Cay w książce Core Java™ (obie wydane nakładem wydawnictwa Sun Microsystems Press). Nasze pierwsze odczucia związane z programowaniem aplikacji internetowej za pomocą technologii serwletów i JavaSe­ rver Pages (JS P ) były raczej negatywne — obie technologie wydały nam się mało intuicyj­ ne i pracochłonne. Twórcy technologii JavaServer Faces zapowiadali, że zmienią oblicze aplikacji internetowych na bardziej przyjazne, aby programiści wreszcie mogli się koncen­ trować na polach tekstowych i menu, zamiast tracić czas na edycję kodu stron i operowanie na parametrach żądań. Obaj zaproponowaliśmy wydawcy Sun Microsystems Press projekty książek poświęconych tej technologii i w odpowiedzi usłyszeliśmy sugestię połączenia sił. Wydanie specyfikacji JS F 1.0 i implementacji referencyjnej zajęło grupie JS F Expert Group (do której należał David) trochę czasu — efekt pracy grupy opublikowano w 2004 roku. Niedługo potem wydano zbiór poprawek w ramach wersji 1.1. W roku 2006 wydano nową specyfikację oznaczoną numerem 1.2, obejmującą szereg rozwiązań porządkujących dotych­ czasowe mechanizmy i ułatwiających pracę programistów. JS F jest obecnie najdoskonalszym z frameworków dla pisanego w Javie oprogramowania serwerów i spełnia większość pokładanych w nim nadziei. Wreszcie możemy projektować interfejsy użytkownika, umieszczając na formularzu komponenty i wiążąc je z obiektami Javy bez konieczności mieszania kodu źródłowego ze znacznikami. Mocnym punktem technologii JavaServer Faces jest rozszerzalny model komponentowy i ogromna liczba komponentów opracowywanych przez niezależnych programistów i organizacje. Co więcej, elastyczny projekt tego frameworku umożliwił rozwój nowych technologii, takich jak A JA X . Framework JS F zaprojektowano też z myślą o współpracy z narzędziami wspierającymi programistów, w tym dostępnymi od niedawna środowiskami do budowy graficznych interfejsów użytkownika metodą przeciągnij i upuść. I wreszcie, w przeciwieństwie do technologii konkurencyjnych, które od razu rzucają programistów na głęboką wodę, JS F oferuje mechanizmy rozwiązujące najtrudniejsze problemy — izolacji prezentacji od logiki biznesowej, nawigacji, zarządzania połączeniami z usługami zewnętrznymi oraz zarządzania konfiguracjami.

12

Przedmowa

JavaServer Faces Nasz zachwyt nad technologią JS F do tej pory nie minął — chcielibyśmy się podzielić tą fascynacją z programistami poszukującymi technologii, która uczyni ich pracę nad aplikacjami internetowymi bardziej efektywną.

0 książce Książka powstała zarówno z myślą o programistach koncentrujących się na projektowaniu interfejsów użytkownika aplikacji internetowych, jak i z myślą o programistach implementu­ jących komponenty wielokrotnego użytku dla aplikacji internetowych. Takie podejście znacznie odbiega od założeń, które przyświecały autorom oficjalnej specyfikacji JS F , czyli dość pompatycznego dokumentu napisanego — jak się wydaje — raczej z myślą o progra­ mistach implementujących frameworki oraz znudzonych bezczynnością autorach książek. W pierwszej części tej książki (w rozdziałach 1 - 6) skoncentrujemy się na znacznikach frameworku JSF . Prezentowane znaczniki pod wieloma względami przypominają znaczniki formularzy języka H TM L. Właśnie znaczniki JS F pełnią funkcję podstawowych cegiełek składających się na interfejsy użytkownika budowanych aplikacji internetowych. Stosowa­ nie tych znaczników w ogóle nie wymaga programowania. Oznacza to, że lektura pierwszej części książki wymaga tylko podstawowej wiedzy o stronach internetowych w formacie H TM L i stosunkowo niewielkich umiejętności programowania logiki biznesowej w Javie. W pierwszej części tej książki omówimy następujące zagadnienia: ■ konfigurowanie środowiska programowania (rozdział 1.); ■ wiązanie znaczników JS F z logiką aplikacji (rozdział 2.); ■ nawigacja pomiędzy stronami (rozdział 3.); ■ stosowanie standardowych znaczników JS F (rozdziały 4. i 5.);

13

Książkę zakończymy rozdziałem, który w założeniu ma odpowiadać na typowe pytania w formie „Jak to zrobić?” (patrz rozdział 13.). Zachęcamy Czytelników do możliwie częste­ go zaglądania do tego rozdziału, kiedy tylko opanują podstawy technologii JS F . Można tam znaleźć pomocne wskazówki na temat diagnozowania i rejestrowania zdarzeń, a także szczegóły implementacyjne i praktyczne przykłady kodu rozszerzającego technologię JS F o brakujące komponenty, w tym komponenty umożliwiające wysyłanie plików, wyświetlanie wyskakujących okien i dzielenie długich tabel pomiędzy wiele stron. Technologię JS F zbudowano co prawda ponad istniejącymi technologiami serwletów i JSP , jednak z perspektywy programisty aplikacji JS F obie technologie tworzą co najwyżej nislcopoziomowe „wnętrzności” . Chociaż znajomość innych technologii aplikacji internetowych, jak serwlety, JS P czy Struts, z pewnością nie zaszkodzi, nie jest konieczna do pracy z tą książką.

Wymagane oprogramowanie Całe oprogramowanie niezbędne do eksperymentów z przykładami prezentowanymi w tej książce jest dostępne za darmo w intemecie. Będziemy potrzebowali pakietu Java Software Development K it (Java SD K ) firmy Sun Microsystems, a także serwera aplikacji oferującego obsługę technologii JSF , np. doskonałego serwera GlassFish udostępnianego w trybie opensource. Wymienione oprogramowanie działa identycznie w takich systemach operacyjnych jak Linux, Mac OS X, Solaris czy Windows. Pracując nad przykładowym kodem, korzystali­ śmy z platformy Java 5, serwera GlassFish oraz systemów operacyjnych Linux, Mac OS X i Windows. Czytelnikom zainteresowanym środowiskiem wytwarzania oprogramowania przygotowa­ nym do tworzenia aplikacji JS F z czystym sumieniem rekomendujemy darmowe narzędzie NetBeans. Dość dobrą obsługę technologii JS F zapewniają też rozszerzenia środowiska Eclipse oferowane przez wielu niezależnych producentów.

■ konwersja i weryfikacja danych wejściowych (rozdział 6.). Począwszy od rozdziału 7., przystąpimy do naprawdę poważnego programowania aplikacji JSF. Z rozdziałów 7-12 można się będzie dowiedzieć, jak realizować zaawansowane zadania i jak rozszerzać sam framework JS F . Główne zagadnienia poruszane w drugiej części tej książki wymieniono poniżej: ■ obsługa zdarzeń (rozdział 7.);

Kod źródłowy Kod źródłowy przykładów przedstawionych w tej książce można pobrać z internetu, spod adresu ftp://ftp.helion.pl/przyklady/javfac.zip.

■ dołączanie wspólnej treści do wielu stron internetowych (rozdział 8.); ■ implementacja własnych, niestandardowych komponentów, konwerterów i mechanizmów weryfikujących (rozdział 9.); ■ nawiązywanie połączeń z bazami danych i innymi usługami zewnętrznymi (rozdział 10.);

Konwencje typograficzne W niniejszej książce zastosowano następujące konwencje typograficzne:

■ technika A JA X (rozdział 11.); ■ technologie oferowane w trybie open-source, ze szczególnym uwzględnieniem frameworków Facelets, Seam i Shale (rozdział 12.).

| S j Ta ikona oznacza ogólną uwagę.

JavaServer Faces

Ta ikona oznacza wskazówkę lub sugestię.

0

Ta ikona oznacza ostrzeżenie.

1 OBB Ta ikona oznacza element API.

Wprowadzenie Dlaczego wybieramy technologię JavaServer Faces? Na podstawie samych ogłoszeń o pracę i witryn internetowych pośredników zatrudnienia można by uznać, że istnieją dwie popularne techniki wytwarzania aplikacji internetowych: ■ styl „wytwarzania błyskawicznego” z wykorzystaniem takich wizualnych środowisk wytwarzania jak Microsoft A SP.N ET ; ■ styl „kodowania hard-core’owego” , w którym tworzy się mnóstwo kodu odpowiedzialnego za obsługę wydajnych mechanizmów wewnętrznych, np. w Javie E E (od Java Enterprise Edition). Zespoły odpowiedzialne za wytwarzanie oprogramowania stają przed trudnym wyborem. Java E E jest platformą dość atrakcyjną. Oferuje skalowalność, zapewnia możliwość przeno­ szenia oprogramowania pomiędzy platformami i jest obsługiwana przez wielu producentów. Z drugiej strony, technologia A SP .N ET ułatwia tworzenie atrakcyjnych interfejsów użyt­ kownika bez konieczność żmudnego programowania niezbędnych mechanizmów. Programiści chcieliby oczywiście dysponować środowiskiem łączącym cechy obu tych modeli: wydajnego oprogramowania wewnętrznego i efektownych interfejsów użytkownika. Twórcy technologii JavaServer Faces (JS F ) obiecują, że za jej pomocą będzie można przenieść techniki błyska­ wicznego wytwarzania interfejsu użytkownika na poziom serwera Javy. Czytelnicy, którzy mają doświadczenie w wytwarzaniu aplikacji klienckich Javy, mogą postrzegać technologię JS F jak swoisty „Sw ing dla aplikacji serwera” . Czytelnicy, którzy pracowali w technologii JS P (JavaServer Pages), zapewne zauważą, że JavaServer Faces oferuje znaczną część gotowych mechanizmów, które do tej pory musieli implementować ręcznie, w tym rozwiązania odpowiedzialne za nawigację pomiędzy stronami internetowymi i weryfikację danych. W porównaniu z wysokopoziomowym frameworkiem JS F serwlety

16

JavaServer Faces i JS P przypominają raczej język asemblera. Czytelników, którzy mieli do czynienia z takimi frameworkami serwerowymi jak Struts, zapewne ucieszy fakt że technologia JavaServer Faces bazuje na podobnej architekturze, ale oferuje wiele dodatkowych usług. I Lektura tej książki nie wym aga od Czytelnika żadnej wiedzy o interfejsie Swing, I technologii JSP ani fram eworku Struts. Zakładam y wyłącznie podstawową zna­ jom ość Javy i HTML-a. Technologia JS F składa się z trzech części:

Rozdziali. ■ Wprowadzenie

Przyjmujemy, że Czytelnik dysponuje już zainstalowanym pakietem JD K i że ma pewne doświadczenie w korzystaniu z jego narzędzi. W ięcej informacji na temat tego pakietu można znaleźć w książce Core Java ™ 2, vol. 2 — Advanced Features (7th ed.)1 autorstwa Caya Horstmanna i Gary'ego Cornelia (Sun Microsystems Press/Prentice Hall). Ponieważ Standard JS F 1.2 jest częścią specyfikacji Javy E E 5, najprostszym sposobem wypróbowania możliwości tej technologii jest użycie jednego z serwerów aplikacji zgodnych ze wspomnianą specyfikacją. W tym podrozdziale wykorzystamy serwer aplikacji GlassFish

(http://glassfish.dev.java. net).

■ zbioru prefabrykowanych komponentów interfejsu użytkownika:

E U W czasie wydawania tej książki istniały dwie główne wersje technologii JSF. W ersję platformy Java EE 5 w roku 2 0 0 6 . W ersja oryginalna (JSF 1 .0 ) została wydana w roku 2 0 0 4 (niedługo potem wydano w ersję JSF 1 .1 z kilkoma poprawkami eliminującymi wykryte błędy). W tej książce będziemy się kon­ centrowali na wersjach 1.1 i 1 .2 , jedn ak bardziej będzie nas interesow ała wersja 1. 2 .

■Kai najnowszą (JSF 1 .2 ) wydano w ram ach

■ modelu programowania sterowanego zdarzeniami: ■ modelu komponentów umożliwiającego niezależnym programistom konstruowanie komponentów dodatkowych. Niektóre komponenty JSF , jak pola tekstowe czy przyciski, są bardzo proste: inne, np. tabele danych i drzewa, sprawiają wrażenie bardziej wyszukanych.

E S || Czytelnicy, którzy nie chcą instalować kompletnego serwera aplikacji, mogą też użyć ■¡¡■I serw era Tom cat (http://tomcatapache.org) wraz z bibliotekam i technologii JSF dostępnymi na witrynie internetowej firmy Sun Microsystems {http://lavaserverfaces.dev. ^ java.net).

JavaServer Faces oferuje kompletny kod odpowiedzialny za obsługę zdarzeń i organizację komponentów. Programiści aplikacji mogą pozostawać w błogiej ignorancji w kwestii odpo­ wiednich rozwiązań szczegółowych i koncentrować się na logice aplikacji. Być może najważniejszą cechą technologii JS F jest to, że jest częścią standardu Java EE. Technologia JavaServer Faces jest obsługiwana przez wszystkie serwery aplikacji platformy Java EE i może być łatwo dodawana do autonomicznych kontenerów W W W , w tym do po­ pularnego Tomcata. Szczegółowych informacji na temat dostępnych usług należy szukać w podrozdziale „Usługi frameworka JS F ". Technologia JS F jest obsługiwana przez wiele zintegrowanych środo­ wisk programowania (ang. Integrated Development Environments — IDE), które z reguły oferują rozmaite funkcje, w tym uzupełnianie kodu i narzędzia do wizualnego projektowania stron. Więcej informacji na ten temat można znaleźć w podrozdziale „Środowiska wytwa­ rzania dla JS F ". W dalszej części tego rozdziału skoncentrujemy się na sposobach samodziel­ nego konstruowania aplikacji JS F , aby lepiej zrozumieć działanie odpowiednich mechani­ zmów środowisk ID E i nauczyć się skutecznego eliminowania ewentualnych problemów.

Należy teraz zainstalować serwer GlassFish zgodnie z zaleceniami sformułowanymi na jego witrynie internetowej. Kolejnym krokiem powinno być uruchomienie już zainstalowanego serwera aplikacji. W systemach operacyjnych Unix i Linux powinniśmy użyć polecenia: g/dSSf łsft/tnn/asadrmn Start doma in

glassfish jest katalogiem, w którym zainstalowano serwer aplikacji GlassFish. Efekt wyko­ nania tego polecenia przedstawiono na rysunku 1.1.

Rysunek 1.1. Uruchamianie serwera GlassFish

Fite

Edit

yiew

Terminal

Tafes

Help

~$ /usr/local/glassfish/bin/asadmin start-domain Starting Domain domainl, please wait. Log redirected to /home/apps/glassfish-b28/domains/domainl/logs/server.log. Domain domainl is ready to receive client requests. Additional services are bein g started in background.

~$ |

Instalacja oprogramowania W systemie Windows należy wydać polecenie: Na początek będziemy potrzebowali następujących pakietów oprogramowania: ■ JD K (Java SE Development Kit) 5.0 lub nowszego ( http://java.sun.com/j2se ); ■ JS F 1.2; ■ przykładowego kodu źródłowego dla tej książki dostępnego pod adresem

17

g/dssf/sd\bin\asadwin start domam

Aby sprawdzić, czy serwer aplikacji GlassFish działa prawidłowo, należy wpisać w oknie przeglądarki adres http://localhost:8080. Przeglądarka powinna wyświetlić stronę powitalną (patrz rysunek 1.2).

ftp://ftp.helion.pl/przyklady/javfac.zip. Polskie wydanie: Java 2. Techniki zaawansowane. Wydanie II, Helion, 2005 — przyp. tłum.

18

JavaServer Faces

Rozdziali. ■ Wprowadzenie

Su» >«»pa Syn w*» Api»«c*tion Server Platform Édition - Server Runntng - M ozifto Fírefox

Rysunek 1.2. Strona powitalna GlassFish

Plik

Edycja

Víidok

Bratona

4» ’ # ' ®

Zakładki

Naizędzia



Ł

"

Pomoc

áS [£¡ hkp^locaïhost:806Q/ __________

_ ........ F..1 .fe“ : Ê H

.............. f v

19

Plik opisujący ekran logowania jest w istocie dokumentem napisanym w języku HTM L z kil­ koma dodatkowymi znacznikami (patrz listing 1.1). Wygląd strony mógłby zostać łatwo po­ prawiony przez artystę grafika, który nie musiałby dysponować żadną wiedzą programistyczną.

?«■-> '.«jm

Listing 1.1. Zawartość pliku login/web/index.jsp

Sun Java System Application Server Platform Edition 9.0

Your server is up and running! io fpolacp tfos page overwrite

i

-.-u /_ J 7.rz’

{hp ń.pjjł,canon Seeret insiaitatfOP directory and Y ou are myitecf to

vv - ’

»»*

domd Lii*

1 donoor

in d e x lu id

where

2 3 4

u u t j j ¿_ j u ' is

jas>cua_ u jve * is the domain name dor example d o i k a m lł

R egistration- ,s option al but a s a r e g iste r e d us»et y o u

get

♦ Newt- auout oroduct updates ♦ Access to jalue added contents ♦ Notrftcattorj ot promottonal Diagrams ♦ Entry tn Jasa EE platform-relatedgifl give-aways Also check out the Giasshsh project. the open source community forthe Java EE application server More Information For more information about the Application Server, samples, documentation, and additional resources, see JJ_Guj->/docs/about. html. where s msraJJ_djj"> is the Application Serverinstallationdirectory. Company into ( Contest j Copyright 2006 Sun Microsystems Zakończono

__

Przerwanie pracy serwera GlassFish w systemach Unix i Linux wymaga użycia polecenia: g/assf7s/7/bin/asadmin stop-domain

W systemie Windows możemy zamknąć serwer za pomocą polecenia w postaci: g la s s fis h W \ n\asadmin stop-domain

Prosty przykład W niniejszym podrozdziale przeanalizujemy prosty przykład aplikacji JS F . Nasza aplikacja w pierwszej kolejności powinna wyświetlić ekran logowania przedstawiony na rysunku 1.3.

Rysunek 1.3. Ekran logowania

0%,Prostaaplikacjaia»sSewi Faees - Moalla Fwfo* g Plik Edycja Widok Historia Zakładki Narzędzia Pomoc | J j u Mtp:/^ocalhosc8080/logm»nct€x.fK«,

..•i «

u§h

Lagi lb u r 1="nt t p //javd Sun com/jsL/core" prefix- f" %>

5 6 Pros t a aplikacja JavaServer Faces 7 8 9 10 Proszę wpisać nazwę i hasło użytkownika 11. 12. 13 Nazwa- 14. 15 16. 17 18 19 Hasło: 20. 21. 22 23. 24 25. 26

Na pewnym etapie takie rozwiązanie można było zaakceptow ać, jed n ak obecnie po­ dobne próby należałoby uznać za karygodne. Nie m a wątpliwości, źe dokum ent w tej form ie z pewnością nie je s t zgodny z typem HTML 4.01 Transitional, a jedynie ma naśladować ten typ. Wiele edytorów i narzędzi języka XML odm awia obsługi podobnych konstrukcji. Oznacza to, że powinniśmy albo całkowicie zrezygnować z deklaracji DOCTYPE, albo stosować się do zaleceń sformułowanych w poprzedniej uwadze.



Nawigacja W naszych stronach JSF stosujem y tradycyjny, uproszczony form at, aby prezen­ tow ane listingi były możliwie czytelne.

Czytelnicy, którzy w odpowiednim stopniu opanowali język XML, prawdopodobnie będą mieli pewne zastrzeżenia. Po pierwsze, warto rozważyć użycie takiego znacznika XML-a w deklaracjach bibliotek znaczników, który umożliwi rezygnację ze stosowanych dotych­ czas znaczników . Co więcej, powinniśmy użyć właściwej deklaracji DOCTYPE dla generowanego dokum entu HTML-a. Poniżej przedstawiono form at, który elim inuje oba te problemy: < /faces-config>

Jedynym elementem tego pliku, na który warto zwrócić uwagę, jest odwzorowywanie (mapowanie) serwletów. Wszystkie strony JS F są przetwarzane przez serwlet specjalny będący częścią kodu stosowanej implementacji technologii JSF. Aby zagwarantować właściwe aktywowanie tego serwletu w momencie żądania strony JS F , stosujemy specjalny format adresów U R L. W naszej konfiguracji wspomniane adresy będą się kończyły rozszerzeniem

.faees. Na przykład nie możemy wpisać w przeglądarce adresu http://localhost:8080/login/index.jsp. Musimy się posłużyć adresem U RL http://localhosi:8080/login/index.faces. Kontener serwletu wykorzystuje do aktywowania serwletu JS F regułę odwzorowywania, która odcina przyrostek faces i ładuje stronę index.jsp.

W technologii JSF 1.1 zam iast deklaracji schem atu stosowano deklarację DOCTYPE: < !OOCTYPE faces-config PUBLIC "-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.0//EN" "h ttp :/ / ja v a .sun.com/dtd/web-facesconfigJL_ 0. dtd"> Zalecam y korzystanie z edytora XML, który właściwie interpretuje deklarację schem a­ tów języka XML. Czytelnicy pracujący w środowisku Eclipse mogą skorzystać z wtyczki XMLBuddy (http://xmlbuddy.com).

Możemy też zdefiniować odwzorowywanie przedrostków (zam iast odwzorowywania I rozszerzenia .faces). Wystarczy użyć następującej dyrektywy w pliku web.xml: Faces Serviet /faces/*

M ożem y następ n ie użyć adresu URL http://iocalhost:8080/iogin/faces/index.jsp. W spom niany adres aktywuje serw let JSF, który następnie obcina przedrostek faces i ładuje plik /login/index.jsp.

30

Rozdziali. ■ Wprowadzenie

JavaServer Faces

I Gdybyśmy chcieli stosować dla plików stron JSF rozszerzenie .jsf, musielibyśmy I tak skonfigurować naszą aplikację internetow ą, aby wywoływała dla plików z tym rozszerzeniem serwlet JSP. W tym celu należy w pliku web.xml zdefiniować następujące odwzorowanie: j$p *.jsf

Musimy jeszcze zasygnalizować implementacji JSF konieczność odwzorowywania rozsze­ rzenia .faces stosowanego w adresach URL w rozszerzenie .jsf powiązanych plików: ': javax.faces.DEFAULT_SUFFIX .j sf

Łatwo zauważyć, że opisywane zabiegi konfiguracyjne są istotne wyłącznie z perspektywy programistów i nie mają żadnego wpływu na pracę użytkownika naszej aplikacji interne­ towej. Adresy URL nadal mogą obejmować rozszerzenie .faces lub przedrostek /faces.

Jeśli korzystamy ze starszego serw era aplikacji obsługującego specyfikację serw wersji 2 .3 , powinniśmy użyć w pliku web.xml deklaracji DTD (DOCTYPE) zam iast deklaracji schem atu. Odpowiednia deklaracja DTD powinna m ieć następującą postać:

■Kfeil wletów



Strona powitalna Kiedy użytkownik wpisuje w oknie przeglądarki adres U R L wskazujący na katalog (np. http:/ W localhost:8080/login), serwer aplikacji automatycznie ładuje stronę index.jsp (jeśli taka strona jest dostępna). Okazuje się jednak, że opisywany mechanizm nie zdaje egzaminu w przypadku stron JS F , ponieważ pomija niezbędny proces przetwarzania. Można ten problem obejść, tworząc plik index.html, który automatycznie będzie kierował użytkownika pod adres U R L wskazujący na odpowiednią stronę .faces. Przykładowy kod takiego pliku przedstawiono na listingu 1.6.

Listing 1.6. Zawartość pliku login/web/index.html 1. 2 3 4 Uruchamianie aplikacji internetowej 5. 6.

7 8

Proszq cze^ac na uruchomienie a p l i k a c j i

31

int ernetowej

9 I wreszcie warto zdefiniować w pliku web.xml stronę index.html jako dokument powitalny (patrz znacznik w e l c o m e file na listingu 1.5). BSJJ Plik index.html kieruje przeglądarkę pod adres URL wskazujący na stronę index. ■ ¡¡J faces. Nieznacznie bardziej efektywne byłoby użycie akcji przekierowania tech­ nologii JSP. Wystarczy utworzyć stronę (np. start.jsp) zaw ierającą następujący wiersz:

Następnie należy wskazać tę stronę w znaczniku welcome-f i Te w pliku konfiguracyjnym web.xml.

środowiska wytwarzania dla JSF Strony i pliki konfiguracyjne dla prostej aplikacji JavaServer Faces można opracować w zwy­ kłym edytorze tekstu. Z drugiej strony, w miarę wzrostu poziomu złożoności naszej aplikacji musimy dobierać coraz bardziej wyszukane narzędzia. W tym podrozdziale omówimy zintegrowane środowiska programowania (ID E ) i wizualne narzędzia projektowe oferujące obsługę technologii JSF , a także sposób automatyzacji procesu kompilacji z wykorzystaniem narzędzia Ant.

Zintegrowane środowiska programowania Zintegrowane środowiska programowania (wytwarzania), takie jak Eclipse czy NetBeans, nieprzypadkowo zyskały ogromne uznanie w świecie programistów. Obsługa automatycznego uzupełniania kodu, możliwość refaktoryzacji kodu, narzędzia diagnostyczne (tzw. debugery) itp. znacznie podnoszą produktywność programisty (szczególnie w przypadku wielkich projektów). W czasie, kiedy pisano tę książkę, istniała eksperymentalna implementacja wtyczki JS F dla środowiska Eclipse, która bez wątpienia wymagała wielu udoskonaleń. Lepszą obsługę technologii JavaServer Faces oferują liczne produkty komercyjne zbudowane na bazie Eclipse (w tym MyEclipse, Exadel Studio, B E A Workshop Studio i Rational Application Developer), jednak niektóre spośród wymienionych środowisk są dość drogie. Dla wszystkich tych pro­ duktów istnieją wersje próbne, które można pobrać za darmo z intemetu. Z drugiej strony, programiści mają do dyspozycji środowisko NetBeans, które nie dość, że jest darmowe, to oferuje doskonałą obsługę wbudowaną technologii JSF . Czytelnicy, którzy nie są usatysfakcjonowani obsługą tej technologii w ramach swoich ulubionych środowisk ID E, koniecznie powinni dać szansę środowisku NetBeans i sprawdzić jego możliwości.

32

Rozdziali ■ Wprowadzenie

JavaServer Faces Środowisko NetBeans oferuje mechanizm automatycznego uzupełniania kodu stron JS F i plików konfiguracyjnych. Co więcej, środowisko NetBeans bardzo ułatwia uruchamianie i diagnozowanie aplikacji JS F — realizacja tych zadań wymaga od programisty klikania odpowiednich przycisków na pasku narzędzi. Na rysunku 1.7 przedstawiono ekran debugera środowiska NetBeans w momencie wstrzymania wykonywania wewnątrz klasy UserBean.

lego środowiska. Użytkownik (projektant) może przeciągać komponenty z tej palety nad cen­ tralny obszar okna i dostosowywać ich właściwości za pośrednictwem arkusza właściwości w prawym, górnym rogu okna. W odpowiedzi na te działania środowisko Sun Java Studio Creator automatycznie tworzy odpowiednie znaczniki JavaServer Faces (patrz rysunek 1.9).

Rysunek 1.8.

. jĘjj^

Na rysunku 1.8 przedstawiono okno narzędzia Sun Java Studio Creator (patrz http://www.sun.com/ K->software/products/jscreator). Paleta komponentów znajduje się w lewej, górnej części okna

=7Sl SZT

a apiikaąn JayaSwver Fwes - ffe ń fe firefc*

m

http,-//'[ocalhosfc8080/iogrn/tndex.faces



Ce^rf-eial S"’roift'-eio?

bu i Iddi r}/$|appnamef war"/>



Kiedy użytkownik klika przycisk Zaloguj i formularz jest wysyłany na serwer, implementacja technologii JS F wywołuje metodę check komponentu user. Wspomniana metoda może podjąć dowolne działania mające na celu aktualizację modelu, po czym zwrócić identyfikator nawigacji właściwy dla kolejnej strony. Opisywany mechanizm zostanie omówiony bardziej szczegółowo w podrozdziale „Nawigacja dynamiczna” w rozdziale 3. Oznacza to, że technologia JavaServer Faces implementuje klasyczną architekturę model-widok-kontroler (ang. Model-View-Controller — MVC).

Konwersja danych — użytkownicy wpisują w formularzach W W W dane tekstowe. Z drugiej strony, obiekty biznesowe oczekują wartości liczbowych, dat i innych typów danych. W rozdziale 6. zostaną omówione elementy frameworku JS F , które bardzo ułatwiają definiowanie i modyfikowanie niezbędnych reguł konwersji.

Obsługa narzędzi — framework JavaServer Faces zoptymalizowano pod kątem korzystania ze zautomatyzowanych narzędzi. Ponieważ odpowiednie narzędzia z czasem zyskują na niezawodności, należy oczekiwać, że w niedalekiej przyszłości właśnie technologia JS F będzie podstawowym frameworkiem dla interfejsów W W W budowanych w Javie.

Mechanizmy wewnętrzne Skoro wiemy już, „czego” i „dlaczego” możemy oczekiwać od technologii JavaServer Faces, możemy przystąpić do analizy tego, jak framework JS F realizuje swoje zadania. Warto się przyjrzeć funkcjonowaniu mechanizmów wewnętrznych w trakcie wykonywania naszej aplikacji przykładowej. Analizę rozpoczniemy od punktu, w którym przeglądarka po raz pierwszy nawiązuje połączenie z adresem U R L http://localhost:8080/login/index.faces. Serwlet JS F inicjalizuje kod JS F i odczytuje stronę indexjsp. Wspomniana strona zawiera takie znaczniki jak f : form i h : inputText. Każdy znacznik ma przypisaną swoją klasę obsłu­ gującą. W czasie odczytywania strony są wykonywane odpowiednie metody obsługujące po­ szczególne znaczniki. Klasy odpowiedzialne za obsługę znaczników JS F współpracują ze sobą w procesie konstruowania drzewa komponentów (patrz rysunek 1.13).

40

Rozdział i. ■ Wprowadzenie

JavaServer Faces

Rysunek 1.13.

Z menu Widokprzeglądarki wybierz opcję Źródło, ab wygenerowanym w procesie wizualizacji. Typowe na rysunku 1 .1 5 . Wspomniana opcja bywa przydatna poc związanych z wykonywaniem aplikacji JSF.

Q

Drzewo komponentów skonstruowane dla przykładowej aplikacji

41

oznać się z kodem HTML-a wynikowe przedstawiono ozowania problemów

■ U l____________ Rysunek 1.15.

Drzewo komponentów jest strukturą danych obejmującą obiekty Javy dla wszystkich elemen­ tów interfejsu użytkownika na stronie JavaServer Faces. Na przykład widoczne na rysunku 1.13 dwa obiekty klasy Ullnput odpowiadają polom h : inpu.tTe.x.t i h . inputSecret w pliku JSF.

^

Efekt wyświetlenia kodu źródłowego strony logowania

Żródto. http^/ioca!tost;8080/1og»i!Vlrw)



Dekodowanie żądań Po wyświetleniu strony w oknie przeglądarki użytkownik wypełnia pola formularza i klika przycisk Zaloguj. Wówczas przeglądarka odsyła na serwer W W W wpisane dane sformatowane w formie żądania POST. Jest to format specjalny zdefiniowany w ramach protokołu HTTP. Żądanie POST zawiera zarówno adres U R L formularza (w tym przypadku /login/index.faces), jak i dane formularza.

aP« Sï3 ssf I Í W m A

doprowa; bezpośrednio

Rysunek 1.14. Kodowanie i dekodowanie stron JSF

- Serwer

%,-4 4 3 i L

Dane formularza mają postać podobnych par identyfikator-wartość:

Drzew o kom p o n en tó w

Przeglądarka '..

7d/=ja&/d^sekret&/d3=login

HTML j

L ............................... .

W ramach standardowego procesu przetwarzania serwletu dane formularza są umieszczane w tablicy mieszającej, do której mają dostęp wszystkie komponenty.

HTTP POST Kodo wanie/dekodowĄiie f Framework JSF

W kolejnym kroku framework JS F umożliwia wszystkim komponentom analizę zawartości tej tablicy mieszającej (w procesie nazywanym dekodowaniem). Każdy komponent sam decyduje o sposobie interpretacji danych formularza.

42

RozdziaM. ■ Wprowadzenie

JavaServer Faces Dla formularza logowania istnieją trzy obiekty komponentów graficznych: dwa obiekty klasy Ullnput właściwe dla pól tekstowych na formularzu oraz obiekt klasy UlComniand właściwy dla przycisku akceptacji. ■ Komponenty graficzne typu U11nput aktualizują właściwości komponentów wskazane w atrybutach val ue (w yw ołują metody ustawiające, przekazując na ich wejściu wartości wpisane przez użytkownika). ■ Komponent graficzny UlComniand sprawdza, czy odpowiedni przycisk został kliknięty. Jeśli tak, generuje zdarzenie akcji, aby wykonać akcję logi n wskazaną w formie atrybutu action. Wspomniane zdarzenie sygnalizuje mechanizmowi odpowiedzialnemu za obsługę nawigacji konieczność odnalezienia kolejnej strony (w tym przypadku welcome.jsp).

43

W fazie przywracania widoku (ang. restore view) odczytujemy dla żądanej strony (jeśli była już wcześniej wyświetlana) lub konstruujemy (jeśli dana strona jest wyświetlana po raz pierw­ szy) drzewo komponentów. Jeśli dana strona była już wyświetlana, odtwarzamy (przywracamy) poprzedni stan wszystkich komponentów. Oznacza to, że framework JS F automatycznie zachowuje informacje z formularza. Jeśli na przykład użytkownik wpisze nieprawidłowe dane, które zostaną odrzucone w procesie dekodowania, ponownie wyświetlony formularz będzie zawierał wszystkie wprowadzone wcześniej dane, które użytkownik będzie mógł skorygować. Jeśli żądanie nie obejmuje żadnych danych formularza, implementacja JavaServer Faces pomija fazę wizualizacji odpowiedzi (ang. render response ). Taka sytuacja ma miejsce wtedy, gdy strona jest wyświetlana po raz pierwszy.

Cały ten cykl jest powtarzany.

W przeciwnym razie przechodzimy do fazy kolejnej: stosowania wartości żądania (ang. apply request values). W lej fazie implementacja JS F iteracyjnie przeszukuje obiekty komponentów

Właśnie zapoznaliśmy się z dwoma najważniejszymi krokami przetwarzania w ramach firameworku JS F . czyli kodowaniem i dekodowaniem. Okazuje się jednak, że sekwencja przetwa­ rzania (określana też mianem cyklu życia; ang. life cyc/e) jest nieco bardziej zawiła. Jeśli wszystkie mechanizmy działają zgodnie z naszymi oczekiwaniami, nie musimy się zajmować szczegółowymi aspektami cyklu życia. Jeśli jednak w trakcie wykonywania naszej aplikacji będzie miał miejsce jakiś błąd. będziemy się musieli zainteresować faktycznym funkcjonowa­ niem frameworku. W kolejnym punkcie dokonamy szczegółowej analizy cyklu życia aplikacji JavaServer Faces.

składowane w drzewie komponentów. Każdy z tych obiektów sprawdza, czy wartości dołą­ czone do żądania nie należą do niego i (w razie konieczności) zapisuje je w swoich właści­ wościach.

Cykl życia aplikacji JSF Specyfikacja JS F definiuje sześć odrębnych faz cyklu życia aplikacji (patrz rysunek 1.16). Standardowy przepływ sterowania oznaczono liniami ciągłymi; przepływ alternatywny jest reprezentowany przez linie przerywane.

Zakończono o dp o w ie dź ----

Zakończono o dp o w ie dź

<

-o

W fazie stosow ania w artości żądania im plem entacja JSF nie tylko w yodrębnia inform acje zaw arte w żądaniu, ale też dodaje do kolejki zdarzenia właściwe dla kliknięć przycisków lub łączy na danej stronie. Tem atyką związaną z obsługą zdarzeń zajm iem y się w rozdziale 7 . Jak widać na rysunku 1 .1 6 , zdarzenia mogą być przetwa­ rzane po każdej fazie. W skrajnych przypadkach m etoda obsługująca zdarzenie m oże wymusić natychmiastowe przejście do fazy wizualizacji odpowiedzi lub nawet całkowicie przerwać proces przetwarzania żądania. W fazie weryfikacji danych (ang. process validations) przekazane wartości łańcuchowe są w pierwszej kolejności konwertowane na odpowiednie „wartości lokalne", które mogą mieć postać obiektów dowolnego typu. Projektując stronę JS F , możemy zdefiniować mechanizmy weryfikacji odpowiedzialne za sprawdzanie prawidłowości tych wartości lokalnych. Jeśli weryfikacja przebiegnie pomyślnie, cykl życia aplikacji JavaServer Faces może być normalnie kontynuowany. Jeśli jednak w czasie tego procesu zostaną wykryte jakieś błędy, implementa­ cja JS F od razu wywoła fazę wizualizacji odpowiedzi, wymuszając ponowne wyświetlenie bieżącej strony, aby użytkownik raz jeszcze mógł wpisać prawidłowe dane wejściowe. Dla wielu programistów właśnie to działanie jes t najmniej spodziewanym aspektem cyklu życia aplikacji JSF. Jeśli działanie m echanizm u konwertującego lub m echa­ nizmu weryfikującego zakończy się niepowodzeniem , bieżąca strona zostanie ponow­ nie wyświetlona. Oznacza to, że powinniśmy dodać znaczniki inform ujące użytkownika o wykrytych błędach, aby w iedział, dlaczego aplikacja wróciła do wysłanego wcześniej form ularza. W ięcej informacji na ten te m a t można znaleźć w rozdziale 6 .

Błędy ko n w ersji/w izua liza cja o d p o w ie d zi

;

Błędy w e ryfika cji lu b konw ersji/w izua liza cja od p o w ie d zi

Rysunek 1.16. Cykl życia aplikacji JSF

Po pomyślnej realizacji zadań przez mechanizmy konwersji i weryfikacji implementacja JS F przyjmuje, że można bezpiecznie zaktualizować dane modelu. W fazie aktualizacji wartości modelu (ang. update model values) wartości lokalne są wykorzystywane do zmiany wartości w ramach komponentów właściwych dla poszczególnych elementów formularza.

44

JavaServer Faces W fazie wywołania aplikacji (ang. invoke application) implementacja JS F wykonuje me­ todę action przycisku lub łącza, którego kliknięcie doprowadziło do wysłania danego for­ mularza. Wspomniana metoda może wykonywać dowolne operacje na otrzymanych danych i zwraca łańcuch wynikowy, który jest następnie przekazywany do mechanizmu obsługują­ cego nawigację. Na tej podstawie mechanizm nawigacji identyfikuje stronę, która powinna zostać wyświetlona jako następna.

2

I wreszcie w fazie wizualizacji odpowiedzi, odpowiedź jest kodowana i wysyłana do przeglą­ darki. Kiedy użytkownik wysyła formularz, klika łącze lub w inny sposób generuje nowe żądanie, cały ten cykl zaczyna się od nowa. Przedstawiliśmy podstawowe mechanizmy umożliwiające funkcjonowanie aplikacji Java­ Server Faces. W kolejnych rozdziałach dokonamy szczegółowej analizy poszczególnych etapów cyklu życia tych aplikacji.

Komponenty zarządzane Centralnym elementem projektów aplikacji internetowych jest rozdział warstw prezentacji i logiki biznesowej. W technologii JavaServer Faces za taki rozdział odpowiadają komponenty (ang. beans ). Strony JS F odwołują się do właściwości komponentów. Kod implementacji tych komponentów definiuje właściwą logikę programu. Ponieważ właśnie komponenty są kluczem do programowania aplikacji JSF , w niniejszym rozdziale skoncentrujemy się na ich szczegółowym omówieniu. W pierwszej części tego rozdziału przeanalizujemy podstawowe elementy komponentów, 0 których powinien wiedzieć każdy programista aplikacji JS F . W dalszej części omówimy program przykładowy, który dobrze ilustruje łączne funkcjonowanie tych elementów. W pozo­ stałych podrozdziałach skupimy się na technicznych aspektach konfiguracji komponentów 1wyrażeń reprezentujących wartości. Czytelnicy, którzy mają tę książkę w rękach po raz pierwszy, mogą te podrozdziały pominąć i wrócić do nich w przyszłości.

Definicja komponentu Zgodnie ze specyfikacją JavaBeans (dostępną na stronie internetowej http://java.snn.com 'products/javabeans/) komponent Javy ma postać „wielokrotnego komponentu oprogra­ mowania, który można modyfikować za pomocą narzędzia do projektowania” . Przytoczona definicja jest bardzo szeroka, co jest o tyle zrozumiałe, że — jak się niedługo okaże — kom­ ponenty można wykorzystywać do rozmaitych celów. Na pierwszy rzut oka komponent wydaje się bardzo podobny do zwykłego obiektu Javy. Okazuje się jednak, że komponent Javy odpowiada za realizację innych zadań. Obiekty są tworzone i wykorzystywane w ramach programów Javy (odpowiednio przez wywołania konstruktorów i wywołania metod). Nieco inaczej jest w przypadku komponentów, które mogą być konfigurowane i wykorzystywane bez konieczności programowania.

46

Rozdział 2. ■ Komponenty zarządzane

JavaServer Faces

K M Część Czytelników zapewne zastanawia się, skąd się wziął angielski term in bean. ■ ¡li W Stanach Zjednoczonych słowo Java je s t synonim em kawy, a sm ak kawy kryje się w jej ziarnach (ang. beans). Opisywana analogia części programistów wydaje się wyjątkowo zmyślna, innych po prostu denerwuje — nam nie pozostaje nic innego, ja k pogodzić się z przyjętą term inologią. „Klasycznym " zastosowaniem komponentów JavaBeans jest konstruowanie interfejsów użytkownika. Okno palety oferowane przez narzędzia do projektowania takich interfejsów obejmuje takie komponenty jak pola tekstowe, suwaki, pola wyboru itp. Zamiast samodzielnie pisać kod Swinga, korzystamy z narzędzia do projektowania interfejsu, który umożliwia nam przeciąganie komponentów dostępnych na palecie i upuszczanie ich na tworzonym formu­ larzu. Możemy następnie dostosowywać właściwości tych komponentów przez ustawianie odpowiednich wartości w odpowiednim oknie dialogowym (patrz rysunek 2.1).

Dostosowywanie komponentu do potrzeb aplikacji za pośrednictwem narzędzia do projektowania graficznego interfejsu użytkownika

Ftie Edit View Navigate

A „ / s»

Retactor Borta Run CVS foob Window Help

0y ë i S ? A

„.ectojj s'Veic«» » j Ö Newjf-i-ame ,a>n ’

** 1



Jak widać, programista aplikacji JavaServer Faces nie musi pisać żadnego kodu konstruującego i operującego na komponencie user. Za konstruowanie komponentów zgodnie z elementami zdefiniowanymi w znaczniku managed bean (w ramach pliku konfiguracyjnego) odpowiada implementacja JS F . W aplikacjach JS F komponenty są zwykle wykorzystywane do następujących celów: ■ w roli komponentów interfejsu użytkownika (w formie tradycyjnych komponentów interfejsu); w roli elementów łączących w jedną całość zachowanie formularza internetowego (są to tzw. komponenty wspomagające; ang. baeking beans):

■ w roli obiektów biznesowych, których właściwości są wyświetlane na stronach W W W ;

# S3

! I Palette

»[

Do tak zdefiniowanego komponentu JavaBean można uzyskiwać dostęp z poziomu kompo­ nentów JSF. Na przykład poniższe pole tekstowe odczytuje i aktualizuje właściwość password komponentu user:

a

MËÊÈÈSÊÊÈ m

'Ęi NetBeans IDE 5.5,1 -)avaApplication!

Rysunek 2.1.

47

Swing

& A P ;

-» 'Labę1

¡1

GS> Button

i l

■ w roli takich usług jak zewnętrzne źródła danych wymagające skonfigurowania w momencie wdrażania aplikacji.

Ci, PoggieBuccon JCne,.KBo> r 1

.■'lexcfifiiai. i l«Atheia2 ■ j Zaiogu)

!'■'

JPadioBuron

Z uwagi na wszechobecność i uniwersalność komponentów skoncentrujemy się wyłącznie na tych elementach specyfikacji JavaBeans, które znajdują zastosowanie z perspektywy programistów aplikacji JavaServer Faces.

BUtCOPbtOuO

j a t iComDoBo^

-

{ r_i HextP'eid ■jButtonl IJButton]

ss :

Properties Code

•-CProperties

A

■ nl IP

naukątouna

*



QOs j O

action

componentPop1ipMeni.. [

D »U

j

t.jnt

i on,.,ma 11 p|am



foreground

■ 10,0 LI] null

0

ï

0

}

Zalodui

n

mnemonic (int) the keyboard character mnemonic

Właściwości komponentu

i I

T 1 m

Klasy komponentów muszą być tworzone w zgodzie z pewnymi konwencjami programowania, aby odpowiednie narzędzia mogły swobodnie operować na ich składowych. W niniejszym punkcie zajmiemy się właśnie tymi konwencjami.

1

Do najważniejszych elementów klasy komponentu należą udostępniane właściwości. Właściwością jest każdy atrybut komponentu, który obejmuje: W technologii JavaServer Faces zadania komponentów nie kończą się na obsłudze elementów interfejsu użytkownika. Stosujemy je za każdym razem, gdy musimy wiązać klasy Javy ze stronami W W W lub plikami konfiguracyjnymi.

■ nazwę, ■ typ, ■ metody zwracające i (lub) ustawiające wartość tej właściwości.

Wróćmy na chwilę do aplikacji login przedstawionej w podrozdziale „Prosty przykład” w roz­ dziale 1. Egzemplarz komponentu UserBean skonfigurowano w pliku faces-config.xml\ user com.corejsf.UserBean session

Znaczenie tych zapisów można by wyrazić następującymi słowami: skonstruuj obiekt klasy com.corejsf .UserBean, nadaj mu nazwę user i utrzymuj go przy życiu w czasie trwania sesji (czyli dla wszystkich żądań wygenerowanych przez tego samego klienta).

Na przykład klasa UserBean zdefiniowana i wykorzystywana w poprzednim przykładzie zawiera właściwość typu S tri ng nazwaną password. Za zapewnianie dostępu do wartości tej właściwości odpowiadają metody getPassword i setPassword. Niektóre języki programowania, w szczególności Visual Basic i C#, oferują bezpośrednią obsługę właściwości. Z drugiej strony, w języku Java komponent ma postać zwykłej klasy zaimplementowanej zgodnie z pewnymi konwencjami kodowania.

48

JavaServer Faces Specyfikacja JavaBeans definiuje pojedyncze wymaganie stawiane klasom komponentów — każda taka klasa musi definiować publiczny konstruktor domyślny, czyli konstruktor bezparametrowy. Z drugiej strony, aby definiowanie właściwości było możliwe, twórca kompo­ nentu musi albo stosować wzorzec nazewniczy dla metod zwracających i ustawiających, albo definiować deskryptory właściwości. Drugi model jest dość kłopotliwy i nie zyskał popu­ larności, więc nie będziemy się nim zajmować. Szczegółowe omówienie tego zagadnienia można znaleźć w rozdziale 8. książki Core Java™ 2. vol. 2 — Advanced Features (7th ed.)1 autorstwa Caya Horstmanna i Gary'ego Cornelia. Definiowanie właściwości w zgodzie ze wzorcami nazewniczymi jest bardzo proste. Prze­ analizujmy teraz następującą parę metod: pub 1tc J getFoo() pub 11 c void setFoo { I newvalue)

Przedstawiona para definiuje właściwość dostępną do odczytu i zapisu, typu T, nazwaną foo. Gdybyśmy użyli tylko pierwszej z powyższych metod, nasza właściwość byłaby dostępna tylko do odczytu. Pozostawienie samej drugiej metody oznaczałoby, że jest to właściwość dostępna tylko do zapisu. Nazwy i sygnatury metod muszą gwarantować pełną zgodność z tym wzorcem. Nazwa metody musi się rozpoczynać od przedrostka get lub set. Metoda get nie może otrzymywać żadnych parametrów. Metoda set musi otrzymywać jeden parametr, ale nie może zwracać żadnych wartości. Klasa komponentu może zawierać inne metody (odbiegające od opisanej konwencji), które jednak nie definiują dodatkowych właściwości.

Rozdział 2. ■ Komponenty zarządzane

49

P M | Specyfikacja JavaBeans przewiduje też możliwość stosowania właściwości indekktóry przed­ stawiono poniżej:

■¡¡3 sowanych definiowanych w formie zbiorów metod podobnych do tego, public public public public

713 getFooO 7 getFoodnt index) void setFoo(7I] newArray) void setFoo(int index. T newValue)

Okazuje się jed n ak, że technologia JSF nie zapewnia obsługi operacji dostępu do in­ deksowanych wartości. Klasa komponentu może też zawierać metody inne niż te odpowiedzialne za zwracanie i ustawianie wartości właściwości. Takie metody oczywiście nie definiują kolejnych wła­ ściwości komponentu.

Wyrażenia reprezentujące wartości Wiele komponentów interfejsu użytkownika aplikacji JS F definiuje atrybut val ue, który umoż­ liwia określanie wartości bądź odwołania do wartości uzyskiwanej z właściwości komponentu JavaBean. Wartość stosowaną bezpośrednio można zdefiniować na przykład w następujący sposób:

Można też użyć wyrażenia reprezentującego wartość:

Warto pamiętać, że nazwa samej właściwości jest identyczna jak nazwa metod uzyskujących dostęp do jej wartości po usunięciu przedrostka get lub set oraz zamianie pierwszej litery na małą. Tworząc na przykład metodę get Foo, definiujemy właściwość nazwaną foo (z pierw­ szą literą F zamienioną na f). Jeśli jednak za przedrostkiem znajdują się co najmniej dwie wielkie litery, pierwsza litera nazwy właściwości pozostaje niezmieniona. Na przykład metoda nazwana get URL definiuje właściwość URL, nie uRL.

W większości przypadków wyrażenia podobne do #{user. name} odwołują się do właściwości. Warto pamiętać, że wyrażenie w tej formie może być stosowane nie tylko do odczytywania wartości, ale też do ich zapisywania, jeśli zostanie użyte dla komponentu wejściowego:

W przypadku właściwości typu boolean mamy do wyboru dwa różne prefiksy dla metod zwracających ich wartości. Obie wersje:

W momencie renderowania danego komponentu zostanie wywołana metoda zwracająca właściwość będącą przedmiotem odwołania. Metoda ustawiająca zostanie wywołana w czasie przetwarzania odpowiedzi użytkownika.



public boolean lsCcmnecteclO

i public boolean get Connected O

są prawidłowymi nazwami metod zwracających wartość właściwości connected. Specyfikacja JavaBeans milczy na temat zachowań metod zwracających i ustawiających. W wielu przypadkach działanie tych metod sprowadza się do prostego operowania na polach egzemplarza. Z drugiej strony, te same metody mogą równie dobrze obejmować bardziej wyszukane operacje, jak dostęp do bazy danych, konwersję danych, weryfikację danych itd.

1 Polskie wydanie: Java 2. Techniki zaawansowane. Wydanie IL Helion, 2005 — przyp. tłum.

Wyrażenia reprezentujące wartości stosowane w aplikacjach JSF m ają ścisły związek z językiem wyrażeń znanym z technologii JSP. Dla tego rodzaju wyrażeń stosuje się je d n a k separator (zam iast sep arato ra # { . . . } ) , W stan d ardach JSF 1 .2 i JSP 1 .2 zunifikowano składnię obu języków wyrażeń. (Kom pletną analizę tej składni można znaleźć w podrozdziale „Składnia wyrażeń reprezentujących w artości”).

liii

Separator $ { . . . } oznacza, że dane wyrażenie ma zostać przetworzone natychmiast, czyli w czasie przetwarzania odpowiedniej strony przez serwer aplikacji. Separator # { . . . } sto­ suje się dla wyrażeń, które m ają być przetwarzane możliwie późno. W takim przypadku serwer aplikacji zachowuje wyrażenie w niezmienionej formie i przystępuje do jego prze­ twarzania dopiero wtedy, gdy odpowiednia wartość je s t naprawdę potrzebna. W arto pam iętać, że wyrażenia odroczone stosuje się dla wszystkich właściwości kom ­ ponentów JSF, natom iast wyrażenia przetwarzane natychm iast są wykorzystywane dla tradycyjnych konstrukcji JSP lub JSTL (od ang. Java$erver Pages Standard Template Library), które rzadko są niezbędne w procesie tworzenia stron JSF.

50

JavaServer Faces

Rozdział 2. ■ Komponenty zarządzane

Szczegółowe omówienie składni wyrażeń reprezentujących wartości można znaleźć w pod­ rozdziale „Składnia wyrażeń reprezentujących wartości".

To wszystko, czego nam trzeba! Kjedy zdecydujemy się na lokalizację naszej aplikacji z myślą o innych ustawieniach regionalnych, będziemy musieli tylko opracować odpowiednie pliki z komunikatami.

Pakiety komunikatów

K M Element resource-bundle zapewnia większą efektywność niż elem ent f:loadBundie, ■ H ponieważ zapew nia, że pakiet kom unikatów będzie odczytywany jednorazow o dla całej aplikacji. W arto jedn ak pam iętać, że elem ent resource-bundl e je s t obsługiwany, począwszy od wersji JSF 1 .2 . Oznacza to, że jeśli chcem y zapew nić zgodność naszej aplikacji ze specyfikacją JSF 1 . 1, m usimy się posługiwać elem entem f: loadBundie.

Implementując aplikację internetową, warto rozważyć zgromadzenie wszystkich łańcuchów komunikatów w jednym, centralnym miejscu. Taki proces ułatwi zachowanie spójności komunikatów i — co bardzo ważne — upraszcza lokalizowanie (internacjonalizację) apli­ kacji z myślą o -wnych ustawieniach regionalnych. W tym podrozdziale przeanalizujemy mechanizmy lecnnologii JS F umożliwiające organizację komunikatów. W podrozdziale „Przy­ kładowa aplikacja" przeanalizujemy przykład komponentów zarządzanych pracujących łącznie z pakietami komunikatów.

Lokalizując pakiety komunikatów, musimy nadawać odpowiednim plikom nazwy z przy­ rostkiem właściwym dla ustawień regionalnych, czyli dwuliterowego kodu języka (zgodnego ze standardem iSO-639) poprzedzonego znakiem podkreślenia. Na przykład łańcuchy w języku niemieckim należałoby zdefiniować w pliku com/corejsf/messciges de.properties.

Łańcuchy komunikatów należy zebrać w pliku, którego format odpowiada przyjętemu porząd­ kowi chronologicznemu: guessNextOdgadnij następny

51

Listę wszystkich dwu- i trzyliterowych kodów języków standardu IS O -6 3 9 m ożna znaleźć na stronie internetowej http://www.loc.gov/standards/iso639-2/ .

IlczDę w s e k w e n c ji !

answe^Twoja odpowiedz

Mechanizmy obsługi umiędzynarodawiania aplikacji Javy automatycznie ładują pakiety komunikatów właściwe dla bieżących ustawień regionalnych. Pakiet domyślny (bez przy­ rostka języka ISO-639) jest swoistym zabezpieczeniem na wypadek, gdyby zlokalizowany pakiet był niedostępny. Szczegółowe omówienie problemu umiędzynarodawiania aplikacji Javy można znaleźć w rozdziale 10. książki Core Java™ 2, vol. 2 — Advanced Features (7th ed.)2 autorstwa Caya Florstmanna i Gary‘ego Cornelia.

I Precyzyjne om ów ienie form atu tego pliku m ożna znaleźć w dokum entacji API dla I metody load klasy ja v a .u t il .Properties. Należy ten plik zapisać w katalogu, w którym składujemy klasy — np. jako /nsrc/java/com/ ^ c o r e jsk messages.properties. Można oczywiście wybrać dowolną ścieżkę do katalogu i nazwę piiku, jednak musimy użyć rozszerzenia .properties.

Przygotowując tłumaczenia aplikacji, należy mieć na uwadze jeden dość specyficzny

■ i i aspekt — pliki pakietów komunikatów nie są kodowane z wykorzystaniem schematu UTF-8 . Znaki Unicode spoza zbioru pierwszych 1 2 7 znaków są kodowane za pom ocą

Pakiety komunikatów możemy deklarować na dwa sposoby. Najprostszym rozwiązaniem jest umieszczenie następujących elementów w pliku konfiguracyjnym faees-eonftg.xmk.

sekw encji specjalnej \uxxxx. Tego rodzaju pliki m ożna tworzyć za pom ocą narzędzia native2ascii pakietu Java SDK.



;resOurce-Dundle> com co^ ej sf messagesmsgs

Komunikaty często obejmują elementy zmienne, które — co oczywiste — wymagają w y­ pełnienia przed wyświetleniem. Przypuśćmy na przykład, że chcemy wyświetlić na ekranie zdanie Masz n punktów , gdzie a? jest wanością uzyskiwaną z komponentu. W tym celu musimy utworzyć łańcuch źródłowy z symbolem zastępczym:

W obu przypadkach dostęp do komunikatów wchodzących w skład pakietu odbywa się za pośrednictwem zmiennej odwzorowania (mapy) nazwanej msgs. (Nazwa bazowa, czyli com.corejsf .messages, przypomina nazwę klasy i rzeczywiście okazuje się, że plik właści­ wości jest wczytywany przez mechanizm ładowania klas).

CurrentScore=MdSZ |0} plinkiow

Możemy teraz uzyskiwać dostęp do łańcuchów komunikatów w ramach wyrażeń reprezen­ tujących wartości: < h: o u tp u tT e x t

v a l u e = "# {m s g s . g u e s s N e x t } "/>

2

Polskie wydanie: Java 2. Techniki zaawansowane. Wydanie //, Helion, 2005 — przyp. tłum.

52

JavaServer Faces Symbole zastępcze (ang. placehorders) mają postać {0}, {1}, {2} itd. W kodzie naszej strony JS F powinniśmy użyć znacznika h : outputFormat i zdefiniować wartości dla tych symboli zastępczych w formie elementów potomnych f : param:

Rozdział 2. ■ Komponenty zarządzane 1- Możemy pozostawić wybór ustawień regionalnych przeglądarce. Ustawienia domyślne i wszystkie ustawienia obsługiwane należy zdefiniować w pliku konfiguracyjnym WEB-INF/faces-config.xmI (lub innym zasobie konfiguracyjnym aplikacji): pl de

Znacznik h: outputFormat wykorzystuje do formatowania łańcuchów komunikatów klasę MessageFormat biblioteki standardowej. Wspomniana klasa oferuje szereg mechanizmów opracowanych z myślą o formatowaniu łańcuchów zależnych od ustawień regionalnych. Za pomocą przyrostka number, currency dołączanego do symbolu zastępczego możemy formatować liczby jako kwoty w lokalnej walucie:

Kiedy przeglądarka nawiązuje połączenie z naszą aplikacją, zwykle dołącza do nagłówka protokołu HTTP wartość Accept -Language (patrz http://www.w3.org/ W htemational/questions/qa-aceept-lang-locales.htmI). Implementacja technologii JavaServer Faces odczytuje ten nagłówek i odnajduje najlepsze dopasowanie wśród obsługiwanych ustawień regionalnych. Możemy sprawdzić funkcjonowanie tego mechanizmu, ustawiając język preferowany w swojej przeglądarce (patrz rysunek 2.2).

currentTotal =Dysponujesz kwotc} {0 .number.currency}

W Stanach Zjednoczonych wartość 1023.95 zostanie sformatowana jako $1,023.95. Ta sama wartość w Niemczech zostałaby wyświetlona jako € 1.023,95 (w Polsce 1 023,95 zl) — z uwzględnieniem symbolu lokalnej waluty i konwencji separatora części dziesiętnej. Format choice umożliwia nam Formatowanie liczb na różne sposoby (w zależności od wartości poprzedzającej jednostkę), czyli: zero punktów, jeden punkt, dwa punkty, 3 punkty, 4 punkty, 5 punktów itd. Poniżej przedstawiono łańcuch formatu zapewniający oczekiwany efekt: currentScore=Mesz {0 .choice,0#zero punktów|1#jeden purikt|2#dwa punkty|3#trzy punkty |4#cztery punkty|5#{0} punktów}

Rysunek 2.2.

Języki

Wybór preferowanego języka

f -S

Niektóre srrony WV»Ajv dostępu

lęsyfcounyc» U^tai lute P^erowanych fezyi-onnr ctia Teqo rodzaju

lezyk'-n,p>efero«vane( koiej^oseD Uk. [pij

Ntemiecli/Nienjcv [de-del

1

j N'eP-'eCk- [de]

Łatwo zauważyć, że symbol zastępczy 0 występuje dwukrotnie: raz na etapie wyboru formatu i drugi raz w ostatniej, szóstej opcji, gdzie generuje wynik w postaci komunikatu 5 punktów.

Dodatkowych informacji na temat klasy MessageFormat należy szukać w dokumentacji A P I lub w rozdziale 10. książki Core Java™ 2, vol. 2 — Advanced Features (7th e d .f autorstwa Caya Horstmanna i Gary’ego Cornelia.

lif iJ S iJ

Angie' ki/Staru Zienr"-.^T0r,r len-,ul *r,gie|.k. len]

Mamy do czynienia z sześcioma możliwymi przypadkami (0, l, 2, 3, 4 oraz >5), z których każdy definiuje odrębny łańcuch komunikatu.

Przykłady użycia tej konstrukcji można znaleźć na listingach 2.5 i 2.6 w podrozdziale „Przy­ kładowa aplikacja” . O ile angielskie ustawienia regionalne (z komunikatem Your score is ...) w ogóle nie wymagają stosowania formatu choice w naszej przykładowej aplikacji, o tyle niemieckie wyrażenie Sie haben ... punkte (podobnie jak polskie Masz ... punktów) nie jest uniwersalne, ponieważ nie uwzględnia liczby pojedynczej einen punkt.

1

w wetu uuers^th

L ii /

| ]

Anuluj

I

i |

tiiun

|

Doclaj

~]

Poniec__j

2. Możemy dodać atrybut 1oca 1e do elementu f : vi ew — oto przykład:

Ustawienia regionalne można też określać dynamicznie:

Kod reprezentujący ustawienia regionalne ma teraz postać łańcucha zwracanego przez metodę getLocal e. Takie rozwiązanie jest wygodne w przypadku aplikacji, które umożliwiają użytkownikowi wybór preferowanych ustawień.

3. Ustawienia regionalne można też definiować programowo. W tym celu wystarczy

Konfigurowanie ustawień regionalnych aplikacji Po opracowaniu pakietów komunikatów musimy zdecydować, jak zdefiniować ustawienia regionalne naszej aplikacji. Mamy do dyspozycji trzy rozwiązania:

3 Polskie wydanie: Java 2. Techniki zaawansowane. Wydanie II, Helion, 2005 — przyp. tłum.

wywołać metodę setLocale obiektu klasy UIViewRoot: UIViewRoot viewRoot = FacesContext.getCurrentInstance() .getViewRoot(); viewRoot.setLocale(new LocaleCde"));

Przykład praktycznego użycia tego mechanizmu można znaleźć w punkcie „Stosowanie łączy poleceń” w rozdziale 4.

53

54

JavaServer Faces

Rozdział 2. ■ Komponenty zarządzane Listing 2.1. Kod zdefiniowany w pliku numberquiz/src/java/com/corejsf/ProblemBean.java

Przykładowa aplikacja Po omówieniu tych dość abstrakcyjnych reguł i obostrzeń warto przystąpić do analizy kon­ kretnego przykładu. Działanie naszej aplikacji będzie się sprowadzało do prezentowania szere­ gu pytań swoistego quizu. Każde z nich będzie obejmowało sekwencję cyfr i wymagało od użytkownika wskazania kolejnej cyfry danej sekwencji. Na rysunku 2.3 przedstawiono przykładowy ekran aplikacji z prośbą o podanie kolejnej liczby dla następującej sekwencji: 3 14 15 Quiz liczbowy

Rysunek 2.3. Quiz liczbowy

Pilic

fcctycja

Vf|§ * ¿1p

-

[o j©

Mozilte Firefox

Wtdok ||f

55

Historo fj| !

¿aktadki

Narzędzia

Pomoc

http://loca1host:808O/t.wrrtLerquiz, i , d e . . f a | * ^ | ; f l r ł

8



y

1 package com.corejsf, 2 import java.util.ArrayList. 3 4. public class ProblemBean { 5 private ArrayList sequence, 6 private int solution; 7 8 public ProblemBean() {} 9 10. public ProblemBean(int[] values, int solution) { 11 sequence = new ArrayList(), 12. for (int i = 0. i < values length, i++) 13 sequence add(valuesfi]) 14

O

if iie

Milej zabawy z programem Quiz liczbowy! Tw o i wynik 11

t n is

SO

łut ion =

SO

1ution

IB 16

)

17

18

// właściwość sequence. public ArrayList geiSequencet ) { return sequence

19

publ ic void se tSeque nc e( Ar ra yL is t< lntege r> newValue) { sequence = newValue

} 1

20

Zgadmi. idka będzie następna li.-z1 a tej s^kwcn« 11 ‘

21

p 1. . 1 1 4 4 1 i TUJ

// właściwość solution. pu blic int g e t S o l u t i o n ( ) { retunn so l u t i o n . }

22 23

T woi a ■dj'-wiMz

24

publ ic void s e t S o l u t i o n ( i n t

newValue)

{

so lu t io n = newValue

\

}

i Daiej 1

Zafconczcmo

----------

Musimy teraz zdefiniować właściwy komponent quizu z następującymi właściwościami:

Podobne łamigłówki często są stosowane w testach na inteligencję. Rozwiązanie tej zagadki wymaga znalezienia pewnego wzorca (w tym przypadku mamy do czynienia z pierwszymi cyframi liczby n).



problems: właściwość dostępna tylko do zapisu i reprezentująca pytania quizu;



score: właściwość dostępna tylko do odczytu i reprezentująca bieżącą punktację;

Kiedy prawidłowo określimy kolejną cyfrę tej sekwencji (w tym przypadku 9), nasz wynik zostanie powiększony o jeden punkt.



istnieje angielski zwrot (wprost idealnie pasujący do środowisk opartych na Javie), którego zapamiętanie ułatwia identyfikację pierwszych ośmiu cyfr liczby r . Can I have a small container of coffee? Wystarczy policzyć litery w kolejnych wyrazach, aby otrzymać sekw encję 3 1 4 1 5 9 2 6. W ięcej inform acji o podobnych konstrukcjach m ożna znaleźć na stronie internetowej http://dir.yahoo.com/Science/Mathematlcs/Numerical y*_Analysi$/Numbers/Specific_Numbers/Pi/Mnemonics/ MĘ . "Ji . ■ ■ : W naszym przykładzie kolejne pytania zaimplementujemy w klasie QuizBean. W rzeczywistej aplikacji najprawdopodobniej wykorzystalibyśmy bazę danych do składowania tego rodzaju informacji. Celem tego przykładu jest jednak demonstracja sposobu wykorzystywania kom­ ponentów JavaBeans o złożonej strukturze. W pierwszej kolejności przeanalizujemy kod klasy Prob 1emBean, która definiuje dwie wła­ ściwości: solution typu mt oraz sequence typu ArrayList (patrz listing 2.1).

■ current: właściwość dostępna tylko do odczytu i reprezentująca bieżące pytanie; answer: właściwość umożliwiająca odczyt i zapis bieżącej odpowiedzi podanej

przez użytkownika. Właściwość problems w ogóle nie jest wykorzystywana przez nasz przykładowy program — ograniczamy się do inicjalizacji zbioru problemów w konstruktorze klasy QuizBean. Z drugiej strony, w punkcie „Wiązanie definicji komponentów” w dalszej części tego roz­ działu omówimy sposób definiowania zbioru problemów wewnątrz pliku faees-config.xml , a więc bez konieczności pisania jakiegokolwiek kodu. Właściwość current jest wykorzystywana do wyświetlania bieżącego problemu. Warto jednak pamiętać, że wspomniana właściwość reprezentuje obiekt klasy ProblemBean, którego nie możemy bezpośrednio wyświetlać w polu tekstowym. W związku z tym sekwencję liczb uzyskujemy za pośrednictwem jeszcze jednej właściwości: < n.output Text

va 1u e = " # { q u i z . c u r r e n t . s e q u e n c e } " / >

Właściwość sequence reprezentuje wartość typu ArrayList. W procesie wyświetlania można ją przekonwertować na łańcuch, wywołując metodę toStnng. W wyniku tego wywołania otrzymamy następujący łańcuch wynikowy: [3, 1, 4, 1, 5]

56

JavaServer Faces

Rozdział 2. ■ Komponenty zarządzane

I wreszcie musimy się zmierzyć z problemem obsługi właściwości answer. W pierwszej kolejności wiążemy ją z polem tekstowym formularza:

W momencie wyświetlania tego pola tekstowego następuje wywołanie metody zwracającej getAnswer, którą zaimplementowano w taki sposób, aby zwracała pusty łańcuch. Po wysłaniu formularza implementacja JS F wywołuje metodę ustawiającą, przekazując na jej wejściu wartość wpisaną przez użytkownika w tym polu tekstowym. Metoda setAnswer sprawdza odpowiedź, aktualizuje wynik punktowy (w przypadku prawidłowej decyzji użytkownika) i przechodzi do kolejnego problemu.

Na tym możemy zakończyć analizę naszej przykładowej aplikacji. Na rysunku 2.5 przedsta­ wiono strukturę katalogów. Pozostały kod źródłowy przedstawiono na listingach od 2.2 do 2.6.

Rysunek 2.5.

£3 numberquiz.war

f ES

Struktura katalogów przykładowej aplikacji łamigłówki liczbowej

meta-inf

j

'

f

£ 3 W EB-INF

j

f

£ 3 classes

j

j

f

D M ANIFEST.MF

IS

public void setAnswer(String newValue) { try { int answer = Integer.parseInt(newValue.trimO); if (getCurrent() ,getSolution() == answer) score++; currentlndex = (currentlndex + 1) % problems.size();

£3 com F £ü2 corejsf \

Q ProblemBean.class

F

D QuizBean. class

I

I

|

j..................Q messages_de. properties

| D messages.properties

| Q faces-config.xml ‘ Q web.xml D ¡ndex.html

; D index.jsp_____________________

}

catch (NumberFormatException ex) {

Umieszczanie w metodzie ustawiającej wartość właściwości kodu niezwiązanego z jej ory­ ginalnym przeznaczeniem nie jest najlepszym rozwiązaniem. Operacje aktualizacji wyniku i przejścia do kolejnego problemu powinny być realizowane przez metodę obsługującą akcję kliknięcia przycisku. Ponieważ jednak nie analizowaliśmy mechanizmów reagujących na tego rodzaju zdarzenia, na razie będziemy korzystać z elastyczności oferowanej przez metody ustawiające. Inną wadą naszej przykładowej aplikacji jest brak mechanizmu przerywania działania po ostatnim pytaniu ąuizu. Ograniczyliśmy się do rozwiązania polegającego na powrocie do pierwszej strony, aby umożliwić użytkownikowi uzyskiwanie coraz lepszych rezultatów. W następnym rozdziale omówimy sposób implementacji lepszego modelu. Warto raz jeszcze przypomnieć, że celem tej aplikacji jest pokazanie, jak w praktyce konfigurować i wyko­ rzystywać komponenty.

Listing 2.2. Zawartość pliku numberquiz/web/index.jsp 1. 2. taglib uri="http://java.sun.com/jsf/html " prefix="h" %> 3. taglib uri="http://java.sun.com/jsf/core" prefix="f" £> 4_ 5. 6. 7. 8 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26 27 28. 29. 31. 32. 33 34. l

A C V

Na koniec warto zwrócić uwagę na mechanizm umiędzynarodowienia naszej aplikacji przez opracowanie dodatkowych pakietów komunikatów. Zachęcamy do przełączenia przeglądarki na język niemiecki, aby naszym oczom ukazał się ekran podobny do tego z rysunku 2.4.

Rysunek 2.4. Viel SpaS mit dem Zahlenquiz!

Raten Sie die nächste Zahl in der Folge I

57

58

Rozdział 2. ■ Komponenty zarządzane

JavaServer Faces

Listing 2.3. Zawartość pliku numberquiz/src/java/com/corejsf/QuizBean.java 1 package com.corejsf. 2 import java.util.ArrayList. 3 4. public class QuizBean { 5 private ArrayList problems = new ArrayList(). 6 private int currentlndex: 7 private int score. 8

9 10 11 12. 13 14 15 16

public QuizBeanO { problems.add( new ProblemBeanCnew in t[] { 3, 1. 4, 1. 5 }, 9)) //liczbapi problems.add( new ProblemBeanCnew in t[] { 1, 1, 2. 3. 5 }. 8)), //ciągFibonacciego problems.add( new ProblemBeanCnew in t[] { 1, 4. 9, 16, 25 }. 36)), //kwadraty problems.add(

17

new ProblemBeanCnew i n t [ ] { 2.

18 19

problems.add( new ProblemBeanCnew i nt [] { 1.

20

21. 22 23 24 25 26 27 28 29. 30 31 32 33. 34 35. 36 37 38 39 40. 41. 42 43 44 45 46 47 48. }

3, 5, 7,

11 },

1 3 )), // liczby pierwsze

2. 4. 8, 16 }. 32)). //potęgi dwójki

} // Właściwość problems: public void setProblems(ArrayList newValue) { problems = newValue: currentlndex = 0: score = 0. } // Właściwość score: public int getScoreC)

4 xsi •schemaLocation="http://java.sun.coni/xml /ns/javaee 5 http://java.sun.com/xml/ns/javaee/web-facesconfig_l_2.xsd" 6 version="l,2"> 7 8 9 pl 10 de 11 12 13 14. 15 /index.jsp 16 17 next 18 /index.jsp 19. 20 21. 22 23 quiz 24. com.corejsf.QuizBean 25 session 26. 27. 28. 29 30. com.corejsf,messages 31. msgs 32 33. 34

{ return score, }

// Właściwość current: public ProbleniBean getCurrentO { return problems.get(currentlndex); } // Właściwość answer: public String getAnswerO { return } public void setAnswerCString newValue) { try { int answer = Integer.parselntCnewValue.trimO), i f CgetCurrentC) .getSolutionC) == answer) score++, currentlndex = (currentlndex + 1) % problems.size(), } catch (NumberFormatException ex) { } }

Listing 2.4. Zawartość pliku numberquiz/web/WEB-INF/faces-config.xml 1. 2.

l l B | c jJS f e l

Jdk brzmiał slogan reklam o w v opisująco piog ram ow an ie w J a v ip? W rite on re. ■de t1u g e .e iy w n e re

Rysunek 3.4.

,j _

1 Spm vytH odpow iedz J

Dziękujemy za udział w qume. Tv/oj wynik, punktowy. 5 Rozpocznij od nowa [

Zakończono

Kiedy użytkownik klika przycisk Sprawdź odpowiedź, aplikacja sprawdza, czy podana od­ powiedź jest prawidłowa. Jeśli nie, użytkownik otrzymuje jeszcze jedną szansę rozwiązania tego samego problemu (patrz rysunek 3.2). Po dwóch błędnych odpowiedziach aplikacja prezentuje użytkownikowi kolejny problem do rozwiązania (patrz rysunek 3.3). Oczywiście także w razie podania prawidłowej odpowiedzi aplikacja przechodzi do następ­ nego problemu. I wreszcie po rozwiązaniu ostatniego problemu następuje wyświetlenie strony podsumowania z uzyskaną liczbą punktów i propozycją ponownego przystąpienia do ąuizu (patrz rysunek 3.4).

Nasza aplikacja składa się z dwóch klas. Klasa Problem (przedstawiona na listingu 3.1) opisuje pojedynczy problem, czyli pytanie, odpowiedź oraz metodę weryfikacji, czy dana odpowiedź jest prawidłowa.

Listing 3.1. Zawartość pliku javaquiz/src/java/com/corejsf/Probiem.java 1. package com.corejsf; 2.

3. public class Problem { 4. private String question;

82

JavaServer Faces 5.

private String answer;

6

7. 8 9

public Problem(String question, String answer) { this.question = question; this.answer = answer;

}

10 11.

12 13 14 15 16 17 18. 19. 20

public String getQuestionO { return question; } public String getAnswerO { return answer; } //implementacja bardziej wyszukanych mechanizmów weryfikacji wymaga nadpisania tej metody: public boolean isCorrect(String response) { return response.trim O.equalslgnoreCase(answer); }

}

Klasa QuizBean opisuje quiz obejmujący szereg pytań. Egzemplarz tej klasy dodatkowo śledzi bieżące pytanie i łączną punktację uzyskaną przez użytkownika. Kompletny kod tej klasy przedstawiono na listingu 3.2.

Listing 3.2. Zawartość pliku ja vaquiz/src/java/com/corejsf/QuizBean.ja va package com.corejsf: public class QuizBean { private int currentProblem; private int tries, private int score, private String response; private String correctAnswer,

15. 16. 17. 18. 19. 20 21.

22 23 24. 25 26. 27. 28 29. 30.

// Poniżej zakodowano problemy na stałe. W rzeczywistej aplikacji / / problemy najprawdopodobniej zostałyby odczytane z bazy danych. private Problem[] problems = { new Problem( "Jak brzmiał slogan reklamowy opisujący programowanie w Javie? Write once, ...", "run anywhere"), newProblem( "Jak wygląda reprezentacja szesnastkowa czterech pierwszych bajtów każdego pliku klasy?", "CAFEBABE"), newProblem( "Co zostanie wyświetlone przez następujące wyrażenie? System.out.pri ntln( l+\"2\" );", " 12" ) ,

new Problem( "Które słowo kluczowe Javy służy do definiowania podklasy?", "extends"), newProblem( "Jak brzmiała oryginalna nazwa języka programowania Java?", "Oak"), new Problem( "Która klasa pakietujava.util opisuje punkt wczasie?", "Date")

Rozdział 3. ■ Nawigacja 31 32. 33. 34 35 36 37 38. 39. 40. 41. 42 43. 44. 45. 46. 47. 48. 49 50. 51. 52. 53. 54. 55. 56. 57. 58 59 60. 61. 62. 63. 64 65.

}, public QuizBeanO { startOverO, } // Właściwość question: public String getQuestionO { return problems[currentProblem],getQuestion(), } // W łaściwość answ er ■ public String getAnswerO { return correctAnswer; } // Właściwość score: public int getScoreO { return score; } // Właściwość response: public String getResponseO { return response; } public void setResponse(String newValue) {response = newValue, } public String answerActionO { tries++; i f (problems[currentProblem].isCorrect(response)) { score++; nextProblemO; i f (currentProblem == problems.length) return "done", else return "success", } else i f (tries == 1) { return "again", } else { nextProblemO; i f (currentProblem == problems.length) return "done"; else return "failure", }

66.

67. 68. 69 70. 71. 72. 73. 74. 75. 76. 77. 78. 79. 80 81. 82. 83. 84. 85. 86

}

public String startOverActionO { startOverO; return "startOver"; } private void startOverO { currentProblem = 0; score = 0; tries = 0; response = ""; } private void nextProblemO { correctAnswer = problems[currentProblem].getAnswerO, currentProblem++; tries = 0; response = }

}

83

84

JavaServer Faces W analizowanym przykładzie właśnie klasa QuizBean jest właściwym miejscem dla metod odpowiedzialnych za nawigację. Wspomniany komponent dysponuje pełną wiedzą o działaniach użytkownika i może bez trudu określić, która strona powinna być wyświetlona jako następna. Warto się przyjrzeć fragmentowi kodu użytego wewnątrz metody answerActi on klasy Qui zBean. Metoda answerAction zwraca jeden z kilku możliwych łańcuchów: "success" lub "done" (jeśli użytkownik prawidłowo odpowiedział na pytanie), "again" (jeśli użytkownik po raz pierwszy udzielił błędnej odpowiedzi) oraz "fai 1ure" lub "done" (jeśli po raz drugi padła zła odpowiedź). public String answerAction() { tries++, i f (problems[currentProblem].isCorrect(response)) { score++: i f (currentProblem == problems.length - 1) { return "done": } else { nextProblem(): return "success"; } } else i f (tries == 1) { return "again"; } else { i f (currentProblem == problems.length - 1) { return "done"; } else { nextProblem(); return "failure";

Z przyciskami na każdej z tych stron wiążemy wyrażenie odwołujące się do metody answer W \cti on. Na przykład strona index.jsp zawiera następujący element:

W tym przypadku quiz jest egzemplarzem klasy (komponentu) QuizBean zdefiniowanym w pliku konfiguracyjnym faces-config.xml. Na rysunku 3.5 przedstawiono strukturę katalogów naszej aplikacji. Na listingu 3.3 przedsta­ wiono kod strony głównej aplikacji quizu: index.jsp. Kodem stron success.jsp i failure.jsp nie będziemy się zajmować, ponieważ różnią się od kodu strony index.jsp tylko komunikatem wyświetlanym w górnej części.

Listing 3.3. Kod strony javaquiz/web/index.jsp_________________________________________________________ 1. 2. taglib uri="http://java.sun.com/jsf/core" prefix="f" %> 3. taglib uri="http://java.sun.com/jsf/htnil" prefix="h" %> 4. 5.

Rozdział 3. ■ Nawigacja Rysunek 3.5. Struktura katalogów aplikacji quizu o Javie

85

Z3 javaquiz.war L META-INF

j

L Q MANIFEST. MF

f -0 5 web-inf j f • classes

|

j

f

j

|

j

j

|

|

j

|

\

j

j

(¡3 c o m f - [¡3 corejsf - Q

Problem.class

j~- Q QuizBean. class L Q messages.properties Q faces-config.xml Q web.xml

j Q index.html ; D again.jsp I- Q done.jsp i

Q failure.jsp

j Q Index.jsp Q success.jsp_______________________

6. 7. 8. 9 10. 11 12. 13. 14 15. 16. 17. 18. 19 20 21 22. 23. 24.



Strona done.jsp, której kod przedstawiono na listingu 3.4, prezentuje użytkownikowi ostateczny wynik i zachęca go do ponownej gry. Warto zwrócić uwagę na jedyny przycisk dostępny na tej stronie. Na pierwszy rzut oka może się wydawać, że mamy do czynienia z nawigacją statyczną, ponieważ każde kliknięcie tego przycisku powoduje powrót na stronę index.jsp. Okazuje się jednak, że element definiujący ten przycisk wykorzystuje wyrażenie odwołujące się do metody:

Listing 3.4. Zawartość pliku javaquiz/web/done.jsp_____________________________________________________ 1. 2. taglib uri="http://java.sun.com/jsf/core" prefix="f" %> 3. taglib uri="http://java.sun.com/jsf/html" prefix="h" %> 4 5. 6. 7

86

JavaServer Faces 8. 9.

Rozdział 3. ■ Nawigacja



10 11. 12.



13 14

< f: pa ram v a lu e = "# {q u iz .sc o re }"/>

15. 16. 17





Metoda startOverAction wykonuje wszelkie działania niezbędne do przywrócenia oryginal­ nego stanu gry. W tym przypadku jej funkcjonowanie sprowadza się do wyzerowania wyniku punktowego i ustawienia znacznika bieżącego pytania tak, aby wskazywał na pierwsze pytanie w serii: p u b lic S t r in g s ta r t O v e r A c tio n () { s ta rtO v e r(); re tu rn "s ta r t O v e r " ;

Ogólnie metody akcji pełnią dwie funkcje: ■ aktualizują model w reakcji na działania podejmowane przez użytkownika; ■ sygnalizują mechanizmowi nawigacji konieczność skierowania użytkownika w określone miejsce. Podczas lektury rozdziału 7. Czytelnicy przekonają się, że istnieje możliwość dołą­ czania do przycisków także obiektów nasłuchujących akcji. Kiedy użytkownik klika ta k zdefiniowany przycisk, następuje wykonanie kodu metody processAction obiektu nasłuchującego. W arto jed n ak pam iętać, że obiekt nasłuchujący akcji nie kom unikuje się z m echanizm em odpowiedzialnym za nawigację. Na listingu 3.5 przedstawiono plik konfiguracyjny tej aplikacji obejmujący między innymi reguły nawigacji.

10 11. 12 13 14. 15 16 17 18. 19. 20 21. 22. 23. 24 25 26. 27. 28 29. 30. 31. 32. 33. 34. 35 36. 37. 38. 39. 40. 41. 42.

/success.jsp agai n /again.jsp fai1ure /failure.jsp done /done.jsp startOver /index.jsp quiz com.corejsf.QuizBean session com.corejsf nessages msgs

Ponieważ tak dobraliśmy nasze łańcuchy wynikowe, aby umożliwiały jednoznaczną identyfi­ kację kolejnych stron internetowych, możemy się ograniczyć do pojedynczej reguły nawigacji: < n a vig a tio n •fule> success /success jsp< /to-view -id> < / n a v ig a lion case> aga i n

Listing 3.5. Zawartość pliku javaquiz/web/WEB-INF/faces-config.xml___________________________________ 1 2

90

JavaServer Faces

Rozdział 3. ■ Nawigacja

Rysunek 3.8.

T^TM l K

Dziecinnie prosty quiz o J<

Przykład aktualizacji adresu URL przeglądarce na skutek dodania elementu przekierowania

PUK 4

fcdycja

» •I #

Widok

Historia

• ®

t2

Zakładki I.

Narzędzia

j

Pomoc

http://localhost: 8 U80 /|avaquiz/index.taces i

6 oogie

Tak zdefiniowana reguła będzie stosowana dla wszystkich stron rozpoczynających się od przedrostka /secure/. Dopuszczalny jest tylko jeden znak gwiazdki (*), który musi się znaj­ dować na końcu łańcucha identyfikatora.

lak brzmiał slogan reklam'\vv ipisuią..v programowarur w Ja^ier Write onre

Jeśli będzie istniało wiele dopasowań do wzorca zdefiniowanego z użyciem symbolu wielo­ znacznego, zostanie wykorzystane dopasowanie najdłuższe.

r,,n p..et .whptp | S prawdź papowted ; ]

lĘfr Dziecinnie prosty quiz o iavie -Mozilla Ftłefox Plik

Edycja

'sfTŚ v

Widok -

Historia

Ęjjjs Utffi

fjj^ |

Zakładki

Narzędzia

I Zam iast rezygnować z elementu from-view-id, można zastosować jedną z dwóch I poniższych konstrukcji definiujących regułę stosowaną dla wszystkich stron:

Pomoc

http;,7localho5t:80S" ja.

j u c c e >s£ace1 ^ j

j ¡[Ćf^iboogia___

/*

'jiauik-i- ip prawirlł^Wct •''dpnwir '!" T w - -1 vwtul-- punktów; I O t" następne pvtame

lub *

Jak wvglqJa reprezentai ia szesnastkowa ■zterci L pierwszvch K m kw każdego pliku klasy'

i Spiaw dz odpow iedz

W razie braku elementu przekierowania oryginalny adres U R L (w tym przypadku localhost: ^ 8080/javaquiz/index.faces) pozostanie niezmieniony, mimo że użytkownik przejdzie ze strony JS F /index.jsp na stronę /success.jsp. Element przekierowania spowoduje wyświetlenie w polu adresu przeglądarki nowego URL-a (w tym przypadku localhost:8080/javaquiz/ ^ success.faces). Elem ent r e d ir e c t należy stosow ać dla stron, które m ogą być dodaw ane przez użytkowników do zbioru stron ulubionych. tęcti;. - . ■& , . _ '. . .. ..



W przypadku braku elem en tu red i rect m echanizm obsługujący nawigację prze kazuje bieżące żądanie do kolejnej strony, która w ten sposób otrzymuje wszystkie pary nazwa-wartość składowane w zasięgu danego żądania. Okazuje się jednak, że użycie elem entu redirect powoduje utratę danych składowanych w tym zasięgu.

0

Stosowanie elementu from-action Struktura elementu navigation case jest bardziej złożona od tej, do której ograniczaliśmy się w naszych dotychczasowych przykładach. Oprócz elementu f nom-outcome mamy do dyspozycji także element from action. Taka elastyczność okazuje się szczególnie istotna w sytuacji, gdy dysponujemy dwiema odrębnymi akcjami z identycznym łańcuchem akcji lub dwoma wy­ rażeniami odwołującymi się do metod zwracających ten sam łańcuch akcji. Przypuśćmy na przykład, że w naszej aplikacji quizu o Javie metoda startOverAct ion zwraca łańcuch "agam" zamiast łańcucha "stantOven". Ten sam łańcuch może zostać zwrócony przez metodę answerAction. Do rozróżnienia obu przypadków (z perspektywy mechanizmu nawigacji) można wykorzystać element from action. Zawartość tego elementu musi jednak być identyczna jak łańcuch wyrażenia odwołującego się do metody zastosowany w atrybucie action: -#{quiz answerAcnon}

again /aga in jsp ^jquiz s t a r t O v e r A c t i on| aga i n< / f rom -outcome>

Symbole wieloznaczne W elementach

91

fro m -view - id

/ind ex.jsp < / na vig a ti o n -case>

wykorzystywanych w ramach reguł nawigacji można stosować

symbole wieloznaczne. Poniżej przedstawiono odpowiedni przykład: ^ n a v ig a t io n - rule> / s e c u r e / * < / fro m - v iew-id> < n avig a tio n - ca se >

< / n a v i g a t i o n ■r u l e >

Mechanizm odpowiedzialny za nawigację nie wywołuje metody zawartej w znaczniku ■ i i # { . . . } , Odpowiednia m etoda je s t wywoływana, zanim jeszcze wspom niany m e­ chanizm przystępuje do pracy. W tej sytuacji mechanizm nawigacji ogranicza się do wyko­ rzystania łańcucha zdefiniowanego w elem encie from-action w roli klucza um ożliwia­ jącego odnalezienie pasującego przypadku.

92

Rozdział 3. ■ Nawigacja

JavaServer Faces

Algorytm nawigacji Nasze rozważania w tym rozdziale zakończymy precyzyjnym omówieniem algorytmu sto­ sowanego przez mechanizm nawigacji do rozwiązywania problemów niejednoznaczności reguł. Czytelnicy, którzy na tym etapie poznawania technologii JS F nie są tym zaintereso­ wani, mogą ten punkt pominąć i wrócić do niego dopiero wtedy, gdy będą zmuszeni anali­ zować reguły autorstwa innych programistów lub wygenerowane przez zautomatyzowane narzędzia. Algorytm otrzymuje trzy dane wejściowe: 1. wynik, czyli wartość atrybutu acti on lub łańcuch otrzymany wskutek przetworzenia wyrażenia odwołującego się do metody;

2. identyfikator bieżącego widoku; 3. akcję, czyli wartość stałą atrybutu acti on zdefiniowaną dla komponentu interfejsu, który zainicjował procedurę nawigacji. Pierwsza z dwóch faz działania tego algorytmu ma na celu odnalezienie pasującego elementu navigation-rule i obejmuje następujące kroki: 1.

Jeśli wynik ma wartość nul 1, natychmiast zwraca sterowanie i ponownie wyświetla bieżącą stronę.

2. Scala wszystkie reguły nawigacji z identyczną wartością elementu f rom- vi ew-i d.

3. Próbuje odnaleźć regułę, której wartość f rom-vi ew-i d dokładnie odpowiada identyfikatorowi widoku. Jeśli taka reguła istnieje, jest od razu wykorzystywana.

4. Analizuje wszystkie reguły nawigacji, których wartości elementu f rom- vi ew- i d kończą się symbolem wieloznacznym (np. secure). Dla każdej takiej reguły sprawdza, czy przedrostek (czyli łańcuch po usunięciu gwiazdki) jest identyczny jak odpowiedni przedrostek identyfikatora widoku. W razie wykrycia pasujących reguł wykorzystuje tę z najdłuższym pasującym przedrostkiem. 5. Jeśli istnieje reguła bez zdefiniowanego elementu f roni -vi ew-i d, wykorzystuje ją.

6. W razie braku jakiejkolwiek pasującej reguły ponownie wyświetla bieżącą stronę. Druga, ostatnia faza działania tego algorytmu polega na analizie wszystkich elementów navigation-case dopasowanej reguły nawigacji (która może obejmować wiele scalonych elementów navi gati on -rui e z pasującymi wartościami elementu from-vi ew-i d). Poszukiwanie pasującego przypadku wymaga wykonania następujących kroków: 1.

Jeśli istnieje przypadek z pasującymi wartościami elementów from-outcome i from-action, właśnie on jest wykorzystywany.

2. W przeciwnym razie jest wybierany (jeśli istnieje) przypadek z pasującą wartością elementu from-outcome, ale z inną wartością elementu from-action.

3. W przeciwnym razie jest wybierany (jeśli istnieje) przypadek z pasującą wartością elementu from-action, ale z inną wartością elementu from-outcome.

93

4. W przeciwnym razie jest wybierany (jeśli istnieje) przypadek, któiy nie obejmuje ani elementu from-outcome, ani elementu from-action.

5. Jeśli nie istnieje żadne dopasowanie, następuje ponowne wyświetlenie bieżącej strony. Oczywiście nie zachęcamy nikogo do tworzenia niejednoznacznych, nieczytelnych reguł nawigacji w budowanych programach. Dopóki będziemy unikali stosowania symboli wielo­ znacznych i elementów from-action, najprawdopodobniej nie będziemy się musieli zagłębiać w podobne algorytmy.

94

JavaServerFaces

4 Znaczniki standardowe JSF Tworzenie atrakcyjnych aplikacji JS F wymaga stosowania odpowiedniego zbioru bibliotek znaczników technologii JavaServer Faces — zarówno podstawowych, jak i znaczników języka H TM L, czyli łącznie 45 znaczników. Z uwagi na znaczenie i liczbę tych znaczników dla frameworku JS F w tym i kolejnym rozdziale zatytułowanym „Tabele danych” skoncentrujemy się właśnie na analizie znaczników, ich atrybutów i ich optymalnych zastosowań. Nawet najprostsze strony JS F wykorzystują znaczniki obu bibliotek. W iele stron aplikacji JavaServer Faces cechuje się strukturą zbliżoną do przedstawionej poniżej: taglib un="http://java.sun.com/jsf/core" prefix="f" %> taglib uri="http://java.sun.com/jsf/htmT' prefix="h" %>

Aby korzystać z bibliotek znaczników frameworku JSF, musimy je zaimportować za pomocą dyrektywy tagl i b (podobnie jak w powyższym fragmencie kodu). W roli przedrostków pro­ gramista może stosować dowolne wybrane przez siebie nazwy. Zgodnie z konwencją stosujemy przedrostki f oraz h odpowiednio dla bibliotek znaczników podstawowych i znaczników HTML-a. Nasze rozważania w tym rozdziale rozpoczniemy od krótkiego przeglądu elementów biblioteki podstawowej. Wspomniana biblioteka obejmuje 18 znaczników i jest nieznacznie mniejsza od swojego odpowiednika w postaci biblioteki H TM L (obejmującej 25 znaczników). Z uwagi na prostotę tej biblioteki i fakt, że większość jej znaczników została lub zostanie omówiona w innych rozdziałach tej książki, w tym rozdziale będziemy się koncentrować przede wszystkim na bibliotece znaczników języka H TM L. W dalszej części tego rozdziału przystąpimy do omawiania biblioteki znaczników HTM L-a ze szczególnym uwzględnieniem popularnych atrybutów wspólnych dla większości tych znaczników. Na końcu dokonamy analizy poszczególnych znaczników, czemu towarzyszyć będzie prezentacja tabel atrybutów i przykładów kodu, którą w przyszłej pracy można trak­ tować jak swoisty leksykon.

96

Rozdział 4. ■ Znaczniki standardowe JSF

JavaServer Faces

Biblioteka podstawowa obejmuje średnio po 3 ,5 atrybutów na znacznik; w bibliotece HTML-a ta średnia wynosi aż 2 8 .9 .

97

Większość znaczników podstawowych reprezentuje obiekty dodawane do komponentów : ■ atrybuty; ■ mechanizmy nasłuchujące; ■ konwertery;

Przegląd podstawowych znaczników JSF

■ mechanizmy weryfikujące; ■ facety;

Biblioteka podstawowa obejmuje znaczniki, które są niezależne od stosowanej technologii wizualizacji. Znaczniki podstawowe wymieniono i krótko opisano w tabeli 4.1.

■ parametry; ■ elementy selekcji.

Tabela 4.1. Znaczniki podstawowe JSF Znacznik

Opis

Patrz rozdział

view

Tworzy widok najwyższego poziomu.

i.

subview

Tworzy podwidok określonego widoku.

8.

attribute

Ustawia atrybut (parę klucz-wartość) komponentu macierzystego.

4.

param

Dodaje parametr komponentu potomnego do komponentu macierzystego.

4.

facet

Dodaje facetę do komponentu.

4.

actionListener

Dodaje do komponentu obiekt nasłuchujący akcji.

7.

setPropertyActi onLi stener

Dodaje obiekt nasłuchujący akcji i ustawiający właściwość.

7.

valueChangeListener

Dodaje do komponentu obiekt nasłuchujący zmian wartości.

7.

phaseListener(JSF 1.2)

Dodaje do widoku macierzystego obiekt nasłuchujący faz.

7.

converter

Dodaje do komponentu dowolny konwerter.

6.

convertDateTime

Dodaje do komponentu konwerter daty i godziny.

6.

convertNumber

Dodaje do komponentu konwerter liczb.

6.

validator

Dodaje do komponentu mechanizm weryfikujący.

6.

validateDoubleRange

Rejestruje walidator Doubl eRangeVal i dator do weryfikacji wartości komponentu.

validateLength

Weryfikuje długość wartości komponentu.

6.

validateLongRange

Rejestruje walidator LongRangeVal idator do weryfikacji wartości komponentu.

6.

loadBundle

Ładuje pakiet zasobów, zapisując właściwości w strukturze typu Map.

2.

selectitems

Wskazuje wiele elementów dla komponentu umożliwiającego wybór jednej lub wielu opcji.

4.

selectitem

Wskazuje pojedynczy element dla komponentu umożliwiającego wybór jednej lub wielu opcji.

4.

verbatim

Przekształca tekst zawierający znacznik w komponent.

4.

(JSF 1.2)

6.

Biblioteka podstawowa obejmuje też znaczniki umożliwiające definiowanie widoków i podwidoków, ładowanie pakietów zasobów oraz umieszczanie na stronie dowolnego tekstu. Wszystkie znaczniki podstawowe wymienione w tabeli 4.1 zostały lub zostaną szczegóło­ wo omówione w innych częściach tej książki. Przyjrzyjmy się teraz znacznikom f : a ttri bute, f : parani i f : facet. Każdy komponent może składować w swojej mapie atrybutów dowolne pary nazwa-wartość. Istnieje możliwość ustawienia atrybutu w kodzie strony i jego późniejszego odczytania na poziomie programu. Przykład takiego rozwiązania przedstawiono w punkcie „Przekazywanie atrybutów mecha­ nizmom konwertującym” w rozdziale 6., gdzie zastosowano następującą definicję separato­ ra grup cyfr numeru karty kredytowej:

Konwerter odpowiedzialny za formatowanie danych wynikowych otrzymuje ten atrybut z komponentu umieszczonego na stronie.

f:

Znacznik parani dodatkowo umożliwia nam definiowanie par nazwa-wartość, w których część wartości znajduje się w odrębnym komponencie potomnym. Na pierwszy rzut oka taki mechanizm składowania sprawia wrażenie niepotrzebnie skomplikowanego, musimy jednak pamiętać, że komponenty potomne tworzą listę (zamiast mapy). Znacznik f : param stosujemy w sytuacji, gdy chcemy przekazać wiele wartości dla pojedynczej nazwy (lub w ogóle bez nazwy). Z przykładem takiego rozwiązania mieliśmy do czynienia w podrozdziale „Kom uni­ katy obejmujące zmienne” w rozdziale 2., gdzie komponent h :outputForm at zawierał listę potomków w formie znaczników f: pa ram. 1 1 1 ! Komponent h;commandl i nk przekształca swoje elem enty potom ne f: pa ram w pary I m I nazwa-wartość żądania protokołu HTTP. M echanizm nasłuchiwania zdarzeń, który je s t aktywowany w odpowiedzi na kliknięcie łącza przez użytkownika, m oże następnie uzyskiwać te pary nazwa-wartość za pośrednictwem mapy żądania. Opisywana technika zostanie zadem onstrow ana w rozdziale 7. I wreszcie element f : fa c e t dodaje nazwany komponent do mapy facet danego komponentu. Wbrew pozorom faceta nie jest komponentem potomnym — każdy komponent obejmuje zarówno listę swoich komponentów potomnych, ja k i mapę nazwanych komponentów facet.

98

Rozdział 4. ■ Znaczniki standardowe JSF

JavaServerFaces Komponenty facet z reguły są wizualizowane w specjalnym miejscu. Sposób korzystania w tabelach danych z facet nazwanych "header" i "footer" omówimy w podrozdziale „N a­ główki, stopki i tytuły” w rozdziale 5. Atrybuty znaczników f :attribute, f: parani i f: facet przedstawiono w tabeli 4.2.

Tabela 4.2. Atrybuty elementów f:attribute,

i f:facet

Tabela 4.3. Znaczniki JSF HTML Znacznik

Opis

form

Formularz HTML

inputText

Kontrolka jednowierszowego pola tekstowego

inputTextarea

Kontrolka wielowierszowego pola tekstowego

inputSecret

Kontrolka pola hasła

iriputHidden

Pole ukryte

Atrybut

Opis

name

Nazwa atrybutu, komponentu parametru lub facety.

value

Wartość atrybutu lub parametru komponentu (poza elementem f: facet).

outputLabel

Etykieta innego komponentu

binding, id

Patrz tabela 4.4 (tylko f: param).

outputLink

Kotwica HTML-a

outputFormat

Odpowiednik kontrolki outputText umożliwiający formatowanie komunikatów złożonych

outputText

Jedno wierszo we, wyjściowe pole tekstowe

commandButton

Przycisk akceptacji, czyszczenia formularza lub opcji

commandLink

Łącze działające jak przycisk

message

Wyświetla najnowszy komunikat dla komponentu

messages

Wyświetla wszystkie komunikaty

graphiclmage

Wyświetla obraz

selectOneListbox

Lista umożliwiająca wybór pojedynczego elementu

selectOneMenu

Menu umożliwiające wybór pojedynczego elementu

selectOneRadio

Zbiór przycisków opcji

selectBooleanCheckbox

Pole wyboru

selectManyCheckbox

Zbiór pól wyboru

selectManyListbox

Lista umożliwiająca wybór wielu elementów

selectManyMenu

Menu umożliwiające wybór wielu elementów

panel Grid

Tabela języka HTML

panelGroup

Dwa lub wiele komponentów rozmieszczanych łącznie

dataTable

Kontrolka tabeli oferująca bogatą funkcjonalność

column

Kolumna w tabeli dataTabl e

- “A 1

-■■■■ ■ — -r-r-?— T7 7 7 —

t

^

7

......

■ .■ X

77

¡^ ■ W s z y s tk im atrybutom znaczników (z wyjątkiem var oraz id ) m ożna przypisywać H wyrażenia reprezentujące wartości lub wyrażenia odwołujące się do m etod. Atry­ butowi v a r zawsze należy przypisywać łańcuchy, natom iast atrybutowi id m ożna przy­ pisywać łańcuchy lub wyrażenia bezpośrednie $ { . . . - } .

Przegląd znaczników JSF reprezentujących znaczniki HTML (JSF HTML) Znaczniki JS F H T M L reprezentują następujące rodzaje komponentów: ■ komponenty wej ściowe, ■ komponenty wyj ściowe, ■ polecenia, ■ komponenty selekcji, ■ inne. Do kategorii „inne” zaliczamy formularze, komunikaty i komponenty odpowiedzialne za roz­ mieszczanie pozostałych komponentów. Wszystkie znaczniki HTML-a wymieniono w tabeli 4.3.

99

Znaczniki języka H T M L można podzielić na następujące kategorie: ■ rozmieszczenia (panel Gri d); ■ wejściowe (in p u t.. .); ■ tabeli danych (dataTable) — patrz rozdział 5.; ■ wyjściowe (output.. .); ■ błędy i komunikaty (message, messages). ■ poleceń (commandButton i commandLi nk); ■ wyboru (checkbox, 1i stbox, menu, radi o);

Znaczniki JS F H T M L wykorzystują atrybuty wspólne, atrybuty przenoszone HTM L-a oraz atrybuty obsługujące dynamiczny H TM L.

100

Rozdział 4. ■ Znaczniki standardowe JSF

üavaServerFaces

K M Znaczn ki MTV IL-a m ogą sprawie >ć w rażeni przesadr lie długich — na prz ykład ■ J znaczni k sele ctManyListbox m ożna by z pow odzeń e m zas tą p ić znaczn ikiem multiList. 0 kazuje się jednak, że takie opisowe ilazwy dobr ze reprezentują komb ¡nacje komponentóv i mecł lanizmów wizualiza«;ji, stąd ws pomniany z nacznik selectManyLi stbox reprezentuje kom po nent selectMany fwłączony ; mechanlz m em wizualizacji lis ¡tbox, Znajom ość t ypu ko m ponentu repreze ntowanege przez zn

Wyrażenia reprezentujące wartość sprawdzają się w sytuacji, gdy musimy kontrolować sto­ sowane style z poziomu kodu aplikacji. Za pomocą atrybutu rendered możemy dodatkowo określać, czy poszczególne komponenty w ogóle mają być prezentowane na stronie. Atrybut rendered okazuje się bardzo wygodny w wielu przypadkach, choćby wtedy, gdy chcemy decydować o wyświetlaniu bądź ukrywaniu opcjonalnej kolumny tabeli. Zam iast trwale zapisywać style w kodzie stron, wartę rozważyć stosowanie arkuszy stylów. Styl CSS m ożna zdefiniować w następujący sposób: •prompts { color:red;

Należy tę konstrukcję um ieścić w arkuszu stylów, np. Odwołanie do tego arkusza wym aga um ieszczenia elem entu li nk w ram ach elem entu head strony JSF:

:

W kolejnym kroku powinniśmy użyć atrybutu styleClass:

'

-



(wyświetlany po próbie wysłania formularza zakończonej niepowodzeniem)

r

......... “

.......

........i

(wyświetlany po próbie wysłania formularza zakończonej niepowodzeniem)

123456 ]



1

¡123456

]

Przykłady zastosowań znacznika h: i nputSecret dobrze ilustrują możliwe sposoby wykorzystywania atrybutu redisplay. Jeśli przypiszemy temu atrybutowi wartość true, odpowiednie pole tekstowe zachowa swoją wartość pomiędzy żądaniami, co oznacza, że jego wartość zostanie ponownie wyświetlona po ponownym załadowaniu danej strony. Jeśli atrybutowi redi spl ay przypiszemy wartość fal se, wartość tego pola nie zostanie utrwalona i tym samym nie będzie widoczna po ponownym wyświetleniu strony. Atrybut size określa liczbę znaków widocznych w definiowanym polu tekstowym. Ponie­ waż jednak większość czcionek cechuje się zmienną szerokością znaków, atrybut si ze jest dalece nieprecyzyjny, co dobrze widać w piątym spośród przykładów przedstawionych w tabeli 4.9, gdzie mimo przypisania atrybutowi size wartości 5 pole tekstowe pomieściło sześć znaków. Atrybut maxlength określa maksymalną liczbę znaków wyświetlanych w polu tekstowym. Okazuje się, że atrybut maxlength gwarantuje dużo większą precyzję niż atrybut si ze. Oba atrybuty mają charakter atrybutów przekazywanych HTML-a. Praktyczne przykłady użycia znacznika h: i nputTextarea przedstawiono w tabeli 4.10. W znaczniku h: i nputTextarea możemy stosować atrybuty cols i rows, które określają od­ powiednio liczbę kolumn i wierszy wielo wierszowego pola tekstowego. Znaczenie atrybutu col s jest analogiczne jak w przypadku atrybutu si ze znacznika h: i nputText, zatem atrybut col s jest równie nieprecyzyjny. Jeśli wartości elementu h: i nputTextarea przypiszemy jeden długi łańcuch, łańcuch ten zostanie umieszczony w całości w jednym wierszu (patrz trzeci przykład przedstawiony w tabeli 4.10). Gdybyśmy chcieli umieścić dane w odrębnych wierszach, powinniśmy użyć znaków nowego wiersza (\n) wymuszających podział danych na wiersze. Na przykład w ostatnim przykładzie z tabeli 4.10 uzyskujemy dostęp do właściwości datalnRows komponentu wspierającego, który zaimplementowano w następujący sposób:

114

JavaServer Faces

Rozdział 4. ■ Znaczniki standardowe JSF

Tabela 4.10. Przykłady zastosowań znacznika h:¡nputTextarea Przykład

Aplikacja ilustrująca możliwe zastosowania jednoi wielowierszowych pól tekstowych

Elekt





115

Edycja

Widok

Historia

Zakładki

Narzędzia

[eOjI lTj O '

rsfm Pomoc

fjf1j • ■ http://locaihost8080/perso { * s i a : :

I Zafconczono

I m ię : W o jcie c h

Informacji? o uzytkcwwku:



Listing 4.4. Kod strony personalData/web/thankYou.jsp 1. 2. 3. taglib uri="http://java.sun.com/jsf/html" prefix="h" %> 4.

5. 6. 7.

|



121

Akcje h:conimanclButton i h: commandLi nk reprezentują komponenty poleceń JS F — oznacza to, że framework JS F generuje odpowiednie zdarzenia akcji i wywołuje je w odpowiedzi na aktywację przycisków lub łączy. Szczegółowe omówienie mechanizmu obsługi zdarzeń dla komponentów poleceń można znaleźć w podrozdziale „Zdarzenia akcji” w rozdziale 7. Znacznik h: outputLi nk generuje element anchor języka H TM L wskazujący na określony zasób (obraz bądź stronę internetową). Kliknięcie wygenerowanego w ten sposób łącza powoduje natychmiastowe przejście do wskazywanego zasobu bez dalszych odwołań do frameworku JSF . W tabeli 4.14 wymieniono i krótko opisano (częściowo wspólne) atrybuty znaczników h:commandButton i h:commandLink.

Stosowanie przycisków poleceń Znacznik h:comrnandButton generuje element input języka H TM L typu butlon, image, submit lub reset (w zależności od zdefiniowanych atrybutów). Przykładowe zastosowania znacznika h: comniandButton przedstawiono w tabeli 4.15.



Trzeci i czwarty przykład z tabeli 4.13 ilustrują możliwe zastosowania atrybutu escape. Jeśli atrybutowi value znacznika h :outputText przypiszemy wyrażenie < !~ J S F 1.1 -->

("wyzeruj”!

image



j w y łą c z o n y j



Tabela 4.16. Przykłady zastosowań znacznika hxommandLink Przykład

Efekt



zarejestruj



2 7. 8. /index.jsp 9 10 thankYou 11. /thankYou.jsp 12. 13 14. 15 16. localeChanger 17. -managed-bean-class>com.corejsf.ChangeLocaleBean 18. session 19. 20. 21 22. user 23. com.corejsf UserBean 24 session 25 26 27. 28 29. com.corejsf,messages 30. msgs 31. 32 33.

Tabela 4.19. Przykłady użycia znaczników selekcji Znacznik

Wygenerowany kod HTML-a

Przykłady

h:selectBooleanCheckbox



Chcę otrzymywać pocztę: ff]

h:selectManyCheckbox



h:selectOneRadio



Same pola wyboru definiujemy za pomocą znaczników f : selectltem lub f : select Items (patrz punkt „Elem enty” ).

134

Rozdział 4. ■ Znaczniki standardowe JSF

JavaServer Faces Znacznik h:selectManyCheckbox generuje element ta ble języka H TM L; poniżej przedstawiono kod HTML-a wygenerowany dla naszego przykładu:



Tym razem nie zdefiniowaliśmy atrybutu size, co oznacza, że lista zostanie wydłużona do rozmiarów umożliwiających prezentację wszystkich dostępnych elementów. Na podstawie tej konstrukcji zostanie wygenerowany następujący fragment kodu HTML-a:

W powyższym fragmencie kodu wszystkie wartości przypisywane atrybutom íteniValue mają postać łańcuchów. W podpunkcie „Wiązanie atrybutu valué” omówimy techniki przypisy­ wania temu atrybutowi danych innych typów. Oprócz etykiet i wartości dla poszczególnych elementów możemy definiować opisy oraz stan, w którym nie są aktywne:

Opisy elementów tworzymy wyłącznie z myślą o narzędziach odwołujących się do kodu naszych stron JS F , ponieważ ich treść nie wpływa na kształt generowanego kodu HTML-a.

139

Z nieco inną sytuacją mamy do czynienia w przypadku atrybutu itemDi sabl ed, którego wartość jest przekazywana do kodu języka H TM L. W tabeli 4.21 wymieniono i krótko opisano atrybuty znacznika f: selectltem.

Tabela 4-21. Atrybuty znacznika f¡selectltem Atrybut

Opis

binding

Reprezentuje związek z komponentem JavaBean — więcej informacji na temat związków z komponentami można znaleźć w rozdziale 2.

id

Identyfikator komponentu.

itemDescription

Opis wykorzystywany wyłącznie przez narzędzia zewnętrzne.

itemDisabled

Wartość logiczna ustawiająca dla danego elementu atrybut di sabl ed języka HTML.

itemLabel

Tekst wyświetlany przez dany element.

itemValue

Wartość danego elementu (przekazywana na serwer w formie parametru żądania).

value

Wyrażenie reprezentujące wartość i wskazujące na egzemplarz klasy Selectltem.

escape (JSF 1.2)

Ma wartość true, jeśli znaki specjalne użyte w wyrażeniu przypisanym atrybutowi val ue mają być konwertowane na odpowiednie sekwencje; lub fal se, jeśli wyrażenie przypisane atrybutowi val ue nie ma podlegać konwersji.

Za pośrednictwem atrybutu value znacznika f: sel ectltem możemy uzyskiwać dostęp do egzemplarzy klasy Sel ectltem utworzonych w ramach komponentu JavaBean:

Wyrażenie reprezentujące wartość, które w przedstawionym przykładzie przypisano atrybutowi val ue, wskazuje na metodę zwracającą egzemplarz klasy javax. faces .model. Sel ectltem: public Selectltem getCheeseltemO return new SelectTtemCSer"):

{

} fflffl javax.faces.model.Selectltem ■ S e le c t!temCObject value) Tworzy obiekt klasy Selectltem na podstawie przekazanej wartości. Etykieta konstruowanego elementu jest uzyskiwana przez zastosowanie dla argumentu value metody toStnngO. ■ SelectitemCObject value. String label) Tworzy obiekt klasy Se l ect Ltem na podstawie przekazanej wartości i etykiety. ■ SelectitemCObject value. String label. String description) Tworzy obiekt klasy Select I tem na podstawie przekazanej wartości, etykiety i opisu. ■ SelectitemCObject value. String label. String description, boolean disabled) Tworzy obiekt klasy Sel ectltem na podstawie przekazanej wartości, etykiety, opisu i stanu aktywności.

140

JavaServer Faces

Znacznik f:selectltems Z lektury podpunktu „Znacznik f:selectltem” wiemy, że znacznik f : sel ectItem jest stosowany niemal na każdym kroku w konstrukcjach reprezentujących kontrolki selekcji. Definiowanie więcej niż kilku elementów za pośrednictwem tego znacznika jest jednak dość kłopotliwe. Okazuje się, że pierwszy fragment kodu ze wspomnianego punktu można by zredukować do wyrażenia w następującej formie: Woda Kawa Herbata ser marynata musztarda sałata cebula

private Select 1lemGroup condiments = new S e le c t 1temGroup( "Dodatk i ". //wartość "przyprawy w menu". f a 1s e . COnd i men 11tems ).

//opis //stan // elementy selekcji

Warto zwrócić uwagę na wykorzystanie obiektów klasy SelectltemGroup w procesie wy­ pełniania tablicy egzemplarzy klasy Selectltem. Takie rozwiązanie jest możliwe, ponieważ klasa SelectltemGroup rozszerza klasę Selectltem. Same grupy są tworzone i inicjalizowane w następujący sposób: private Selectltem[] burgerltems = { new Select 1tem("Poczwórna"). new Select1tem("Pojedyncza" ). new Select 1tem("wegetamańska" ).

f

143

private SelectItem[] beverageLtems = { new Select 1tem("Coca-cola"). new Selectltem!"Pepsi"), new Selectltem!"Woda"),

K M Specyfikacja HTM L 4 .0 1 nie przewiduje m ożliwości zagnieżdżania elem en tó w M i l optgroup, co byłoby korzystne, gdybyśmy chcieli konstruować m enu kaskadow e. Co więcej, w spom niana specyfikacja nie wspom ina o planach wprowadzenia tej moż­ liwości w przyszłych wersjach HTML-a.

javax.faces.model.SelectltemGroup ■ SelectItemGroup(Stnng label) Tworzy grupę z etykietą, ale bez elementów selekcji. ■ SelectrtemiGroup(String label. Stnng description. boolean disabled. SelectltemLJ items) Tworzy grupę na podstawie otrzymanej etykiety, opisu (ignorowanego w implementacji referencyjnej JavaServer Faces), wartości logicznej określającej

144

Rozdział 4. ■ Znaczniki standardowe JSF

JavaServer Faces stan wszystkich elementów oraz tablicy elementów selekcji (użytych do wypełnienia tej grupy). ■ setSelectItems( SelectItem [] i tems) Wskazuje dla danej grupy tablicę obiektów klasy Sel ectltem.

Wiązanie atrybutu value Niezależnie od tego, czy stosujemy pola wyboru, menu czy listy, najprawdopodobniej bę­ dziemy chcieli mieć dostęp do informacji o zaznaczonych elementach. Do tego celu służą znaczniki selectOne i selectMany, których atrybut value reprezentuje właśnie zaznaczony element lub elementy. Możemy na przykład określić zaznaczony element za pośrednictwem atrybutu value znacznika h:selectOneRadio:

Wyrażenie reprezentujące wartość # {form, beverage} odwołuje się do właściwości beverage komponentu JavaBean nazwanego form. Właściwość beverage zaimplementowano w nastę­ pujący sposób: private Integer beverage, public Integer getBeverage!) { return beverage;

}

public void setBeverage!Integer newValue) { beverage = newValue;

} Łatwo zauważyć, że właściwość beverage jest obiektem typu Integer. Oznacza to, że dla przycisków opcji musimy zdefiniować wartości całkowitoliczbowe. Same przyciski definiu­ jemy za pomocą znacznika f :selectltems z atrybutem value wskazującym na właściwość beverageltems komponentu form: private static Selectltem[] beverageltems = { new Sel ectltem! 1, "Woda"), / / wartość, etykieta new Selectltem(2, "Coca-cola"), new Selectltem(3, "Sok pomarańczowy")

}; public Selectltemf] getBeverageltems!) { return beverage;

} W powyższym przykładzie napoje są reprezentowane przez wartości typu Integer. Mogliby­ śmy wybrać dowolny inny typ, jednak pod warunkiem zachowania zgodności typów wła­ ściwości reprezentujących elementy i zaznaczony element. W tym przypadku lepszym roz­ wiązaniem byłoby użycie typu wyliczeniowego (przykład takiego podejścia przedstawiono na listingu 4.14). Za pośrednictwem znacznika selectMany możemy śledzić zaznaczenie wielokrotne. Znacz­ nik selectMany obejmuje atrybut value, który umożliwia nam określanie jednego lub wielu zaznaczonych elementów. Wartość tego atrybutu musi mieć postać tablicy lub listy obiektów typu umożliwiającego niezbędną konwersję.

145

Przyjrzyjmy się teraz różnym typom danych. Aby umożliwić użytkownikowi wybór wielu dodatków, w kodzie odpowiedniej strony JS F użyjemy znacznika h : sel ectManyLi stbox:

Poniżej przedstawiono definicje właściwości condinientI tenis i condiments: private static Selectltem[] condimentItems = { new Selectltem!1, "Ser"), new Selectltem(2, "Marynata"), new Selectltem(3, "Musztarda"), new Selectltem(4, "Sałata"), new Selectltem(5, "Cebula"),

};

public Selectltem[] getCondimentItems!) { return condimentltems;

} private Integer[] condiments; public void setCondiments(Integer[] newValue) { condiments = newValue;

}

public Integer[] getCondiments() { return condiments;

} Zamiast tablicy obiektów typu Integer właściwość condiments mogłaby równie dobrze reprezentować tablicę typu prostego i nt: private i nt[] condiments; public void setCondiments(int[] newValue) { condiments = newValue:

}

public in t[] getCondiments() { return condiments;

} Gdybyśmy atrybutom wartości elementów przypisali łańcuchy, moglibyśmy wykorzystać do reprezentowania zaznaczonych elementów właściwości w formie tablicy lub listy łańcuchów: private static Selectltem[] condimentltems = { new Selectltem!"ser", "Ser"), new Selectltem!"marynata", "Marynata"), new Selectltem!"musztarda", "Musztarda"), new Selectltem!"sałata", "Sałata"), new Selectltem!"cebula", "Cebula"),

};

public Selectltem[] getCondimentltems() { return condimentltems;

}

private String[] condiments; public void setCondiments(String[] newValue) { condiments = newValue;

}

public String[] getCondiments() { return condiments;

146

JavaServer Faces

Rozdział 4. ■ Znaczniki standardowe JSF

W powyższym przykładzie właściwość condiments ma postać tablicy łańcuchów. Okazuje się, że zamiast tej tablicy można by użyć kolekcji: private static Collection condiments, static { condiments = new ArrayListList(); condiments.add(new Selectltem!"ser", "Ser")), condiments.add(new Selectltem!"marynata", "Marynata")); condiments.add(new Selectltem!"musztarda", "Musztarda")); condiments.add(new Selectltem!"sałata", "Sałata")); condiments.add(new Selectltem!"cebula", "Cebula"));

Listę umożliwiającą wybór roku urodzenia zaimplementowano z wykorzystaniem znacznika h : sel ectOneLi stbox; pola wyboru języka zaimplementowano za pomocą znacznika h : sel ect ^ManyCheckbox; wybór poziomu edukacji jest możliwy dzięki użyciu znacznika h: sel ect ^OneRadio. Kiedy użytkownik wysyła wypełniony formularz, mechanizm nawigacji JavaServer Faces wyświetla stronę prezentującą wpisane dane. Strukturę katalogów aplikacji z rysunku 4.7 przedstawiono na rysunku 4.8. Kod stron JS F , komponentu Regi sterForm, pliku konfiguracyjnego oraz pliku pakietu komunikatów przed­ stawiono na listingach od 4.12 do 4.16.

}

public List getCondiments() { return condiments; }

¡ 3 select.war

Rysunek 4.8.

Podsumowanie: pola wyboru, przyciski opcji, menu i listy Nasze rozważania w podrozdziale poświęconym znacznikom selekcji zakończymy analizą przykładu ilustrującego praktyczne zastosowania niemal wszystkich opisanych elementów. Nasza przykładowa aplikacja (patrz rysunek 4.7) implementuje formularz, w którym użytkow­ nik powinien wpisać swoje dane osobowe. Do określenia, czy użytkownik chce otrzymy­ wać wiadomości poczty elektronicznej, wykorzystano znacznik h;selectBooleanCheckbox. Znacznik h;selectManyMenu umożliwia użytkownikowi wybór dni tygodnia, w którym oczekuje kontaktu.

9 C3

Struktura katalogów przykładowej aplikacji gromadzącej dane osobowe

Q

lĘfy Poia wyboru, .przyciski opqj '

Edycja

W.dok

Historia

Zakładki

■ t C3 com I

?

m.

!

I I

|

f-

.

é i:

I

Prosimy podać następujące informacje Dawkujemy za wysłanie ds

Imię i nazwisko: Proszę o kontakt

Możliwość kontaktu

i

!Jan Kowalski

;

Wybrane dni kontaktu: 2 ,'. Rok urodzenia

19

Języki

Pl

Wiorst Środa Czosnek Piątek

Kolory:

C*

Sobota

9

com E 3 corejsf ‘ Q messages properties

Listing 4.12. Kod strony select/web/indexjsp

1901 ( 190? >903 190-a

13 Czerwony §3 Niebieski D

Rółty

Angielski Włoski Hiszpański Rosyjski €• Średnie Jakie masz wykształcenie?

C3

Q showlnformatton jsp

1

Wybierz język, którym się posługujesz.

£ ¡3 bm

r Q index jsp

Wykształcenie:

Wybierz swoje ulubione kolory:

Q faces-config xml Q web xml

j Q index ht ml

m' ’ Nieazieia

Wybierz rok urodzenia.

Q messages properties

I Q styles css

truj

Wybierz dzień (dni) tygodnia, w którym mamy się z Tobą skontaktować

; Q RegisterForm class

) I D "b

9



C3 co rejsf Q RegisterFormJ Education class

,

Pomoc

] ntrp.1 .-iio^air>oit:bU8u^ciecvmde^’

M A N IF E S T MF

9 ET3 Classes

[ ca ; iii j g

jp Pata wybaw, przyciski opcji, menu »foty -

m e t a - in f

9 UWEB-iMF

i

Plik

%

147

Licencjat Wyższe

i Ę $ Doktorat . ' ^ M a n ę osobową j

Rysunek 4.7. Przykład użycia w praktyce pól wyboru, przycisków opcji, menu i list

D

Zielony

0

Pomarańczowy



2 3

tagl ib u n -"http://java sun com/jsf/core" pref ix=" f ’’ %> 45 46 47. 48 49 50

Listing 4.14. Kod komponentu select/src/java/com/corejsf/RegisterForm.java 1. package com.corejsf, 2

3. 4. 5 6 7. 8. 9.

import import import import import import import

java.text.DateFormatSymbols; java.util.ArrayList; java.util.Calendar; java.util.Collection; java.util.HashMap; java.util.LinkedHashMap; java.util.Map;

10.

11 import javax.faces.model.Selectltem;

12. 13. public class RegisterForm { 14. enum Education { HIGH_SCHQOL, BACHELOR, MASTER, DOCTOR }; 15 16. private String name; 17. private boolean contactMe; 18. private Integer[] bestDaysToContact, 19. private Integer yearOfBirth; 20 private String[] colors, 21. private String[] languages, 22. private Education education; 23. 24. // Właściwość name: 25.public String getNameO { 26. return name; 27. } 28. public void setName(String newValue) { 29. name = newValue, 30. } 31. // Właściwość contactMe: 32. 33. public boolean getContactMeO { 34. return contactMe; 35. } 36 public void setContactMe(boolean newValue) { 37 contactMe = newValue; 38. } 39. 40 . // Właściwość bestDaysToContact: 41. public Integer[] getBestDaysToContact() { 42. return bestDaysToContact; 43. } 44. public void setBestDaysToContact(Integer[] newValue) { 45. bestDaysToContact = newValue,

Rozdział 4. ■ Znaczniki standardowe JSF 46. 47. 48. 49. 50. 51. 52. 53. 54. 55. 56. 57. 58. 59. 60. 61. 62 63. 64. 65. 66 67. 68 69. 70. 71. 72 73 74 75. 76. 77. 78. 79. 80. 81 82. 83. 84. 85. 86. 87. 88. 89. 90. 91. 92 93. 94. 95. 96 97 98 99

} // Właściwość yearOfBirth: public Integer getYearOfBirth() { return yearOfBirth; } public voidsetYearOfBirth(Integer newValue) { yearOfBirth = newValue; } // Właściwość colors: public String[] getColorsO { return colors, } public void setColors(String[] newValue) { colors = newValue; } // Właściwość languages: publicStri ng[] getLanguagesC) { return languages, } public void setLanguages(String[] newValue) { languages = newValue; } // Właściwość education: public Education getEducation() return education; }

{

public void setEducation(Education newValue) { education - newValue; } // Właściwość yearltems: public Collection getYearItems() { return birthYears; } // Właściwość daysOfTheWeekltems: public Selectltemf] getDaysOfTheWeekItems() { return daysOfTheWeek; } // Właściwość languageltems: public Map getLanguageItems() { return languageltems; } // Właściwość colorltems: public Selectltem[] getColorltemsO { return colorltems; }

100.

101 102.

// Właściwość educationltems: public Selectltem[] getEducationItems() {

151

152

Rozdział 4. ■ Znaczniki standardowe JSF

JavaServer Faces 103 104. 105 106 107. 108 109

return educationltems; } // Właściwość bestDaysConcatenated: public String getBestDaysConcatenated() { return concatenate(bestDaysToContact); }

110.

Ill. 112 113. 114 115 116 117. 118 119. 120 . 121. 122. 123. 124. 125. 126. 127. 128. 129. 130 131 132. 133. 134. 135. 136. 137 138. 139. 140. 141. 142. 143 144. 145. 146. 147 148. 149. 150 151. 152. 153. 154. 155 156. 157. 158. 159.

// Właściwość languagesConcatenated: public String getLanguagesConcatenated() { return concatenate(languages), } // Właściwość colorsConcatenated: public String getColorsConcatenated() { return concatenate(colors); } private static String concatenate(Object[] values) { i f (values == null) return StringBuiIder r = new StringBui1der(), for (Object value : values) { i f (r.lengthO > 0) r append(’ , ') ; r.append(value.toStringO); } return r toStringO , } private static Selectltem[] colorltems = { new Selectltem("Czerwony"), new Selectltem("Niebieski"), new Selectltem("Żółty"), new SelectltemC'Zielony"), new SelectItern( "Pomarańczowy") }, private static Selectltem[] educationltems = { new SelectItem(Education.HIGH_SCHOOL, "Średnie"), new Selectltem(Education.BACHELOR,"Licencjat"), new SelectItem(Education.MASTER, "Wyższe"), new SelectItem(Education.DOCTOR, "Doktorat") }, private static Map languageltems; static { languageltems = new LinkedHashMap(); languageltems. put ( "angi el ski", "en"); //etykieta, wartość languageltems.put( " polski", "pi"), languageltems,put("rosyjski", "ru"), languageltems.put( "włoski", " it " ), languageltems.put("hiszpański", "es"), } private static Collection birthYears; static { birthYears = new ArrayList();

160 161. 162. 163 164. 165. 166 167. 168 169. 170. 171 172. 173. 174.

for (int i =1900; i

italic.

6 }

|

* Q indexJ5p_________________

2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21.

1 body { 2 background #eee.

10 11 12

evenRows { background

PowderBlue.

oddRows { background MediumTurquoise.

}

163

164

JavaServer Faces

5 Tabele danych Klasyczne aplikacje internetowe niemal na każdym kroku wykorzystują dane stabelaryzowane. Tabele języka H T M L były niegdyś stosowane także w roli menedżerów układu na stronach aplikacji internetowych. Mimo że zadanie to można obecnie z powodzeniem realizować za pomocą arkuszy stylów CSS, wciąż nie ma alternatywy dla tabel wykorzystywanych do pre­ zentowania danych stabelaryzowanych. W niniejszym rozdziale skoncentrujemy się na znaczniku h:dataTable, czyli wygodnym, ale dość ograniczonym komponencie umożliwiającym nam operowanie na danych stabelary­ zowanych.

P | j Znacznik h :d a ta T a b le reprezentuje w istocie parę kom ponentu i m echanizm u H mI odpowiedzialnego za wizualizację. M ożem y więc bez trudu wyświetlać komponenty JSF w komórkach tabel, dodawać do tabel nagłówki i stopki, a także zarządzać wyglądem i sposobem obsługi tych tabel za pośrednictwem klas CSS. Okazuje się jednak, że znacz­ nik h ;d a ta T a b le nie oferuje zaawansowanych funkcji, których m ożna by oczekiwać od tego rodzaju kom ponentów. Gdybyśmy na przykład chcieli posortować zawartość kolumn tabeli, m usielibyśm y sam i opracować odpowiedni kod. W ięcej informacji ną ten te m a t można znaleźć w punkcie „Sortowanie i filtrow anie” w dalszej części tego rozdziału.

Znacznik tabeli danych— h:dataTable Znacznik h: dataTabl e iteracyjnie przeszukuje zdefiniowane przez nas dane celem utworzenia tabeli języka H TM L. Przykład użycia tego znacznika przedstawiono poniżej: < % — komponenty w lewej kolumnie

—%>



9

być może rue ok

nie ok tak * być może me ok

|

Komponenty stosowane w tabeli nie różnią się od komponentów umieszczanych poza tabelą — możemy na nich operować w dowolny sposób — możliwa jest więc wizualizacja warun­ kowa za pośrednictwem atrybutu rendered, obsługa zdarzeń itp.

sun com /jsf/cone"

p re fix = "f"

Lag 1 1D u n = "h t ip / / ja v a .su n.com /jsf/htm l"

ta g lib u ri ="iu tp ./ / ja v a

p refix = "h "



outputText value="#{msgs w in d o w T itle }"/>



13. 14 15. 16. 17. 18. 19.

20. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32.

%> %>



10.

21

Ponieważ znacznik h:dataTable iteracyjnie przeszukuje dane, na podstawie tabeli przed­ stawionej na rysunku 5.5 zostanie utworzona lista liczb całkowitych. Bieżącą wartość całkowitoliczbową wykorzystujemy w procesie konfigurowania komponentów w kolumnach Liczba , Pola tekstowe, Przyciski oraz Menu.

ch tm l>

11. 12.

tak 1 4



3

Lis ty

B1I1I1B

;3..... :j

5

ciice3 gil

Listing 5.8. Kod strony components/web/index jsp

■_ .

Liczba Pola tekstowo Przyciski Pola-wyboru Lacza G ra fik a M en u Przyciski opcji

1

D

| D d":e4 D dices

Do tej pory w kolumnach tabel umieszczaliśmy wyłącznie komponenty wyjściowe, co nie oznacza, że nie możemy w komórkach stosować dowolnych komponentów JSF . Na rysun­ ku 5.5 przedstawiono przykład aplikacji prezentującej tabelę z rozmaitymi komponentami.

Rysunek 5.5.

175





176

Rozdział 5. ■ Tabele danych

JavaServer Faces 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. 46. 47. 48. 49. 50. 52. 53. 54. 55. 56. 57. 58. 59. 60. 61. 62. 63. 64. 65. 66. 67. 68. 69. 70. 71. 72. 73. 74. 75.

180

JavaServer Faces 30. 31. 32. 33. 34. 35. 36. 37 38. 39. 40. 41. 42. 43. 44. 45



Rysunek 5.9.

£§P Stosowanie stylów dla kolumn • Moziüa Firefox

Przykład zastosowania stylów dla wierszy

Plik

Edycja

Widok

Historia

Zakładki

Narzędzia

Pomoc

¡Numer zamówienia ¡Data zamówienia ¡Identyfikator klienta j Cena j

Młynek do kawy

Strona JS F przedstawiona na rysunku 5.10 wykorzystuje znacznik h: da La Table w następu­ jącej formie: 3. 4. 5. 6. 7. 8. 9. 10. 11. 12 13. 16. 1/. 18. 19. 20 21. 22. 23 24. 31

32. 33. 34. 35 36. 37. 38. 39 40 41. 42. 43. 44. 45. 46. 47. 48 49. 50. 51. 52. 53. 54. 55. 56.

Listing 5.14. Kod komponentu database/sre/java/com/corejsf/CustomerBean.java 1 package com.corejsf; 2 3 import java.sql.Connection; 4 import java.sql.ResultSet; 5 import java.sql.SQLException; 6 import java.sql.Statement; 7 import javax.naming.Context; 8 import javax.naming.InitialContext; 9 import javax.naming.NamingException; 10 import javax.servlet.jsp.jstl sql.Result; 11 import javax.servlet.jsp.jstl sql.ResultSupport; 12 import javax.sql.DataSource; 13 14 public class CustomerBean { private Connection conn; 15 16 public void openO throws SQLException, NamingException { 17 i f (conn != null) return; 18 Context ctx = new InitialContext(); 19 DataSource ds = (DataSource) ctx.lookup("java:comp/env/jdbc/mydb"); 20 conn = ds,getConnection(); 21 22

23 24 25 26 27 28 29

public Result getAllO throws SQLException, Nami ngExcepti on { try { openO; Statement stmt = conn.createStatementO; ResultSet result = stmt.executeQuery("SELECT * FROM Customers" return ResultSupport.toResult(result);

185

186

Rozdział 5. ■ Tabele danych

JavaServer Faces 30. 31 32 33. 34. 35. 36 37. 38 39 40. }

} finally { close(), } } public void closeO throws SQLException { i f (conn == null) return; conn.closeO; conn = nul1, }

LiStfng 5.15. Zawartość pliku konfiguracyjnego database/web/WEB-INF/web.xml 1. 2. 7. Faces Servlet 8. 9. javax.faces.webapp.FacesServlet 10. l 11. 12 13. 14. Faces Servlet 15 * faces 16. 17. 18. 19. i ndex.html 20 21. 22. 23. jdbc/mydb 24. javax.sql.DataSource 25. Container 26. 27.

Listing 5.16. Pakiet komunikatów zdefiniowany w pliku database/src/java/com/corejsf/messages.properties 1. 2. 3. 4 5. 6. 7. 8.

pageTitle=Prezentacja tabel bazy danych customerIdHeader=Identyfikator klienta nameHeader=Imię i nazwisko phoneHeader=Numer telefonu addressHeader=Adres cityHeader=Miasto stateHeader=Województwo refreshFromDB=0dczytaj z bazy danych

187

Obiekty wyników biblioteki JSTL kontra zbiory wynikowe Atrybutowi value znacznika h:dataTable można przypisać między innymi egzemplarz kla­ sy javax. serví e t . jsp. js t l .Result lub egzemplarz klasy java. sql .ResultSet (jak w przy­ kładzie przedstawionym w poprzednim podrozdziale). Znacznik h:dataTable opakowuje te obiekty odpowiednio w ramach egzemplarzy klas Resul tDataModel i Resul tSetDataModel. Warto się więc zastanowić, czym się różnią wymienione modele i który model powinniśmy wybrać. Każdy, kto kiedykolwiek wykorzystywał w swoim oprogramowaniu zbiory wynikowe, do­ skonale wie, że mają one postać wysoce zmiennych obiektów wymagających od programi­ sty skutecznych mechanizmów kontrolnych. Klasa Result biblioteki JS T L jest komponentem opakowującym zbiór wynikowy i implementującym niezbędne mechanizmy kontrolne. Oznacza to, że operowanie na danych reprezentowanych za pomocą tej klasy jest prostsze niż w przy­ padku tradycyjnych zbiorów wynikowych. Z drugiej strony, opakowywanie zbiorów wynikowych wiąże się z pewnymi kosztami w y­ nikającymi choćby z konieczności konstruowania obiektów klasy Result, co może prowadzić do istotnego spowolnienia pracy aplikacji. W aplikacji omówionej w podrozdziale „Tabele bazy danych” zdecydowaliśmy się na roz­ wiązanie polegające na zwracaniu przez metodę Custonier Bean .a ll właśnie obiektu klasy Result biblioteki JS T L .

Modele tabel Jeśli do reprezentowania danych w tabeli wykorzystujemy obiekt Javy, tablicę, listę, zbiór wynikowy lub obiekt wyników biblioteki JS T L , znacznik h:dataTable opakowuje te kon­ strukcje w ramach modelu rozszerzającego klasę javax. faces .model. DataModel. Wszystkie wymienione poniżej klasy modeli należą do pakietu javax. faces. model: ■ ArrayDataModel ■ ListDataModel ■ ResultDataModel ■ ResultSetDataModel ■ ScalarDataModel Znacznik h: dataTabl e zawsze wykorzystuje wymienione powyżej modele — nigdy nie uzy­ skuje bezpośredniego dostępu do obiektu (tablicy, listy itd.) wskazanego za pomocą atrybutu value. Okazuje się jednak, że sami możemy uzyskiwać dostęp do tych obiektów za pośred­ nictwem metody DataModel .getWrappedData. Wspomniana metoda jest szczególnie wygodna, jeśli chcemy dodawać i usuwać wiersze tabeli.

188

Rozdział 5. ■ Tabele danych

JavaServerFaces

189

int i=0; for (Name name : currentNames) { i f (Iname.isMarkedForDeletionO) { newNames[ i ++] = name;

Edycja modeli tabel Dodawanie i usuwanie wierszy tabeli jest bardzo proste, ponieważ mamy do dyspozycji dwie metody implementowane przez wszystkie modele danych: getWrappedData( ) i setWrapped ^D ataO . Przeanalizujmy teraz faktyczne działanie tego mechanizmu w aplikacji umożli­ wiającej użytkownikom usuwanie wierszy z tabeli (patrz rysunek 5.12).

} } model.setWrappedData(newNames); return nul 1;

} }

J p Usuwanie wierszy tabel» - MoziiU» firefa*

Rysunek 5.12. Usuwanie wierszy tabeli

j Piik

Edycja

Widok s £i?tow#-; Zakładki

Narzędzia .. P 12) return false, 58. i f (m == 2) { 59. i f (isLeapYear(y)) return d

20 22 23





listing 7.7. Kod strony rushmore/web/lincoln.jsp 1 2 3 4

Lag 1ib uri =' h t t p :/ / j a v a sun com/jsf/core" p r e f i x - ' T %> t a g l i D uri= 'hL Lp //java Sun com/jsf/html" p r e f i x - ' h ' 1 %>

5



57. 8 9.





Alternatywnym rozwiązaniem byłoby użycie znacznika f: val ueChangeLi stener w ramach następującej konstrukcji: < h : s e l ec tO ne M en u v a l u e = " # { f o r m . c o u n t r y } " o n c h a n g e = " s u b m i t ( ) " >

< f •s e l e c t I t e m s

v a l u e = "# {fo rm . countryNam es}"/>





Klasy nasłuchujące akcji muszą implementować interfejs ActionListener definiujący metodę processAction. Oznacza to, że dla powyższego fragmentu kodu implementacja JS F wywoła metodę RushmoreLi stener .processAction bezpośrednio po aktywacji (kliknięciu) przycisku z obrazem. Stosując wiele znaczników f: actionLi stener lub f: val ueChangeLi stener, możemy zdefi­ niować wiele klas nasłuchujących dla pojedynczego komponentu interfejsu użytkownika. Możemy na przykład dodać jeszcze jedną metodę nasłuchującą akcji do zdefiniowanego wcześniej przycisku polecenia:



Prezentowane znaczniki mają jedną zasadniczą przewagę nas stosowanymi wcześniej atry­ butami — um ożliwiają nam wiązanie pojedynczego komponentu z wieloma metodami nasłuchującymi. Warto zwrócić uwagę na różnicę dzielącą wartość przypisaną atrybutowi val ueChangeLi stener od wartości przypisanej atrybutowi value znacznika f : val ueChangeLi stener w powyższym kodzie. W pierwszym przypadku mieliśmy do czynienia z wyrażeniem reprezentującym metodę, w drugim przypadku odwołujemy się do klasy Javy. Klasę wskazaną w powyższym fragmencie kodu można by zaimplementować w następujący sposób: Count r y L i s tener implements ValueChangeListener { p r i v a t e s t a t i c f in a l S t r in g US - " S t a n y Z j e d n o c z o n e " .

p u b lic

Zamiast wskazywać metody nasłuchujące zdarzeń akcji za pośrednictwem atrybutu action ^ L is te n e r, możemy użyć znacznika f:actio n Listen er:

cla ss



W powyższym fragmencie kodu klasa ActionLogger pełni funkcję prostego mechanizmu nasłuchiwania akcji, którego funkcjonowanie ogranicza się do rejestrowania zdarzeń akcji. Jeśli dla pojedynczego komponentu zdefiniujemy wiele klas nasłuchujących (jak w powyższym fragmencie kodu), odpowiednie metody będą wywoływane w następującej kolejności: ■ metoda nasłuchująca wskazana za pośrednictwem atrybutu; ■ metody klas nasłuchujących wskazanych za pośrednictwem znaczników (w kolejności deklaracji).

public void processValueChange(ValueChangeEvent event) { FacesContext if

context

= FacesContext

( U S . e q u a 1s ( ( S t r i n g ) context

g e tC u rre n tIn s ta n c e d .

e v e n t . g e t N e w V a 1u e ( ) ) )

g etV iew R o o t( )

s e tL o ca le (L o ca le .u S ),

else c o n te x t.g e tV ie w R o o t( ) .s e tL o c a le (L o c a le .C A N A D A ).

Podobnie jak wszystkie obiekty nasłuchujące wskazywane w ramach znacznika f : valueChange ^Listener, także powyższa klasa musi implementować interfejs Val ueChangeLi stener. Nasza klasa definiuje tylko jedną metodę: void processValueChange(ValueChangeEvent). Znacznik f : actionL i stener jest odpowiednikiem znacznika f : val ueChangeLi stener stoso­ wanym dla mechanizmów nasłuchiwania akcji. Za pośrednictwem atrybutu type tego znacz­ nika należy zdefiniować nazwę klasy implementującej interfejs ActioriLi stener. Poniżej przedstawiono nową wersję definicji przycisku z listingu 7.6:

W ielu program istów nie rozumie, dlaczego za pośrednictwem atrybutów acti on ^ L i stener i vał ueChangeLi stener należy bezpośrednio wskazywać m etody na­ słuchujące, podczas gdy znaczniki f: acti onLi stener i f wal ueChangeLi stener wska­ zuj ą na klasy obejm ujące tego rodzaju metody. Okazuje się, że ta oczywista niezgodność atrybutów i znaczników m echanizm ów nasłuchiwania wynika z-przeoczenia grupy eksperckiej pracującej nad standardem JSF.

" I

Komponenty bezpośrednie W podrozdziale „Zdarzenia cyklu życia” we wcześniejszej części tego rozdziału wspomniano, że zdarzenia zmiany wartości zwykle są wywoływane po fazie weryfikacji, natomiast zda­ rzenia akcji z reguły są wywoływane po fazie wywołania aplikacji. W większości przypadków

266

Rozdział 7. ■ Obsługa zdarzeń

JavaServer Faces takie działanie jest jak najbardziej pożądane. Zwykle chcemy być informowani o zmianach wartości tylko wtedy, gdy nowe wartości są poprawne, natomiast wywołań akcji oczekujemy po przekazaniu wszystkich nadesłanych wartości do modelu.

Rysunek 7.7. Przykład niechcianej weryfikacji

ł|§/ Przykład wykorzystania zdarzeń zmiany wartośd - Mozilł^ F?relbx Plik

fcaytja

Wictok

Historia

Zakładki

Narzędzia

%

267

.

Pomoc

* # Q S rij-P ' Proszę wpisać adres

Zdarza się jednak, że generowania zdarzeń zmiany wartości lub akcji oczekiwalibyśmy ra­ czej na początku cyklu życia, aby pominąć fazę weryfikacji danych udostępnianych przez jeden lub wiele komponentów interfejsu użytkownika. Potencjalne uzasadnienie takiego podejścia można znaleźć w punkcie „Stosowanie bezpośrednich komponentów wejściowych" (w dalszej części tego podrozdziału) oraz w punkcie „Pomijanie procesu weryfikacji" (w roz­ dziale 6.). W pierwszej kolejności skoncentrujemy się na analizie mechanizmu umożliwia­ jącego dostarczanie zdarzeń bezpośrednich (patrz rysunek 7.6).

:

Akcje bezpośrednie: 1. Sygnał d ia m e to d nasłuchujących a kcji w yyw w oołanie ła n ie a kcji 2.i W

i Bezpośrednie p o la wejściowe: I 1. Konwersja i w eryfikacja zrm an w artości I 2. Sygnał d la m e to d nnasłuchujących asłuchuj

Przywrócenie 1 w id oku f

I

Zakończono odp o w ie dź

Zakończono o dpow iedź

......-ł>

r............

Stoso w anie w artości żądania

(

\

j j

Weryfikacja

W izualizacja o dp o w ie dzi

Zakończono odp o w ie dź

.....

Za kończono o dp o w ie dź

W yw o ła n ie aplikacji

Wizualizacja odp ow ie dzi

Aktualizacja wartości m o d elu

Błędy konw ersji/w izua liza cja odpow iedzi Błędy w e ryfika cji lu b konw ersji/w izua liza cja o dp o w ie dzi

Rysunek 7.6. Komponenty bezpośrednie Zdarzenia bezpośrednie są generowane po fazie stosowania wartości żądania. W przypadku komponentów wejściowych procesy konwersji i weryfikacji (poprzedzające generowanie zdarzeń zmiany wartości) są realizowane bezpośrednio po fazie stosowania wartości żąda­ nia. W przypadku komponentów poleceń wywołania metod nasłuchujących akcji następują przed wywołaniam i samych akcji, a cały ten proces decyduje o działaniu mechanizmu odpowiedzialnego za obsługę nawigacji i eliminuje dalsze fazy cyklu życia aż do fazy wizualizacji odpowiedzi.

Stosowanie bezpośrednich komponentów wejściowych Na rysunku 7.7 przedstawiono przykład obsługi zdarzeń zmiany wartości zaczerpnięty z pod­ rozdziału „Zdarzenia zmiany wartości” . Warto przypomnieć, że prezentowana aplikacja wykorzystuje metodę nasłuchiwania zmian wartości do modyfikowania ustawień regionalnych widoku, które z kolei decydują o opisie jednego z pól tekstowych formularza.

Adres

. ••

.

-o

Miasta i Stan Kraj

Stam, żiecmocrone [_^j

vVysti) aflres j

Wydaje się, że wprowadzona zmiana jest zupełnie nieszkodliwa z punktu widzenia prezento­ wanej aplikacji — do pola wejściowego Adres dodaliśmy mechanizm weryfikujący, nato­ miast do całego formularza dodaliśmy znacznik komunikatu. Takie rozwiązanie oznacza jednak, że wybór kraju przed wypełnieniem pola Adres doprowadzi do błędu procesu weryfi­ kacji (menu Kraj wysyła bowiem cały formularz bezpośrednio po zmianie jego wartości). Można ten problem sformułować w następujący sposób: chcemy weryfikować dane w sytuacji, gdy użytkownik aktywuje przycisk akceptacji formularza, ale też chcemy ten proces wyeliminować w sytuacji, gdy użytkownik zmienia ustawienia w menu kraju. Jak dopro­ wadzić do stosowania mechanizmu weryfikacji w jednym przypadku i zrezygnować z jego stosowania w innym? Rozwiązaniem tego problemu jest przekształcenie menu Kraj w komponent bezpośredni (ang. immediate component). Bezpośrednie komponenty wejściowe same realizują zadania związane z konwersją i weryfikacją, po czym dostarczają zdarzenia zmian wartości na po­ czątku cyklu życia JS F (bezpośrednio po fazie stosowania wartości żądania, zamiast po fazie weryfikacji). Komponenty bezpośrednie należy definiować za pomocą atrybutu immediate, który może być stosowany dla wszystkich komponentów wejściowych i komponentów poleceń:

Jeśli przypiszemy atrybutowi immediate wartość true, nasze menu będzie generowało zda­ rzenia zmian wartości bezpośrednio po fazie stosowania wartości żądania, a więc na długo przed weryfikacją danych komponentów wejściowych. Część Czytelników zapewne zasta­ nawia się, dlaczego późniejsza weryfikacja miałaby być lepsza od wcześniejszej, skoro nie ma wątpliwości, że weryfikacja i tak zostanie przeprowadzona, a odpowiedni błąd wyświe­ tlony. Aby zapobiec weryfikacji danych wejściowych pozostałych komponentów danego formularza, musimy podjąć jeszcze jeden ważny krok polegający na wywołaniu metody renderResponse kontekstu stron na końcu metody nasłuchującej zmian wartości: private static final String US = "Stany Zjednoczone"; public void countryChanged(ValueChangeEvent event) {

268

JavaServer Faces FacesContext context = FacesContext g e i C u r r e m I n s t a n c e ( ). i f (US e q u a l s ü S t n n g ) event getNewValue( ) )) context getvlewRooL( ) . s e t L o c a l e ( l o c a le US), e is e context getViewRoot( ) . s e t L o c a l e ( L o c a l e CANADA),

context. renderResponse():

1

Rozdział 7. ■ Obsługa zdarzeń

269

■4ahaiBM~

Rysunek 7.8.

Zmiana ustawień regionalnych za pośrednictwem łączy

Plik

tdycja

Widok

tttstona

Zakładki

Naizjctoa

Pomoc

ii

.: http://localhost:8080/flags/index.f j ▼ j j£ ł Ijc IR jF t

,4>ł

¡5 -* »

in r t ili j!

j Proszę wpisać następujące dane osobowe f

Inuę

1 Hasło jj Proszę napisać coś o sobie

Wywołanie metody renderResponsef ) powoduje pominięcie pozostałych faz cykJu życia (łącznie z fazą weryfikacji danych dostarczonych przez pozostałe komponenty wejściowe) z wyjątkiem fazy wizualizacji odpowiedzi. Za jej pomocą możemy pominąć proces weryfi­ kacji bez konieczności rezygnacji z normalnej wizualizacji (w tym przypadku polegającej na ponownym wyświetleniu bieżącej strony). Podsumowując, możemy pominąć proces weryfikacji danych wejściowych (w razie wyge­ nerowania zdarzenia zmiany wartości) w następujący sposób: 1.

Dodając atrybut i mmedi ate do znacznika komponentu wejściowego.

2. Wywołując metodę FacesContext. renderResponse( ) na końcu metody nasłuchującej. Przy okazji analizy tego przykładu warto wspomnieć o jeszcze jednym aspekcie. Łatwo za­ uważyć, że do naszego znacznika h:selectOneMenu dodano atrybut onchange wskazujący na funkcję submit( ). Takie ustawienie wspomnianego atrybutu oznacza, że za każdym razem, gdy użytkownik zmieni opcję zaznaczoną w danym menu, zostanie wywołana funkcja submit języka JavaScript, która spowoduje wysłanie na serwer całego formularza. Opisane rozwiązanie jest kluczowe, ponieważ implementacja JavaServer Faces obsługu­ je wszystkie zdarzenia po stronie serwera. Gdybyśmy usunęli atrybut onchange ze znacz­ nika h : sei ectOneMeriu, formularz nie zostałby wysłany na serwer w reakcji na zmianę opcji w menu Kraj , co wykluczyłoby możliwość wywołania cyklu życia JS F i — tym samym — wywołania naszej metody nasłuchującej zmian wartości (w tym przypadku odpowiedzialnej za dostosowanie ustawień regionalnych). To, że w technologii JS F wszystkie zdarzenia są obsługiwane po stronie serwera, wielu programistom wydaje się dość dziwne. Z drugiej strony, powinniśmy pamiętać, że istnieje możliwość obsługi zdarzeń także po stronie klienta za pośrednictwem skryptów JavaScript dołączanych do komponentów (z wykorzystaniem takich atrybutów jak onbl ur, onfocus czy onclick). Co więcej, dodanie mechanizmów obsługi zdarzeń po stronie klienta jest planowane już w kolejnej wersji technologii JSF .

Stosowanie bezpośrednich komponentów poleceń W rozdziale 4. omówiono aplikację oferującą możliwość zmiany ustawień regionalnych za pomocą łączy poleceń (patrz rysunek 7.8).

1

i

11 Zakończono

11

Jeśli do jednego z pól wejściowych tego formularza dodamy mechanizm weryfikujący czy została do niego wprowadzona jakakolwiek wartość, będziemy się musieli zmierzyć z tym samym problemem, o którym była mowa w punkcie „Stosowanie bezpośrednich kompo­ nentów wejściowych” . Próba zmiany ustawień regionalnych przez kliknięcie łącza (w tym przypadku flagi) spowoduje wygenerowanie błędu weryfikacji. Tym razem jednak będzie­ my potrzebowali bezpośredniego komponentu polecenia (zamiast — jak wcześniej — bez­ pośredniego komponentu wejściowego). W tym celu wystarczy dodać do istniejącego znacznika h: commandLi nk atrybut immediate:

Listing 7.15. Kod komponentu phase-tracker/src/java/com/corejsf/FormBeanJava 1 package com.corejsf, 3 import javax.faces.FactoryFinder; 4 import javax.faces.event.PhaseEvent, 5. import javax.faces.event.PhaseListener: 6 import javax.faces.event.ValueChangeEvent, 7. import javax.faces.lifecycle.Lifecycle; 8 . import javax.faces.lifecycle.LifecycleFactory. 9 import javax.faces.model.Selectltem; 10

11. public class FormBean { TO -------1— _-i __i ti 12 private Selectltem[] phases = { 13. new Selectltem("RESTORE_VIEW"), 14. new Selectltem ("APPLYJREQUESTJ/ALUES"), 15 new Selectltem ("PROCESS_VALIDATIONSM), 16 new Selectltem("UPDATE_MODEL_VALUES") , 17. new Selectltem (" INVOKE_APPLICATION"), 18. new Selectltem("RENDER_RESPONSE"), 19 new SelectItem("ANY_PHASE"), 20 },

21. 23 24. 25.

public SelectltemE] getPhasesO {

FactoryFinder LIFECYCLE_FACTORY), Lifecycle lifecycle = factory.getLifecycle(LifecycleFactory. DEFAULT_LIFECYCLE); PhaseListener[] listeners = lifecycle.getPhaseListenersO: for (int i = 0. i < listeners length; i++) { PhaseListener listener = 1isteners[i]; i f (listener instanceof com.corejsf.PhaseTracker) ((com.corejsf PhaseTracker) 1istener).setPhase( (String) e.getNewValue()); } } public void afterPhase(PhaseEvent event) { System.out.printlnC'PO FAZIE " + showEvent(event)), } public void beforePhase(PhaseEvent event) { System.out.printIn("PRZED FAZĄ " + showEvent(event)); } private String showEvent(PhaseEvent event) { return "Zdarzenie fazy. " + event.getPhaseId(); }

Listing 7.16. Kod klasy phase-tracker/src/java/com/corejsf/PhaseTracker.java___________________ 1. package com.corejsf;

2. 3 import java.util.logging.Logger; 4. import javax.faces.context.FacesContext, 5. import javax.faces.event.PhaseEvent, 6. import javax.faces.event.PhaseListener; 7. import javax.faces.event.Phaseld;

8

2.

22

26 27 28. 29 30 31 32 33. 34 35. 36 37. 38 39. 40. 41 42. 43. 44 45. 46 47 }

return phases; }

public void phaseChange(ValueChangeEvent e) {

LifecycleFactory factory = (LifecycleFactory) FactoryFinder.getFactory(

9. public class PhaseTracker implements PhaseListener { 10. private static final String PHASE_PARAMETER ="com.corejsf.phaseTracker phase"; 11 private sta tic final Logger logger = Logger.getLoggerC'com.corejsf.phases"); 12. private s ta tic String phase = null; 13. 14. public void setPhase(String newValue) { phase = newValue, } 15. 16. 17. 18. 19. 20 21. 22. 23. 24. 25 26. 27. 28. 29. 30. 31.

public Phaseld getPhaseldO { i f (phase ==n u ll) { FacesContext context = FacesContext.getCurrentInstance(), phase = (String) context.getExternalContextO.getInitParameter( PHASE_PARAMETER);

} Phaseld phaseld = Phaseld.ANY_PHASE; i f (phase != null) { i f ("RESTOREJ/IEW" equals(phase)) phaseld =Phaseld.RESTOREJ/IEW; else i f ("APPLY_REQUEST_VALUES".equals(phase)) phaseld =Phaseld.APPLY_REQUEST_VALUES; else i f ( " PROCESSJ/ALIDATI0NS".equals(phase)) phaseld =Phaseld.PROCESSJ/ALIDATI0NS. else i f ( "UPDATE_M0DEL_VALUES".equals(phase)) t

-j

m — ..— t

iin n A T r

M nnn

\/a l l IC C

279

280

JavaServer Faces 33 34. 35 36 37 38. 39. 40. 41. 42. 43. 44. 45. 46 47 48.

Rozdział 7. ■ Obsługa zdarzeń else i f ( " INV0KE_APPLICATI0N" .equals(phase)) phaseld =Phaseld. INV0KE_APPLICATI0N. else i f ("RENDER_RESPONSE" equals(phase)) phaseld =Phaseld.RENDER_RESPONSE. else i f ( "ANY_PHASE".equals(phase)) phaseld =Phaseld.ANY_PHASE;

} return phaseld; } public void beforePhase(PhaseEvent e) { logger infoC'PRZED " + e.getPhaseId()), } public void afterPhase(PhaseEvent e) { logger.infoC'PO "+ e.getPhaseId()); } }

Listing 7.17. Plik konfiguracyjny phase-tracker/web/WEB-\NF/faces-config.xml

5. 6. 7.

vertical-align, top, font-style: ita lic ; font-size l.lem;

8- } 9

10 .

.columns { vertical-align: top;

1 1 .}

Podsumowanie całego materiału w jednym miejscu Bodaj najlepszym zakończeniem tego rozdziału będzie analiza przykładu prostej implementa­ cji panelu podzielonego na zakładki. Nasza aplikacja demonstruje zarówno obsługę zdarzeń, jak i zaawansowane aspekty stosowania znaczników JS F języka H TM L. Wykorzystamy następujące techniki:

1 . 2 . com.corejsf messages msgs

■ stosowania facet;

form com.corejsf FormBean session

■ statycznego dołączania stron JSF.

4. 5. 6. 7

8 9

10 11. 12 13. 14 15 16. 17 18. 19.

20 2 1. 22 23.

com.corejsf PhaseTracker

Listing 7.18. Plik właściwości phase-tracker/src/java/com/corejsf/messages.properties 1. indexWindowTitle=Zdarzenia fazy

■ definiowania indeksów zakładek; ■ dodawania podpowiedzi do komponentów za pośrednictwem atrybutu t i t l e; ■ dynamicznego określania klas stylów; ■ stosowania metod nasłuchujących akcji; ■ wizualizacj i opcj onalnej;

Technologia JS F 1.2 nie oferuje gotowego komponentu panelu podzielonego na zakładki, stąd przed opracowaniem odpowiedniej strony musimy się zdecydować na jedno z dwóch rozwią­ zań: implementację komponentu niestandardowego lub użycie znaczników już istniejących (łącznie z komponentem wspomagającym). Na rysunku 7.11 przedstawiono efekt zastoso­ wania drugiego z tych rozwiązań. Analizę pierwszego rozwiązania można znaleźć w pod­ rozdziale „Stosowanie komponentów i facet potomnych” w rozdziale 9. Przedstawiony na rysunku 7.11 panel podzielony na zakładki zaimplementowano wyłącznie za pomocą istniejących znaczników JS F języka H T M L oraz komponentu wspomagającego — nie korzystaliśmy z żadnych niestandardowych mechanizmów wizualizacji ani własnych komponentów. Kod strony JS F obejmującej ten panel przedstawiono poniżej:

2 . phasePrompt=Wybierz fazę3. submitPrompt=Wyślij formularz

Listing 7.19. Arkusz stylów phase-tracker/web/styIe s.css 1 . body { 2 background: #eee; 3- } 4. .phaseFormColumns {

281

5. 7. 8.

Listing 7.22. Kod strony tabbedpane/web/roosevelt.jsp______________________________ 1. 3. 4. 5. 7. 8 .

Listing 7.23. Kod strony tabbedpane/web/lincoln.jsp________________________________ 1. 3.

4. 5. 7. 8 .

285

286 Listing 7.24.

Rozdział 7. ■ Obsługa zdarzeń

JavaServer Faces Kod strony

tabbedpan

1. 3. 4 5. 7. 8

Listing 7.25. Plik właściwości tabbedpane/src/java/com/corejsf'/messages.properties windowTitle=Przykład panelu podzielonego na zakładki lincolnTooltip=Abraham Lincoln 1incolnTabText=Abraham Lincoln lincolnDiscussion=Prezydent Lincoln przeszedł do historii jako Wielki \ Emancypator, ponieważ w ogromnym stopniu przyczynił się do zniesienia \ niewolnictwa na terenie Stanów Zjednoczonych. Urodzony w 1809 roku wychował \ się w biednej rodzinie w stanie Kentucky. Na urząd prezydenta został wybrany \ w roku 1860. Zginął w zamachu w 1865 roku z rąk Johna Wilkesa Bootha. 10

washi ngtoriTooltip=George Washi ngton

1 1 . washi ngtoriTabText=George Washi ngton 12 washingtoriDiscussion=George Washington był pierwszym prezydentem Stanów \

13. Zjednoczonych Urodził się w roku 1732 w stanie Wirginia. W roku 1775 został \ 14. mianowany naczelnym wodzem Armii Kontynentalnej. Sześć lat później \ 15. zmusił do złożenia kapitulacji Charlesa Cornwallisa pod Yorktown Urząd \ 16. prezydenta Stanów Zjednoczonych objął 30 kwietnia 1789 roku. 17. 18 rooseveltToolti p=Theodore Roosevelt 19 rooseveltTabText=Theodore Roosevelt 20 rooseveltDiscussion=Theodore Roosevelt był 26. prezydentem Stanów Zjednoczonych. \ 21. Został prezydentem w roku 1901 po zamachu na prezydenta McKinley’a. Urząd \ 22. prezydenta objął w wieku 42 lat i przeszedł do historii jako najmłodszy \ 23. prezydent w historii Stanów Zjednoczonych. 24. 25 jeffersonTooltip=Thomas Jefferson 26. jeffersonTabText=Thomas Jefferson 27. jeffersonDiscussion=Thomas Jefferson, trzeci prezydent Stanów Zjednoczonych, \ 28. urodził się w roku 1743 w stanie Wirginia. Jefferson był znany z doskonałych \ 29. przemówień. W roku 1785 objął urząd ministra ds. Lrancji, na którym zastąpił \ 30. Benjamina Lranklina. W roku 1796 Jefferson niechętnie zgodził się kandydować \ 31. na urząd prezydenta, ale przegrał wybory zaledwie trzema głosami. Prezydentem \ 32. Stanów Zjednoczonych został w 1801 roku i pełnił ten urząd przez 8 lat.

Listing 7.26, Plik arkusza stylów tabbedpane/web/styles.css 1 body { 2. background- #eee; 3 } 4. .tabbedPaneHeader { 5. vertical-align: top, 6. text-align: left; 7 padding. 2px 2px Opx 2px,

9. tabbedPaneText { 10. font-size: l.Oem, 11. font-style- regular, 12 padding 3px; 13 border thin solid CornflowerBlue, 14 } 15. tabbedPaneTextSelected { 16 font-size l.Oem, 17 font-style. regular. 18. padding 3px; 19. background. PowderBlue; 20 border thin solid CornflowerBlue, 21

}

22 23 24 25. 26 27. 28. 29 30 31. 32 33. 34. 35. 36. 37. 38. 39 40. 41. 42. 43.

.tabbedPane { vertical-align: top, text-align- left; padding lOpx, } .displayPanel { vertical-align: top; text-align left; padding. 10px; } .tabbedPaneContent { width- 100%; height. 100%, font-style ita lic ; vertical-align top; text-align. left; font-size- 1 . 2m; } .presidentDiscussionColumn { vertical-align- top, text-align: left; }

Listing 7.27. Plik konfiguracyjny tabbedpane/web/WEB-iNF/faces-config.xmi 1 . 2 com.corejsf messages msgs

13. 14. 15. tp 16. coni.corejsf.TabbedPane 17. session 18. 19.

287

288

JavaServer Faces

Rozdział 7. ■ Obsługa zdarzeń

Listing 7.28. Kod klasy tabbedpane/src/java/com/corejsf/TabbedPane.java 1 . package com.corejsf; 2. 3. import javax.faces.event.ActionEvent; 4. 5. public class TabbedPane { 6. private int index; 7. private static final int JEFFERSON_INDEX = 0; 8 private static final int ROOSEVELT_INDEX = 1; 9. private static final int LINCOLN_INDEX = 2; 10. private static final int WASHINGT0N_INDEX = 3, 11 .

12. 13. 14. 15. 16. 17. 18. 19. 20 2 1. 22 . 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40 . 41. 42. 43. 44. 45. 46. 47. 48. 49. 50. 51. 52. 53. 54. 55.

private String[] tabs = { "jeffersonTabText", "rooseveltTabText", "lincolnTabText", "washingtonTabText", }; private String[] tabTooltips = { "jeffersonTooltip", "rooseveltTooltip", "lincolnTooltip", "washingtonTooltip" }; public TabbedPaneO { index = JEFFERSON_INDEX;

} I I metody nasłuchujące akcji odpowiedzialne za ustawianie bieżącej zakładki public public public public

void void void void

jeffersonAction(ActionEvent e) { index =JEFFERSON_INDEX, } rooseveltAction(ActionEvent e) { index =R00SEVELT_INDEX; } lincolnAction(ActioriEvent e) { index =LINC0LN_INDEX; } washingtonAction(ActionEvent e) { index =WASHINGT0N_INDEX; }

// style CSS public public public public

String String String String

getJeffersonStyleO { return getCSS(JEFFERSON_INDEX); } getRooseveltStyle() { return getCSS(R00SEVELT_INDEX); } getLincolnStyle() { return getCSS( LINC0LN_INDEX); } getWashingtonStyleO { return getCSS(WASHINGT0N_INDEX); }

private String getCSS(int forlndex) { return forlndex == index ? "tabbedPaneTextSelected" : "tabbedPaneText"; }

I I metody określające bieżącą zakładkę public public public public

boolean boolean boolean boolean

isJeffersonCurrentO { return index == JEFFERSON_INDEX; } isRooseveltCurrentO { return index == ROOSEVELTJ NDEX; } isLincolnCurrentO { return index == LINCOLN_INDEX; } isWashingtonCurrentO { return index ==WASHINGT0N_INDEX; }

U metody zwracające podpowiedzi dla tytułów zakładek public String getJeffersonTooltip() { return com.corejsf.util.Messages.getString( "com.corejsf.messages", tabTooltips[J EFFERSON_INDEX], nuli); } public String getRooseveltTooltipO { return com.corejsf.util.Messages.getString( "com.corejsf.messages", tabToolti ps[ROOSEVELT_INDEX], nuli);

56 57. 58. 59. 60. 61. 62. 63. 64. 65. }

} public String getLincolnTooltipO { return com.corejsf.util.Messages.getString( "com.corejsf messages", tabTooltips[LINCOLN_INDEX], nuli); } public String getWashingtonTooltip() { return com.corejsf.util.Messages.getString( "com.corejsf messages", tabToolti ps[WASHI NGT0N_INDEX], nuli); }

289

290

JavaServer Faces

8

Podwidoki i pakiet Apache Tiles Interfejsy użytkownika z reguły cechują się największą zmiennością w procesie wytwarzania aplikacji internetowych, stąd konieczność tworzenia możliwie elastycznych i rozszerzalnych rozwiązań w tym obszarze. W niniejszym rozdziale przeanalizujemy techniki osiągania ta­ kiej elastyczności i rozszerzalności przez dołączanie do wielu stron wspólnych elementów. W pierwszej kolejności skoncentrujemy się na standardowych mechanizmach technologii JS P (konstrukcjach dołączania i importowania biblioteki JS T L ), które można z powodzeniem wykorzystywać do definiowania wspólnych elementów interfejsu w ramach aplikacji JSF . W dalszej części tego rozdziału omówimy techniki stosowania pakietu Apache Tiles (umoż­ liwiającego między innymi definiowanie zarówno właściwej treści, jak i jej rozmieszczenia) łącznie z technologią JavaServer Faces.

Typowe rozmieszczenia Wiele popularnych witryn internetowych, w tym nytimes.com, allegro.pl i amazon.com, zbu­ dowano na bazie tego samego, bardzo popularnego układu. Wszystkie wymienione witryny cechuje typowe rozmieszczenie: nagłówek, menu, treść (patrz rysunek 8.1).

Rysunek 8.1. Typowe rozmieszczenie elementów na stronie internetowej

NAGŁÓWEK

MENU

TREŚĆ

292

JavaServer Faces

Rozdział 8. ■ Podwidoki i pakiet Apache Tiles

Efekt rozmieszczenia przedstawionego na rysunku 8.1 można oczywiście osiągnąć z wyko­ rzystaniem ramek języka H TM L, jednak stosowanie ramek jest z kilku względów niepożą­ dane. Ramki powodują na przykład utrudnienia w dodawaniu stron do zbioru stron ulubio­ nych. Ramki generują też odrębne żądania, co w przypadku aplikacji internetowych może stwarzać poważne problemy. Ogólnie, lepszą techniką jest dołączanie treści, czyli rozwiązanie, którym zajmiemy się w tym rozdziale.

293

Rysunek 8.3. Biblioteka

K IŁ

\

Piotruś Pan

Przeglądarka książek i biblioteka

Rozdział 5 Rozdaał 2 Rozdaał 3.

Aby jak najlepiej zilustrować techniki implementacji rozmieszczeń (układów), dołączania wspólnych elementów treści oraz stosowania pakietu Tiles, przeanalizujemy w tym rozdziale dwie przykładowe aplikacje: przeglądarkę książek i bibliotekę. Wybrane strony tych aplikacji przedstawiono odpowiednio na rysunkach 8.2 i 8.3.

Rozdaał 4. B-ozdaał 5

Rysunek 8.2. Przeglądarka książek

s* *** » «

h««»

Rozdaał 8

Rozdaał i 1 Rozdaał U Rozdaał 14 Rozdaał i o

**•*•

m

tmp./'iocahost;9O9O/bool'-viewe>-tile>/tiO0k feces'c ła )

Znacie zapewne wszyscy Mak; Domek w parku Jest to jedyny domek na swiecie który elfy zbudowały dla luda Mało daem jednak widaało go naprawdę, może dwoje albrt najwvzei troie. i

Rozdział i I Przeglądarka k^ąże|— M

Mały domek

to musiały w mm przenocować ł'>o inaczej mo mogłyby go zolaczyć

Bo b e J v daeck-,- się J,; snu

ubada, domku me ma jeszcze wcale, a b edy się obuda z rana. w ida go jak na dłom Tylko coś mec oś tn^zna zen zobduzyc, a mianowicie światło w tego oknach, ale i to dopieio „po dzwonku

Amelcia Claie (ta. co tak chętnie dair sobie plombować zęby, bo za to biorą ją do

cubemi) widuje czasem po kilkaset świateł na raz Zapewne w tej chwik elfy budują swói domek bo teraz buduią g> każdego wieczora, ale co daen w mnei części parku Aniela się wydawało, ze ledno światełko bvło większe od innych, ale na pewno tego puwi^darc me mogła, bo tyle innych świateł migotało i przesłaniało go. ze trudno Kyło rozpoznać, czy to lest to samo Jeżeli lednak między światełkami migocącymi w Parku Leśnym iest jedno największe będzie to z pewnością latarka Piotrusia Pana Bardzo duzo dzieci widziało te svziatelka w parku, ale pierwszym dzieebern. dla którego elfy zbudowały swój domek była Toma Mamenng Toma była zawsze niezwykłą dziewczynką, a iuz wieczorem stawała się wprosi nadzwyczajną M ała juz ezteiy lata i w dzień zachowywała się iak inne dziewczynb w iej wieku Cieszyła się

Alicja w Krainie Czarów

bardzo, jeżeli jei sześcioletni braciszek Jas pozwolił się jei bawtr z sobą, naśladowała go we wszystkim i me gniewała się wrale, jeżeli w zabawie poturbował ją nawei troszkę W ogolę z Jasia była baidzo dumna, uważała go bowiem za ideał rozumu i dzielności Potrafiła także pokazać, ze

Rozdział 1.

Rozdział 1

ma nowe b u aczb na nóżkach w chwili gdy powinna bvła złapać rzuconą jei piłkę Jednym

Rozdział .2, Rozdział 3.

słowem w dzień Toma podobna była do wszystbeh małych dziewczynek

Przez króliczą norę

Rozdział 4

Ale po.d wieczór b ed y zmrok zaczynał zapadać, dziea zmieniały się najzupełniej Tęgi bajeczny

Rozdaał 5.

Alicja miała juz dość siedzenia na ławce obok siostry i próżnowania Raz czy dwa razy zerknęła do

Rozdział $ .

książki, którą czytała siostra Niestety w książce me było obrazkow amrozmow "A cózjesl warta

niespokojnie, bo im ciemniej i obiło się w pokoju tvm mocniej błyszczały oczy małej Tom Wztok

Rozdział ?

książka

jej lasmal spokojnym taiemmczym bbsbem , a oczy Jasia pełne były lęku i grozy W tabej chwili

Rozdział 9.

Alicja rozmyślała właśnie

Rozdaał 10.

niemrawą - czy warto męczyć się przy zrywaniu stokrotek po to, abv uwić z mch wianek Nagle tuz

Rozdział 11.

obok mej przebiegł Biały K rólik o rozowych ślepkach

pomyślała Alicja

w której me ma rozmow am obrazków-?"

Jas, który w dzień tak przewodził nad młodszą siostrzyczką, raz po raz oglądał się na mą

Jaś oddawał Tom naiulubieńsze swoje zabawb i.nazajutrz odbierał je z powrotem) a Toma a raczej starała się rozmyślać, ponieważ upał czynił ją bardzo senną i

Rozdział 12. 'Właściwie me było w tym mc nadzwyczajnego Alicja me dziwiła się nawet zbytnio słysząc, jak K rólik szeptał do siebie 'O rety, o rety, na pewno się spóźnię' Dopiero b edy Królik wyjął z beszonb od kamizelb zegarek, spojrzał nań i puścił się pędem w dalszą drogę. Akcja zerwała się na równe nogi Przyszło jej bowiem na myśl, ze nigdy przedtem me widziała królika w kamizelce am królika z zegarbem Płonąc z ciekawości pobiegła na przełaj przez pole za Białym Królibem i zdązyła jeszcze spostrzec, ze znikł w sporej norze pod żywopłotem Wczołgała się więc za mm do krókczej nory me myśląc o rym, jak się pózmej stamtąd wydostanie N o ra była początkowo prosta niby tunel, po czvm skręcała w dół tak nagle, ze Akcja me mogła juz się zatrzymać i runęła w orwor przypominający wylot głębobej studni Studnia była widać tak głęboka, czy może Akcja spadała tak wolno, ze miała dość czasu, aby

Obsługa aplikacji przeglądarki książek jest dość intuicyjna. Po kliknięciu łącza reprezentu­ jącego rozdział odpowiedni rozdział jest prezentowany w obszarze treści na danej stronie. Aplikacja biblioteki jest w istocie rozszerzeniem przeglądarki książek i umożliwia użytkow­ nikowi czytanie więcej niż jednej publikacji. Książki można wybierać z menu widocznego w górnej części strony internetowej. Przykład przeglądarki książek poddamy analizie w następujących punktach: ■ „Monolityczne strony JS F ";

rozejrzeć się dokoła i zastanowić nad tym, co się dalej stanie Przede wszystkim starała się dojrzeć dno studni, ale jak to zrobić w ciemnościach9 Zauważyła jedynie, ze ściany norv zapełnione bvły szafami i półkami na ksiązb Tu i ówdzie wisiały mapy i obrazb M ijając jedna z półek Akcja zdązyła zdjąć z mej słój z nabejką Marmolada pomarańczowa Niestety był on pusty Akcja me upuściła słoja, obawiając się, ze może zabić mm kogoś na dole Postawiła go po drodze na jednej z mzszych półek 'No. no

■ „Dołączanie wspólnej treści"; ■ „Prezentacja pakietu Apache Tiles"; ■ „Parametryzacja kafelków";

pomyślała - po tej przygodzie żaden upadek ze schodów me zrobi juz na mnie wrażenia

W domu zdziwią się. ze jestem taka dzielna Nawet gdybym spadła z samego wierzchołka

---

„Rozszerzanie kafelków",

294

JavaServer Faces

Rozdział 8. ■ Podwidoki i pakiet Apache Tiles

Przykład aplikacji biblioteki ilustruje zagadnienia omówione w następujących punktach:

295

Klasa Book wykorzystuje właściwość numChapters do wyznaczenia odpowiednich kluczy rozdziałów.

■ „Kafelki zagnieżdżone” ;

W implementacji klasy Book trudno się doszukać szczególnie interesujących aspektów. Kod tej klasy przedstawiono na listingu 8.3 w dalszej części rozdziału. Poniżej przedstawiono kod definiujący egzemplarz tej klasy w pliku konfiguracyjnym faces-config.xml:

■ „Kontroler kafelków” . Do analizy aplikacji przeglądarki książek przystąpimy już w kolejnym podrozdziale zatytu­ łowanym „Przeglądarka książek” . Aplikację biblioteki omówimy w podrozdziale „Biblioteka” .



P U Na potrzeby przykładów prezentowanych w tym rozdziale wykorzystamy tekst książek ■MW pt. Alicja w Krainie Czarówautorstwa Gharlesa Lutwidge’a Dodgsona oraz Przygody Piotrusia Pana autorstw a Jam esa M a tth ew B arrie'ego, które podzielono na rozdziały i przekonwertowano na form at HTML.

Przeglądarka książek Aplikacja przeglądarki książek oferuje dość ograniczoną funkcjonalność. Prezentuje zaledwie jedną książkę reprezentowaną przez komponent zarządzany, który zdefiniowano w pliku konfiguracyjnym. Wspomniany komponent nazwano book. Komponent book obejmuje następujące właściwości: ■ titleKey ■ image ■ numChapters ■ chapterKeys Właściwość titleKey reprezentuje klucz tytułu książki składowanego w pliku komunikatów. W pliku właściwości aplikacji przeglądarki książek zdefiniowano następującą parę kluczwartość: titleKey=Al icja w Krainie Czarów. Właściwość titleKey wykorzystujemy podczas wyświetlania tytułu strony:

Właściwość image reprezentuje łańcuch. Aplikacja interpretuje ten łańcuch jako adres U R L wskazujący na obraz wyświetlany w nagłówku strony:

Właściwość chapterKeys reprezentuje listę kluczy (po jednym dla każdego rozdziału) i jest dostępna tylko do odczytu. Aplikacja przeglądarki książek wypełnia obszar menu odpowied­ nimi wartościami odczytanymi z pakietu komunikatów:

296

Rozdział 8. ■ Podwidoki i pakiet Apache Tiles

JavaServer Faces



1 Q messages, properties



\

j

! Q faces-config.xml

\

’ - Q web. xml

; D styles, css j- Q chapterl.html j- Q chapterl0.html : Q chapterll.html : D chapte.rl2.html 0 cnapter2 html



Q cnapter3 html



Q chapter4 html Q coaoTerS htmi



Q chapter^ htmi



Możemy następnie wykorzystać te atrybuty w kodzie opisującym rozmieszczenie:



Użyte w powyższym fragmencie kodu atrybuty kafelków (w tym headerClass, menuClass itp.) obejmują zasięgiem tylko kafelki i jako takie nie są dostępne z poziomu kodu JS F . Aby udostępnić nasze atrybuty stronie JS F definiującej rozmieszczenie, należy użyć znacznika ti les: importAttribute. Znacznik tile s : ímportAttnbute importuje wszystkie atrybuty do zakresu wskazanego za pośrednictwem atrybutu scope. W powyższym przykładzie zaimpor­ towano atrybuty do zasięgu żądania. Możemy teraz stosować różne klasy C SS dla poszczególnych kafelków:

dodatkowo umożliwia nam im portowanie poje óego rozwiązania je s t następująca konstrukcja:

’Class" scope»"...



11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22.

311

312

JavaServer Faces

Rozdział 8. ■ Podwidoki i pakiet Apache Tiles

Biblioteka

313

Kafelki zagnieżdżone

W tym podrozdziale spróbujemy przekształcić przeglądarkę książek w bibliotekę (patrz rysunek 8.7).

Aplikacja biblioteki, którą przedstawiono na rysunku 8.7, obejmuje przeglądarkę książek. W tej sytuacji przeglądarka książek powinna być zawarta także w kafelku 1i b r a r y :

Rysunek 8.7.



Biblioteka zaimplementowa na w technologii JSF z wykorzystaniem kafelków



__ ■! Phk

laycjs *

Widok

Historia

Ćy - ¡Ęj

¿¡udacr,:,

^ j



Naaęozia

v ,vam trudn' zrozumie' przvgn L Piotrusia Pana

rn- w r laeL i 8 9. 10 11. 12 13. 14. 15 16. 17. 18. 19. 20 21. 23. 24. 25. 26.

Listing 8.15. Kod strony library/web/libraryLayout.jsp 1. 2. 3. 4_ 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15.

  

315

316

Rozdział 8. ■ Podwidoki i pakiet Apache Tiles

üavaServer Faces 8. 10. 11.

Listing 8.17. Kod klasy library/src/java/com/corejsf/Library.java 1. package com.corejsf:

2 3. import java.util 4. import javax.faces.model.Selectltem: 5. import javax.faces.event.ValueChangeEvent;

6. 7. public class Library { 8. private Map bookMap = new HashMap(), 9. private Book initialBook = null: 10. private List bookltems = null: 11. private String book = null; 12. private String selectedBook = null: 13. 14. public Library() { 15. Book peterpan = new BookO; 16. Book alicelnWonderland = new BookO, 17. 18. initialBook = peterpan; 19. 20. alicelnWonderland.setDi rectory( "books/ali celnWonderland"); 21. alicelnWonderland.setTitleKey("ali celnWonderl and"); 22 alicelnWonderland.setlmage("books/alicelnWonderland/cheshire.jpg"): 23. alicelnWonderland.setNumChapters(12); 24. 25. peterpan.setDi rectory!"books/peterpan"): 26. peterpan.setTitleKey("peterpan"); 27. peterpan.setlmage("books/peterpan/peterpan.jpg"); 28. peterpan.setNurnChapters(15); 29. 30. bookMap.putOalicelnWonderland", alicelnWonderland): 31. bookMap.put("peterpan", peterpan): 32. } 33. public void setBook(String book) { this.book = book; } 34. public String getBookO { return book; } 35. 36. public Map getBooksO { 37. return bookMap, 38. } 39. public void bookSelected(ValueChangeEvent e) { 40. selectedBook = (String) e.getNewValueO; 41. } 42. public Book getSelectedBook!) { 43. return selectedBook != null ? bookMap.get(selectedBook) : initialBook; 44. } 45. public List getBookItems0 { 46. if(bookltems == null) { 47. bookltems = new LinkedList(); 48. Iterator it = bookMap.values!) iteratorO; 49. while(it.hasNextO) { 50. Book book = it.n ex tO ;

51. 52. 53 54. 55. 56. 57. 58. 59. 60. 61.

bookltems.add(new SelectItem(book.getTitleKey(), getBookTitle(book.getTitleKeyO))): } } return bookltems; } private String getBookTitle(String key) { return com.corejsf.util.Messages. getStringC'com.corejsf.messages", key, null); } }

Listing 8.18. Kod strony library/web/bookSeiector.jsp

  

Listing 8.19. Kod kontrolera library/src/java/com/corejsf/LibraryTileController.java 1 package com.corejsf; 2

3 import java.io.IOException; 4 import javax.servlet.ServletContext; 5 import javax.servlet.ServletException; 6 import javax.servlet.http.HttpServletRequest; 7 import javax.servlet.http.HttpServletResponse; 8 import javax.servlet.http.HttpSession; 9 import org.apache.t i 1es.ComponentContext; 10 import org.apache.tiles.Controller; 11 12

13 14 15 16 17 18 19 20 21 22

23 24 25 26 27 28

public class LibraryTileController implements Controller { public void execute(ComponentContext tilesContext, HttpServletRequest request, HttpServletResponse response, ServletContext context) throws IOException, ServletException { HttpSession session = request .getSessionO; String chapter = (String) request.getParameter("chapter"); session.setAttribute("chapter", chapter == null || " " .equals(chapter) ? "chapterl" : chapter); Library library = (Library) session.getAttribute!"library"); i f (1ibrary == nul 1) { library = new Library(); session.setAttribute!"library", lib ra ry);

317

318

Rozdział 8. ■ Podwidoki i pakiet Apache Tiles

JavaServer Faces 29 } 30. 31. Book selectedBook = 1ibrary.getSelectedBook(); 32 if(selectedBook != null) { 33. session.setAttribute("book", selectedBook), 34. } 35. } 36. public void perform(ConiponentContext tilesContext, 37 . HttpServletRequest request. 38. HttpServletResponse response, 39 ServletContext context) 40 throws IOException, ServletException { 41 HttpSession session = request,getSession(): 42 execute(ti1esContext, request, response, context); 43 } 44. }

Listing 8.20. Kod klasy library/src/java/com/corejsf/Book.java 1. package com.corejsf; 2.

3. import java.útil LinkedList; 4 import java.ú til.List, 5. 6. public elass Book { 7 private String titleKey, 8. private String image; 9. private String directory; 10. private int numChapters; 11. private List chapterKeys = null;

1. 2 3. 4. "/>

W kolejnym podrozdziale omówimy sposób wykorzystywania tych nazw na poziomie metody decode komponentu datownika. U l javax. faces.component.UlComponent ■ void encodeBegm(FacesContext context) throws lOException Metoda encodeBegin jest wywoływana w fazie wizualizacji odpowiedzi cyklu życia JSF , pod warunkiem że typem mechanizmu wizualizacji danego komponentu jest nul 1 (co oznacza, że dany komponent sam odpowiada za swoją wizualizację).

GEU javax. faces *context.FacesContext ■ ResponseWr i ter get Responses i te r() Zwraca referencję do obiektu odpowiedzialnego za generowanie odpowiedzi. W razie konieczności możemy do cyklu życia JS F włączyć własny obiekt tego typu. Framework JS F domyślnie wykorzystuje obiekt zapisujący znaczniki języka HTM L. QQQ javax.faces.context.ResponseWriter ■ void start El ement (Stnng element Name. UlComponent component) Zapisuje znacznik początkowy wskazanego elementu. Za pośrednictwem parametru component narzędzia zewnętrzne mogą wiązać komponenty z odpowiednimi

334

Rozdział 9. ■ Niestandardowe komponenty, konwertery i mechanizmy weryfikujące

JavaServer Faces

335

try { int submittedValue = Integer.parselntOString) requestMap.get(cl ien tld)). int newValue = getIncrementedValue(submittedValue, increment); setSubmittedValue("" + newValue), setValid(true);

znacznikami. Implementacja referencyjna technologii JS F w wersji 1.0 ignorowała ten atrybut. ■ void endElement(String elementName) Zapisuje znacznik końcowy wskazanego elementu.

} ■ void writeAttribute(String attributeName, String attributeValue, String componentProperty) Zapisuje atrybut wraz z jego wartością. Metoda wri teAttribute może zostać wywołana tylko pomiędzy wywołaniami metod startElement i endElement. Parametr componentProperty reprezentuje nazwę właściwości komponentu odpowiadającej danemu atrybutowi. Wspomniany parametr w założeniu powinien być wykorzystywany przez narzędzia zewnętrzne (nie jest obsługiwany w implementacji referencyjnej w wersji 1.0).

Dekodowanie: przetwarzanie wartości żądania Aby lepiej zrozumieć proces dekodowania, musimy mieć na uwadze sposób funkcjonowa­ nia aplikacji internetowych. Serwer wysyła formularz języka H T M L do przeglądarki. Prze­ glądarka odsyła na serwer żądanie PO ST protokołu H TTP obejmujące pary nazwa-wartość. Wspomniane żądanie PO ST jest dla serwera jedynym źródłem danych o działaniach podej­ mowanych przez użytkownika w oknie przeglądarki. Jeśli użytkownik kliknie przycisk inkrementacji lub dekrementacji, odpowiednie żądanie PO ST będzie obejmowało nazwy i wartości wszystkich pól tekstowych, nie tylko nazwę i wartość klikniętego przycisku. Jeśli na przykład użytkownik kliknie przycisk inkrementacji datownika właściwego dla miesiąca (patrz rysunek 9.1), przeglądarka wyśle na serwer następu­ jące parametry żądania:

Nazwa

Wartość

_idl:monthSpinner

i

_idl:yearSpinner

12

_i d l;monthSpi nner.more

>

catch(NumberFormatException ex) { / / Obsługą błędnych danych wejściowych pozostawiamy co prawda konwerterowi, / / co nie oznacza, że nie musimy ustawić wysłanej wartości — w przeciwnym razie / / konwerter nie dysponowałby danymi wejściowymi do przetworzenia. setSubmittedValue((String) requestMap.get(clientld));

Metoda decode analizuje parametry żądania i na tej podstawie określa, które przyciski kontrolki datownika spowodowały wygenerowanie danego żądania (jeśli aktywacja któregoś z tych przycisków w ogóle nastąpiła). Jeśli istnieje parametr żądania nazwany identyfikdtorKl ienta. ^ le s s (gdzie identyfi katorKl ienta jest identyfikatorem klienta dekodowanej kontrolki), możemy być pewni, że użytkownik aktywował przycisk dekrementacji. Jeśli metoda decode zlokalizuje parametr żądania identyfi katorKl ienta .more, wiemy, że aktywowano przycisk inkrementacji. Jeśli nie istnieje żaden parametr, możemy być pewni, że dane żądanie nie zostało zainicjo­ wane przez kontrolkę datownika, zatem wartość inkrementacji powinna wynosić zero. Mimo to musimy tę wartość zaktualizować — użytkownik mógł przecież wpisać jakąś wartość w polu tekstowym i kliknąć na przykład przycisk Dalej. Nasza konwencja nazewnicza sprawdza się także w przypadku wielu datowników na poje­ dynczej stronie, ponieważ każdy datownik jest kodowany łącznie z identyfikatorem klienta komponentu, co gwarantuje, że jest unikatowy. Jeśli umieścimy na stronie naszej aplikacji wiele kontrolek datownika, każdy komponent kontrolki będzie dekodował własne żądanie. Kiedy już metoda decode określi, że kliknięto jeden z przycisków kontrolki datownika, zwięk­ sza lub zmniejsza jego wartość o 1 (w zależności od tego, który przycisk aktywowano). Zmie­ niona wartość jest obliczana przez metodę prywatną getlncrementedVal ue: private int getlncrementedValue(int submittedValue, int increment) { Integer minimum = (Integer) getAttributesO ,get("minimum"), Integer maximum = (Integer) getAttributesO.get("maximum"); int newValue = submittedValue + increment; i f ((minimum == null || newValue >= minimum.intValueO) && (maximum == null || newValue < co m p on en t-typ e > -*■— co m .co re jsf S p m n e r < /com p o nen t-type > < c o m p o n e n t-c fa ss> co m .co re jsf.U IS p in n e r

- Klasa komponentu p a c k a g e com .corejsf; pu blic c l a s s U iS p in n e r e xte n ds U lin put {

< /c o m p o n e n t-c ia ss> < /com p on en t>

341

^ S j Plikom TLD można nadawać dowolne nazwy — jedynym elementem, który ma f i i i l istotne znaczenie, jest rozszerzenie .tid. Implementacja JS F przeszukuje nastę­ pujące katalogi pod kątem zawierania plików TLD: ■ katalog HEB-INF i jego podkatalogi: ■ katalog META-INF i wszystkie pliki JAR w katalogu WEB-INF/lib.

Fakt przeszukiwania katalogu WEB-INF/lib jest szczególnie przydatny w sytuacji, gdy chcemy udostępniać swoje konwertery w formie plików JAR wielokrotnego użytku.

B S I W pierwszej kolejności skoncentrujemy się na plikach TLD i klasach obsługujących ■ lii znaczniki w kontekście technologii JSF 1.2. Szczegółowe mechanizmy w tej wersji pod wieloma względami różnią się od swoich odpowiedników znanych z wersji 1.1 (patrz punkt „Definiowanie klas obsługujących znaczniki w technologii JSF 1.1" w dalszej części tego podrozdziału). Większość definicji atrybutów w pliku T LD obejmuje element potomny deferred value. Przykład takiej konstrukcji przedstawiono poniżej: wartosc minimalna k o n tro lk i datowm ka< /descripiion>

minimum int

RysuneK 9.5. Proces konstruowania komponentu niestandardowego

Plik deskryptora TLD Musimy opracować plik deskryptora biblioteki znaczników (ang. Tag Library Descriptor — TLD), który będzie opisywał jeden lub wiele znaczników wraz z ich atrybutami. Wystarczy umieścić ten plik w katalogu WEB-LNF. Zawartość pliku deskryptora TLD opisującego nasz niestandardowy komponent spinner przedstawiono na listingu 9.2 (w dalszej części tego punktu). Zadaniem tego pliku jest określenie nazwy klasy odpowiedzialnej za obsługę znacznika (w tym przypadku com.corejsf .SpinnerTag) oraz dopuszczalnych atrybutów tego znacznika (w tym przypadku id, rendered, minimum, maximum, size i value). Warto zwrócić uwagę na znacznik u ri, który identyfikuje bibliotekę znaczników: http://corejsf.com/spinner

Do tak zdefiniowanego identyfikatora U R I odwołujemy się w dyrektywie taglib w kodzie strony JS F : taglib uri="http://corejsf.com/spiriner" prefix="corejsf" %>

Dyrektywa tagl i b jest odpowiednikiem dyrektyw definiujących standardowe przedrostki f oraz h w kodzie wszystkich stron JSF.

< /attrib u te>

Z powyższej konstrukcji składniowej wynika, że nasz przykładowy atrybut jest definiowany przez wyrażenie reprezentujące wartość. Wartość atrybutu może mieć albo postać stałej łańcuchowej, albo postać łańcucha zawierającego wyrażenia f \ . }. Element type określa typ Javy właściwy dla danego wyrażenia reprezentującego wartość. W naszym przypadku atrybut mini mumjest definiowany przez obiekt typu i nt. Niektóre atrybuty są definiowane przez wyrażenia odwołujące się do metod (zamiast do wyrażeń reprezentujących wartości). Aby zdefiniować na przykład metodę nasłuchującą akcji, powinniśmy się posłużyć następującym znacznikiem: acti oriLi stener void actionListener(javax.faces.event.ActionEvent)

Ogólnie możliwość stosowania wyrażeń reprezentujących wartość i odwołujących się do metod w definicjach atrybutów jest bardzo pożądana. Jedynym wyjątkiem od tej reguły jest atrybut id definiowany jako wartość wyrażenia przetwarzanego w czasie wykonywania (czyli wyrażenia JS P zamiast wyrażenia JS F reprezentującego wartość).

342

JavaServer Faces Identyfikator klienta tego komponentu id true

Listing 9.2. Plik deskryptora TLD spinner/web/WEB-INF/spinner.tld______________ 1 2. 7. l.l 8 spinner 9. http://corejsf,com/spinner 10. 11. 12. spinner 13. com.corejsf.SpinnerTag 14. empty 15. 16. Wartość wskazująca na właściwość komponentu 17. binding 18. 19.j avax.faces.component.UIComponent 20. 21. 22. 23. 24. Identyfikator klienta tego komponentu 25. id 26. true 27. 28. 29 30. Czy dany komponent podlega wizualizacji? 31. rendered 32 33. boolean 34. 35 36. 37. 38. Wartość minimalna kontrolki datownika 39. minimum 40. 41. int 42. 43. 44. 45. 46. Wartość maksymalna kontrolki datowmka 47. maximum 48. 49. int

Rozdział 9. ■ Niestandardowe komponenty, konwertery i mechanizmy weryfikujące

343

50. 51. 52 53. 54 Rozmiar pola tekstowego 55. size 56 57. int 58. 59. 60. 61. 62. Wartość w polu tekstowym datownika 63 value 64. true 65. 66. int 67. 68. 69. 70.

Klasa obsługująca znacznik Dla każdego znacznika niestandardowego musimy opracować, oprócz pliku deskryptora TLD , klasę obsługującą ten znacznik. W przypadku znacznika komponentu taka klasa powinna być podklasą klasy Ul Component ELTag. Jak się niedługo okaże, klasy obsługujące znaczniki właściwe dla konwerterów niestandardowych muszą dziedziczyć po klasie bazowej Component ELTag, natomiast klasy obsługujące znaczniki właściwe dla niestandardowych mechanizmów wery­ fikacji muszą dziedziczyć po klasie bazowej Val idatorELTag. Klasy znaczników komponentów odpowiadają za realizację pięciu zadań: ■ identyfikację typu komponentu; ■ identyfikację typu mechanizmu wizualizacji; ■ udostępnianie metod ustawiających wartości atrybutów znacznika; ■ składowanie wartości atrybutów znacznika w jego komponencie; ■ zwalnianie zasobów. Przyjrzyjmy się teraz implementacji klasy Spi nnerTag: public class SpinnerTag extends UIComponentELTag { private ValueExpression minimum; private ValueExpression maximum; private ValueExpression size; private ValueExpression value;

344

Rozdział 9, ■ Niestandardowe komponenty, konwertery i mechanizmy weryfikujące

JavaServer Faces Klasa znacznika kontrolki datownika definiuje po jednym polu egzemplarza dla każdego atrybutu. Klasa znacznika powinna składować wszystkie atrybuty w formie obiektów klasy

345

Metoda get mapy atrybutów sprawdza, czy dla danego komponentu istnieje wyrażenie repre­ zentujące wartość z określonym kluczem, po czym przetwarza to wyrażenie.

V a lu e E x p re s s io n .

1 wreszcie musimy zdefiniować metodę release przywracającą wartości domyślne we wszyst­ kich polach egzemplarzy: Dublic

void

release!) {

/ / zawsze wywołujemy najpierw metodę release klasy bazowej super

Klasa SpinnerTag identyfikuje typ swojego komponentu jako com.corejsf Spi n n e r oraz typ mechanizmu odpowiedzialnego za wizualizację jako n u l 1. Mechanizm wizualizacji typu n u l 1 oznacza, że dany komponent albo sam realizuje zadania związane z wizualizacją, albo wskazuje własny mechanizm wizualizacji: P u b lic

S tn in g

getComponentT y p e ! )

{

return

"com C O r e j s f

Pu b lic

S in n g

g etR en d e rerlyp e !)

{

return

n u li.

Sp in n er"

release!)

imnimum = nu 11 max imum = nu 1 1. S i z e - nul l v a 1ue = n u l i

i

\

}

■89 Jeśli metoda getRendererlype klasy obsługującej znacznik zwraca wartość nul 1, m M musimy w konstruktorze komponentu wywołać metodę setRendererType. Jeśli dany komponent sam realizuje zadania związane ze swoją wizualizacją, należy wywołać metodę setRendererType( nul 1).

Definicja tej metody jest niezbędna, ponieważ implementacja JS F może składować obiekty obsługujące znaczniki w pamięci podręcznej i wielokrotnie je wykorzystywać do analizy składniowej znaczników. Jeśli obiekt klasy obsługującej znacznik jest wykorzystywany wielokrotnie, w żadnym razie nie powinniśmy w nim pozostawiać ustawień właściwych dla poprzedniego znacznika.

Klasa SpinnerTag definiuje metody ustawiające dla wszystkich obsługiwanych przez siebie atrybutów, czyli minimum, maximum, value i size:

M | Klasy znaczników nadpisujące metody setProperties i release zawsze muszą ■Kaa wywoływać odpowiednie metody swoich nadkłas.

p u b lic

vo id

setM inim um (ValueEx p ressio n

newValue)

\

p u b lic

vo id

seLM axim u m (ValueExp ression

newValue)

{ maximum = n e w V a l u e .

P u b lic

vo id

se tV a lu e (V a lu e E x p re ssio n

p u b lic

vo id

s e tS iz e (V alu e Ex p re ssio n

newValue) newValue)

{ {

minimum

v a lu e s iz e

= newValue

■ = newValue.

= newValue

} \

Kompletny kod klasy SpinnerTag przedstawiono na listingu 9.3.

|

f

W czasie przetwarzania danego znacznika w ramach strony JS F wartości jego atrybutów są konwertowane na obiekty klasy ValueExpression, po czym następuje wywołanie metod ustawiających. Metody zwracające są w tym przypadku zbędne. Klasy obsługujące znaczniki muszą nadpisywać metodę setProperties, aby mieć możli­ wość kopiowania wartości atrybutów znaczników do odpowiednich komponentów. Nazwa przytoczonej metody jest o tyle myląca, że za jej pomocą z reguły ustawiamy atrybuty lub wyrażenia, nie właściwości: public void setPropertiesdJIComponent component) { / / zawsze wywołujemy najpierw metodę setProperties klasy bazowej super.setProperties(component), component,setValueExpression("size", size); component.setValueExpression("minimum", minimum); component.setValueExpression("maximum". maximum); component.setValueExpression("value", value);

} Istnieje też możliwość przetwarzania wyrażeń reprezentujących wartości z wykorzystaniem mapy atrybutów. Przykład takiego rozwiązania przedstawiono poniżej: Integer minimum = (Integer) component.getAttributes() . get( "minimum").

Listing 9.3. Kod klasy spinner/src/java/com/corejsf/SpinnerTag.java 1

package c o m .c o r e js f .

2 3. import javax.el.ValueExpression; 4. import ja vax.faces.component.UIComponent; 5. import javax.faces.webapp.UIComponentELTag; 6.

7. public class SpinnerTag extends UIComponentELTag { 8. private ValueExpression minimum = null; 9. private ValueExpression maximum = null: 10. private ValueExpression size = null; 11. private ValueExpression value = null; 12.

13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24.

public String getRendererType() { return null. } public String getComponentType() { return "com.corejsf.Spiriner” ; } public public public public

void void void void

setMinimum(ValueExpression newValue) { minimum = newValue; } setMaximum(ValueExpression newValue) { maximum = newValue; } setSize(ValueExpression newValue) { size = newValue; } setValue(ValueExpression newValue) { value = newValue; }

public void setPropertiesdJIComponent component) { I I zawsze wywołujemy najpierw metodę setProperties klasy bazowej super.setProperties(component);

346

JavaServer Faces 25. component.setValueExpressionC'size", size); 26. component.s e tV a l ueExpression("minimum", minimum); 27. component.setValueExpression("maximum", maximum); 28. component.setValueExpression("value", value); 29. } 30. 31. public void releaseO { 32. I I zawsze wywołujemy najpierw metodę release klasy bazowej 33. super releaseO; 34. 35. minimum = nul 1; 36. maximum = nul 1; 37. size = nul 1; 38 value = nul 1; 39 ) 40 }

QQQ javax.faces.webapp.UIComponentELTag (JSF 1.2) ■ void setPropertiesdJIComponent component) Przenosi wartości atrybutów znacznika do właściwości, atrybutów lub właściwości i atrybutów komponentu. Komponenty niestandardowe muszą wywoływać metodę setProperties swoich nadklas, aby zagwarantować prawidłowe ustawienie atrybutów obsługiwanych przez klasę bazową Ul Component EL Tag, czyli bmd mg, id i rendered. ■ void releaseO Przywraca oryginalny stan danego znacznika i tym samym stwarza możliwość jego ponownego użycia.

QBQ javax.faces.component.UlComponent ■ void setValueExpression(String name, ValueExpression expr) (JS F 1.2) Jeśli przekazane wyrażenie jest stałe (nie zawiera wyrażeń # { ...}) , następuje jego przetworzenie i umieszczenie pary nazwa-wartość w mapie atrybutów komponentu. W przeciwnym razie para nazwa-wyrażenie jest umieszczana w mapie wyrażeń reprezentujących wartości danego komponentu.

Aplikacja zbudowana z wykorzystaniem kontrolki datownika Po omówieniu komponentu datownika z wielu różnych perspektyw warto przystąpić do bliższej analizy kompletnego przykładu praktycznego użycia tego komponentu. W tym punkcie przed­ stawiono kod aplikacji testowej z rysunku 9.1. Strukturę katalogów tej aplikacji przedstawiono na rysunku 9.6; kod źródłowy przedstawiono na listingach od 9.4 do 9.9.

Rozdział 9. ■ Niestandardowe komponenty, konwertery i mechanizmy weryfikujące Rysunek 9.8.

spinner.war

Struktura katalogów aplikacji wykorzystującej kontrolkę datownika

f ¿3 META-INF

|

L Q MANIFEST. MF

^-£3 WEB-INF ! f ■[¡3 classes j j f | 3 com \ f-£3 corejsf Q OreditCardExpiration. class

j \

r D

SpinnerTag. class

T Q UlSpinner.class 1 j

j

Q messages, properties

D

spinner.tld

\

P O faces-config.xml

I

' Q web.xml

f •Q styies.css

Q Q D

index html »naex jsp next jsp

Listing 9.4. Kod strony spinner/web/index.jsp 1 2 lagi ib un ="http //java Sun com/jsf/core" prefix=” f" %> 3 taglib uri-"http //java.sun com/jsf/html" prefix="n" %> 4 taglib uri =-"nUp.//corejsf com/spinner" prefix-"corejsf" %> 5 6 7 8 9 10 11 12 13 15 16. 17. 18. 20. 21. 22 24. 25. 26 27. 28. 29. 30. 31.

Listing 9.5. Kod strony spinner/web/next.jsp 1. 2. 3. 4.

347

348

JavaServer Faces 5. 6 7. 8. 9 10. 11 12. 13 14. 15 / 16.

■ MethodBindmg createMethodBmdingCString methodBmdmgExpnession. Cl ass[ ] arguments)

Okazuje się jednak, że metoda selAction interfejsu ActionSource we wszystkich przypadkach wymaga obiektu klasy MeŁhodBmdmg. Jeśli akcja ma postać stałej łańcuchowej, musimy skonstruować obiekt klasy MethodBindmg, którego metoda getExpressionString zwraca ten

Tworzy dowiązanie do metody i składuje je w danej aplikacji. Łańcuch reprezentowany przez argument methodBindmgExpression musi mieć postać wyrażenia reprezentującego dowiązanie do metody. Parametr typu Class[] reprezentuje typy argumentów przekazywanych na wejściu danej metody.

łańcuch: if

(UIComponentTag is V a lu e R e fe re n c e (a u n p u te V a lu e )) ((ActionSounce) component) .setMeihodB ind mg (component. " a c tio n ". a ttn b u te V a lu e .

javax, faces.component.UlComponent

new C 1 a s s [ ] { } ) . e ls e { FacesContext context = F a c e s C o n te x t.g e tC u rre n tIn s ta n c e !). A p p lic a tio n app = c o n t e x t .g e t A p p lic a tio n ().



void setValueBinding(String name. ValueBinding va1ueBinding)

Składuje w danym komponencie dowiązanie do wartości (według nazwy).

MethodBindmg mb = new ActionM ethodBm clingtat L n b u t e V a lu e ). com ponent.getA ttn p u t e s !) . p u t( " a c t io n " .

ValueBinding createVa 1ueB i nd i ng ( S t r i ng valueBmchngExpression)

mb).

033 javax.faces.webapp.UIComponentTag

1 Poniżej przedstawiono definicję klasy ActionMethodBindmg (niezbędnej do prawidłowego funkcjonowania naszego kodu):

■ static boolean isValueReferenceCStrmg expression) Zwraca wartość true, jeśli łańcuch expression rozpoczyna się od sekwencji "tt{ " i kończy sekwencją "}

p u b lic c la s s ActionMethodBindm g extends MethodBindmg implements S e r ia liz a b le { p r i v a le S trm g resul t . p u b lic A ctio n M etn o d B m d m g (Strin g r e s u lt ) { t h i s . r e s u lt = r e s u lt . } p u b lic O bject m voke(FacesContext con tex t. O bject p aram sf]) { re tu rn r e s u lt . p u b lic S t r in g g e tE x p re s s io n S tm n g !) { re tu rn r e s u lt .

003 javax, faces.component.EditableValueHolder [

}



p u blic C lass getType(FacesContext co n tex t) { retu rn S t r in g . c la s s . }

W technologii JS F 1.2 obsługa wyrażeń wskazujących na metody jest dużo prostsza (szcze­ gółowe omówienie tego modelu można znaleźć w punkcie „Obsługa wyrażeń wskazujących na metody” w dałszej części tego rozdziału).

00| ■

Ustawia dowiązanie do metody nasłuchującej zmian wartości właściwej dla danego komponentu. Dowiązywana metoda musi zwracać void i przyjmować na wejściu obiekt klasy ValueChangeEvent. ■

Zwraca referencję do bieżącego egzemplarza klasy FacesContext. ■ Application getApplicati on C)

void se tV a lidaton(MethodBinding m)

Ustawia dowiązanie do metody weryfikującej dane właściwe dla tego komponentu. Dowiązywana metoda musi zwracać void i przyjmować na wejściu obiekt klasy ValueChangeEvent.

javax. faces. context,FacesContext s ta tic FacesContext getCurrentlnstanceO

void setValueChangeListenen(MethodBindmg m)

BSD javax. faces.component.ActionSource ■ void setActionListenen(MethodBinding m) Ustawia dowiązanie do metody nasłuchującej akcji właściwej dla danego komponentu. Dowiązywana metoda musi zwracać void i przyjmować na wejściu obiekt klasy Act i onEvent.

Zwraca obiekt klasy Appl i cation właściwy dla danej aplikacji internetowej.



void setAction(MethodBindmg m)

Ustawia dowiązanie do akcji właściwej dla danego komponentu. Dowiązywana metoda może zwracać obiekt dowolnego typu i nie przyjmuje na wejściu żadnych parametrów.

353

354

Rozdział 9. ■ Niestandardowe komponenty, konwertery i mechanizmy weryfikujące

JavaServer Faces

javax.faces.Input com.corejsf.Spinner com.corejsf.SpinnerRenderer

Doskonalenie komponentu datownika W tym podrozdziale wrócimy do komponentu datownika omówionego we wcześniejszych podrozdziałach. Datownik w dotychczasowej formie miał dwie istotne wady. Po pierwsze, nasz komponent sam odpowiadał za swoją wizualizację, co uniemożliwiało między innymi dołączanie odrębnych mechanizmów wizualizacji w trakcie przenoszenia aplikacji na tele­ fony komórkowe. Po drugie, komponent datownika wymagał komunikacji z serwerem za każdym razem, gdy użytkownik klikał przycisk inkrementacji lub dekrementacji. Takie podejście byłoby trudne do zaakceptowania w przypadku rzeczywistej kontrolki, od której wymaga się znacznie wyższej wydajności. W tym podrozdziale spróbujemy rozwiązać opisane problemy. Przy okazji spróbujemy rozszerzyć funkcjonalność komponentu datownika o możliwość dołączania metod nasłuchujących zmian wartości.

Stosowanie zewnętrznych mechanizmów wizualizacji W przykładzie analizowanym do tej pory klasa UI Spinner sama odpowiadała za wizualizację naszego komponentu. Okazuje się jednak, że większość klas interfejsu użytkownika deleguje zadanie wizualizacji do odrębnych, wyspecjalizowanych klas. Stosowanie zewnętrznych mechanizmów wizualizacji jest o tyle korzystne, że ułatwia wymianę tych mechanizmów, dostosowywanie komponentu do współpracy z różnymi zestawami narzędzi interfejsu użytkownika, a także stosowanie rozmaitych efektów HTML-a.

Element component-fami ly ma na celu wyeliminowanie problemu, z którym od niepamięt­ nych czasów zmagają się twórcy stron HTML-a. Nazwy standardowych znaczników tego języka w założeniu mają określać typ komponentu i mechanizmu wizualizacji. Na przykład znacznik h :selectOneMeriu wskazuje na komponent UISelectOne wykorzystujący mechanizm wizualizacji typu javax. faces .Menu. Ten sam mechanizm wizualizacji może być stosowany także dla znacznika h: sel ectManyMenu. Okazuje się jednak, że opisywany model nie we wszystkich przypadkach zdaje egzamin. Mechanizm wizualizacji dla znacznika h:inputText zapisuje pole tekstowe i nput języka HTM L. Ten sam mechanizm nie powinien być stosowany dla znacznika h: outputText, ponieważ nie chcemy, aby dane wynikowe były prezentowane na stronie w formie pola tekstowego. Zamiast identyfikować mechanizmy odpowiedzialne za wizualizację poszczególnych kom­ ponentów, należy określać tylko typy tych mechanizmów i rodziny komponentów. W ta­ beli 9.2 przedstawiono rodziny komponentów właściwe dla wszystkich standardowych klas komponentów. W naszym przypadku najlepszym rozwiązaniem jest użycie rodziny kompo­ nentów javax. faces. Input, ponieważ klasa UISpinner jest podklasąklasy Ullnput.

Tabela 9.2. Rodziny komponentów, do których naieżą kiasy komponentów standardowych Klasa komponentu

Rodzina komponentów

W podrozdziale „Użycie skryptu JavaScript do ograniczenia komunikacji z serwerem” po­ każemy, jak można wykorzystać alternatywny mechanizm wizualizacji stosujący kod języka JavaScript do śledzenia wartości datownika po stronie klienta.

UlCommand

javax.faces.Command

UI Data

javax.faces.Data

Ul Form

javax.faces.Form

Stosowanie zewnętrznego mechanizmu wizualizacji wymaga podjęcia następujących kroków:

UIGraphic

javax.faces.Graphic

1. Zdefiniowania łańcucha identyfikatora dla mechanizmu wizualizacji.

Ullnput

javax.faces.Input

2. Zadeklarowania tego mechanizmu w pliku konfiguracyjnym JSF .

UIMessage

javax.faces.Message

UIMessages

javax.faces.Messages

UlOutput

javax.faces.Output

UlPanel

javax.faces.Panel

UISelectBoolean

javax.faces.SelectBoolean

UISelectMany

javax.faces.Sel ectMany

UISelectOne

javax.faces.SelectOne

3. Takiego zmodyfikowania klasy znacznika, aby jej metoda getRendererType zwracała identyfikator mechanizmu wizualizacji. 4. Implementacji klasy wizualizacj i. Odpowiedni identyfikator (w naszym przypadku com.corejsf .Spinner) koniecznie należy zdefiniować w pliku konfiguracyjnym JSF : com.corejsf.Spinner com.corejsf. LlISpi nner

355

Metoda getRendererType klasy naszego znacznika musi zwracać identyfikator mechanizmu wizualizacji:

356

Rozdział 9. ■ Niestandardowe komponenty, konwertery i mechanizmy weryfikujące

JavaServerFaces public class SpinnerTag extends UIConiponentTag { public String getComponentType() { return "com.corejsf.Spinner", } public String getRendererType!) { return "com.corejsf .Spiriner"; } }

I | i Identyfikatory komponentów i rnechanizmów wizualizacji należą do odrębnych ■ m i przestrzeni nazw, Oznacza to, ż

■ void addVal i da tor (V alidator v a l ) (JS F 1.2)

2 3

Dodaje do danego komponentu mechanizm weryfikujący.

UDjavax. faces. component.Act ionSource ■

8 9

10 11 12 13 L4 15 16 17

J8 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36. 37. 38.

javax. faces.component.EditableValueHolder

Dodaje do danego komponentu metodę nasłuchującą akcji.

< n a vig a n o n - ru ie >

/index jsp next /next. jsp

/next.jsp rom-outcowe>again /mdex.jsp cardExpiramonDate com.corejsf CreditCardExpiration session com corejsf Spinner component-class>com.corejsf UlSpmner javax.faces.Input

void addActionListener(ActionListener 1istener) (JS F 1.2)

¡01 ■

javax.faces.component.ActionSource2 (JSF 1.2) void addAction(MethodExpression m)

Dodaje do danego komponentu akcję. Metoda akcji zwraca wartość typu String i nie przyjmuje na wejściu żadnych parametrów.

M javax.faces.event.MethodExpressionValueChangeListener ■

(JSF 1.2)

MethodExpressi onVa1ueChangeListener(MethodExpression m)

Konstruuje mechanizm nasłuchujący zmian wartości na podstawie wyrażenia wskazującego na metodę. Metoda nasłuchująca zwraca typ void i przyjmuje na wejściu obiekt klasy Va 1ueChangeEvent..

¡¡U javax.faces.validator.MethodExpressionValidator (JSF 1.2) ■

MethodExpressi onVa11 da tor(MethodExpression m)

Konstruuje mechanizm weryfikujący na podstawie wyrażenia wskazującego na metodę. Nowa metoda zwraca typ void i przyjmuje na wejściu obiekty klas FacesContext, Ul Component i Object.

367

368

JavaServer Faces M javax. faces.event.MethodExpressi onActionListener (JS F 1.2) ■ MethodExpressionActionnstener(MethodExpression m)

Konstruuje mechanizm nasłuchujący akcji na podstawie wyrażenia wskazującego na metodę. Metoda nasłuchująca zwraca typ void i przyjmuje na wejściu obiekt klasy Act lonEvent.

UJ

Rozdział 9. ■ Niestandardowe komponenty, konwertery i mechanizmy weryfikujące

369

Jeśli klucza targetClass nie uda się odnaleźć we wspomnianej strukturze mapy, metoda createConverter przeszuka tę strukturę pod kątem zawierania klucza właściwego dla interfejsów i nadklas klucza targetClass (właśnie w tej kolejności) aż do odnalezienia pasującej klasy. Po zakończeniu procesu poszukiwania pasującej klasy metoda createConverter tworzy i zwraca odpowiedni konwerter. Jeśli poszukiwanie konwertera dla przekazanego klucza (oraz jego interfejsów i nadklas) zakończy się niepowodzeniem, metoda createConverter zwróci wartość nul 1.

javax. faces.event.ValueChangeEvent

■ Object getO1dVa1ue() Zwraca starą wartość danego komponentu. ■ Object getNewValue()

Użycie skryptu JavaScript do ograniczenia komunikacji z serwerem

Zwraca nową wartość danego komponentu.

U J ja v a x .faces .component. Val ueHol der ■ Converter getConverter()

Zwraca konwerter przypisany do danego komponentu. Interfejs Val ueHol der jest implementowany zarówno przez komponenty wejściowe Ja k i przez komponenty wyjściowe.

UJ

javax.faces.component.UlCornponent

■ ValueExpression getVa 1ueExpression(5tring name) (JS F 1.2)

Komponent datownika komunikuje się z serwerem za każdym razem, gdy użytkownik klika któryś z jego przycisków. Komunikacja z serwerem ma na celu aktualizację składowanej tam wartości datownika. Wymiana komunikatów pomiędzy klientem a serwerem może znacznie obniżyć wydajność komponentu datownika, zatem niemal we wszystkich przypadkach lepszym rozwiązaniem jest składowanie wartości komponentu po stronie klienta i aktualizowanie jej odpowiednika po stronie serwera tylko po wysłaniu odpowiedniego formularza na serwer. Możemy ten cel osiągnąć, stosując następujący skrypt języka JavaScript:



George Wasfafegtoa był pierwszym prezydentem Stanów Zjednoczonych. Urodzi się w roku 1752 w stanie Wirginia W roku 1775 został mianowany naczelnymwodza» Aitw Kontynentalnej. Sześć hi ,później zmusi do złożenia kapitulacji Chaitesa CorawaShsa pod Yorktown Urząd prezydenta Stanów j Zjednoczooycłj objął 30 kwietnia 1789 roku

<

trzy kolejne facey —>

< /co re jsf tabbeclPane>

Zakończono

Komponent panelu podzielonego na zakładki w kluczowych aspektach różni się od imple­ mentacji podobnej konstrukcji z rozdziału 7. Implementacja z rozdziału 7. była przygoto­ wywana naprędce i obejmowała takie standardowe znaczniki JS F jak h: graphicImage czy

W wyniku wykonania powyższego kodu zostanie wyświetlony panel podzielony na zakładki bez specjalnych efektów wizualnych (patrz rysunek 9.10).

374

JavaServer Faces

Rysunek 9.10.

Rozdział 9. ■ Niestandardowe komponenty, konwertery i mechanizmy weryfikujące panek,

ę

Przykład prostego panelu podzielonego na zakładki

Plik

Edycja

Widok

Mosń. Buton a

Zakładki

Narzędzia

ls * S llS Í> :

http://localhort:8080/tabbedpa j

Jefferson

Roosevelt

Lincoln

0

Pomoc Google

lilii

W ashmgtori

Theodore Roosevelt- bvł 26 prezydentem Stanów Zjednoczonych Został prezydentem w roku 1901 po zamachu na prezydenta M cKin ley a Urząd prezydenta objął w wieku 42 lat i przeszedł do histom jako najmłodszy w

375

i wreszcie komponent panelu podzielonego na zakładki generuje zdarzenie akcji w reakcji na zaznaczenie zakładki przez użytkownika. Aby dodać jedną lub wiele metod nasłuchują­ cych, należy użyć znacznika f act íonL isLener. Można też wskazać metodę odpowiedzialną za obsługę zdarzeń akcji za pośrednictwem atrybutu actioriL istener znacznika panelu:

historii Stanów Zjednoczonych

sud-iwez

Skoro dysponujemy już ogólną wiedzą o komponencie panelu podzielonego na zakładki, możemy przystąpić do szczegółowej analizy implementacji zaawansowanych elementów tej konstrukcji. Poniżej wymieniono dalsze punkty tego podrozdziału: ■ „Przetwarzanie znaczników potomnych typu Selectltenf

Aby uzyskać efekt widoczny na rysunku 9.9, wystarczy użyć stylów C SS, stosując nastę­ puj ącą konstrukcj ę:

Zamiast wielu znaczników f :sel ecti tem można też użyć pojedynczego znacznika f :sel ect Items:

W takim przypadku znaczniki będą definiowane na poziomie komponentu JavaBean. W poprzednim przykładzie bezpośrednio zdefiniowano tekst wyświetlany w poszczegól­ nych zakładkach (czyli etykiety Jefferson , Roosevelt itd.). Tym razem zastosowano zdecy­ dowanie bardziej elastyczny model, zgodnie z którym mechanizm odpowiedzialny za wizu­ alizację jeszcze przed zakodowaniem zakładki podejmuje próbę odnalezienia odpowiedniej etykiety w pakiecie komunikatów i w razie jej odnalezienia wykorzystuje wartość właściwą dla danego klucza. Jeśli odpowiedniej etykiety nie uda się odnaleźć w pakiecie komunika­ tów, mechanizm wizualizacji koduje etykiety w niezmienionej formie. Pakiet komunikatów wskazujemy za pośrednictwem atrybutu resourceBundl e:

Zapisywanie stanu po stronie klienta jest niezbędne, jeśli nasza aplikacja ma być wykorzy­ stywana przez użytkowników, którzy wyłączyli obsługę znaczników kontekstu klienta. Opisywany mechanizm poprawia też skalowalność aplikacji internetowej. Zapisywanie stanu aplikacji po stronie klienta oczywiście ma też swoje wady: największą z nich jest konieczność przesyłania obszernych informacji o stanie w ramach wszystkich żądań i odpowiedzi.

380

JavaServer Faces Klasa UITabbedPane definiuje pole egzemplarza reprezentujące nazwę facety właściwej dla aktualnie prezentowanej zakładki. Jeśli nasze komponenty obejmują pole egzemplarzy i jeśli istnieje możliwość wykorzystywania tych komponentów w ramach aplikacji internetowych, których stan jest zapisywany po stronie klienta, musimy zaimplementować metody saveState i restoreState interfejsu StateHolder. Wymienione metody mają następującą postać: public Object saveState(FacesContext context) { Object values[] = new 0bject[n], va1ues[0] = supen saveState(context); values[1] = pole egzemplarza #1, values[2] = pole egzemplarza #2 : return values:

Rozdział 9. ■ Niestandardowe komponenty, konwertery i mechanizmy weryfikujące ■ Dodać do kodu strony index.jsp przycisk zdefiniowany przez konstrukcję . ■ Uruchomić aplikację i kliknąć jedną z zakładek. ■ Kliknąć przycisk Przetestuj zapisywanie stanu. Bieżąca strona zostanie co prawda ponownie wyświetlona, jednak na tej stronie nie będzie już zaznaczona żadna z zakładek!

Opisany problem wynika z faktu, że stan strony jest zapisywany po stronie klienta i kodo­ wany w formie wartości pola ukrytego. W momencie ponownego wyświetlania strony jest konstruowany nowy obiekt klasy UITabbedPane, po czym następuje wywołanie jego metody restoreState. Jeśli klasa UITabbedPane nie nadpisuje metody restoreState, wartość pola content nie jest przywracana.

} public void restoreState(FacesContext context, Object state) { Object values[] = (Object[ ]) state, super.restoreState(context, values[0]): pole egzemplarza #1 = (.Typ) val ues [1]: pole egzemplarza #2 = (Typ) values[2]:

} W przedstawionym rozwiązaniu zakładamy, że wartości pól egzemplarzy mogą być serializowane. Gdyby było inaczej, musielibyśmy samodzielnie operować na reprezentacji stanu komponentu. (Więcej informacji na temat mechanizmu serializacji w Javie można znaleźć w rozdziale 12. książki Core Java™ 2, vol. 1 — Advanced Features (7th ed.) 1autorstwa Cay a Horstmanna i Gary’ego Cornelia). Implementację mechanizmu zapisywania i odtwarzania stanu klasy UITabbedPane można znaleźć na listingu 9.16 w dalszej części tego rozdziału.

Jeśli wszystkie informacje opisujące stan w formie atrybutów, nie będziemy musieli implei restoreState, ponieważ atrybuty k o m p o n ™ implementację JSF. Na przykład kompone wykorzystywać atrybut "content" zamiast pola content.

a

ly składować I saveState ani ycznie przez kładki może

W takim przypadku w ogóle nie potrzebujemy klasy UITabbedPane — wystarczy użyć nadklasy UlCornmand i zadeklarować klasę komponentu za pośrednictwem następującej konstrukcji: com.corejsf.TabbedPane javax.faces.compónent .UICornrnand i | | t l | :|T TT j ;U J 7 % I S J f

Listing 9,16. Kod klasy tabbedpane/src/java/com/corejsf/UITabbedPane.java 1. package com.corejsf; 2.

P S I Wielu Czytelników zapewne nie rozumie, dlaczego nie zdecydowaliśmy się na użycie K i l standardowego algorytmu serializacji Javy. Mechanizm serializacji obiektów Javy jest co prawda dość elastyczny i uniwersalny, ale nie gwarantuje najbardziej efektywnego formatu kodowania stanu komponentów. Architektura JSF umożliwia twórcom kontenerów JS F konstruowanie bardziej efektywnych mechanizmów. Aby przekonać się, dlaczego zapisywanie stanu jest konieczne, warto przeprowadzić nastę­ pujący eksperyment: ■ Objąć metody saveState i restoreState komentarzami. ■ Aktywować mechanizm zapisywania stanu po stronie klienta przez dodanie następujących wierszy do pliku konfiguracyjnego web.xml: javax.faces.STATE_SAVING_METHOD client

1 Polskie wydanie: Java 2. Podstawy, Helion, 2003 — przyp. tłum.

381

3. import javax.faces.component.UlCornmand: 4. import javax.faces.context.FacesContext: 5. 6. public class UITabbedPane extends UlCornmand { 7. private String content: 8.

9. 10.

public String getContentO { return content: } public void setContent(String newValue) { content = newValue; }

11.

12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23.

/ / A by sprawdzić, jaki będzie skutek niemożności prawidłowego zapisania I I stanu przez komponent, wystarczy objąć komentarzem te dwie metody. public Object saveState(FacesContext context) { Object val ues[] = new 0bject[3]: values f 0] = super.saveState(context); valuesfl] = content: return values; } public void restoreState(FacesContext context, Object state) { Object values[] = (Objectf]) state: super.restoreState(context, valuesf0]);

382

Rozdział 9. ■ Niestandardowe komponenty, konwertery i mechanizmy weryfikujące

JavaServer Faces 24

content = (S t r in g ) vd 1ues[1 ] .

25

}

26 }

_

_

QQQjavax. faces. component. StateHolcier ■ Object saveState(FacesContext context)

Zwraca obiekt typu Seria 1izable, który zapisuje stan danego obiektu. ■

void rescoreState(FacesContext context. Object state)

Przywraca stan tego obiektu na podstawie danego obiektu stanu, który ma postać kopii obiektu uzyskanego wcześniej za pomocą wywołania metody saveState. ■

void setTransient(boolean newValue)

■ boolean isTransientO Ustawia i zwraca wartość właściwości pomijania. Po ustawieniu tej właściwości stan danego komponentu nie jest zapisywany.

Generowanie zdarzeń akcji Jeśli nasz komponent ma obsługiwać zdarzenia akcji lub same akcje, musimy podjąć nastę­ pujące kroki: ■ Komponent powinien rozszerzać klasę bazową UICommmand. ■ Musimy kolejkować obiekty klasy Acti onEvent w ramach metody decode naszego mechanizmu wizualizacji. Komponent panelu podzielonego na zakładki generuje zdarzenie akcji w chwili kliknięcia przez użytkownika którejś z zakładek tego komponentu. Odpowiednia akcja jest umieszczana w kolejce przez metodę decode klasy TabbedPaneRenderer: public void decode(FacesContext context, UlComponent component) { UITabbedPane tabbedPane = (UITabbedPane) component; component.queueEvent(new ActionEvent(tabbedPane));

} Na tym możemy zakończyć naszą analizę klasy TabbedPaneRenderer. Kompletny kod tej klasy przedstawiono na listingu 9.17. Klasa TabbedPaneTag jest równie nieciekawa jak w poprzednich wersjach naszej aplikacji, stąd zrezygnowano z jej ponownego prezentowania.

Listing 9.17. Kod klasy tabbedpane/src/java/com/corejsf/TabbedPaneRenderer.java________________ 1. package com.corejsf; 2.

3. import j ava.i o.IOException; 4. import java.util.Map; 5. import java.u ti1.1ogging.Level;

6 7 8 9 10 11 12 13 J4 15 16 17 18 19

383

import java util logging.Logger. import javax faces component UlComponent

import javax faces context.ExternaIContext. import javax faces context.FacesContext. import javax faces context Responsewrite r. import javax faces event ActionEvent. import javax faces.model Selectltem. import javax faces. render Renderer. import javax servlet.SenvletContext. import javax.servlet ServletException import javax servlet ServletRequest import javax servlet ServletResponse. / / Klasa odpowiedzialna za wizualizację komponentu UITabbedPane

20

21. 22 23. 24 25. 26. 27. 28. 29. 30. 31. 32. 33. 34 35. 36. 37. 38 39 40 . 41 42. 43. 44. 45. 46. 47. 48. 49 50. 51. 52. 53. 54. 55. 56. 57 58. 59. 60 . 61. 62.

public class TabbedPaneRenderer extends Renderer { private static Logger logger = Logger.getLogger("com.corejsf.util"); / / Metoda getRenders ChiIdrenQ domyślnie zwraca wartośćfalse, zatem metoda encodeChildrenQ nie zostanie // wywołana, dopóki nie nadpiszemv metody getRenders Children () wersją zwracającą wartość tnie. public boolean getRendersChildren() { return true; } // // II II II

Metoda decode uzyskuje wartość parametru żądania, którego nazwa odpowiada identyfikatorowi klienta komponentu panelu podzielonego na zakładki. Odpowiedni parametr żądaniajest kodowany przez metodę encodeHiddenField (wywoływaną przez metodę encodeEnd) wformie pola ukrytego. Wartość tego parametni jest ustawiana przez skryptjęzyka JavaScript wygenerowany przez metodę encodeTab i reprezentuje nazwę facety lub strony JSP.

/ / Metoda decode wykorzystuje wartość tego parametru żądania do ustawienia I I atrybutu treści komponentu panelu podzielonego na zakładki. I I I wreszcie metoda decodeQ umieszcza zdarzenie akcji w kolejce, z której zostanie I I przekazane metodom nasłuchującym wfazie wywołania aplikacji cyklu życia JSF. I / Metody nasłuchujące akcji można definiować za pośrednictwem atrybutu I I actionListener znacznika lub znaczników I I w ciele znacznika . public void decode(FacesContext context, UlComponent component) { Map requestParams = context.getExternalContext O .getRequestParameterMap(); String clientld = component.getClientld(context); String content = (String) (requestParams.get(clientld)); i f (content != null && ! content.equals(" " )) { UITabbedPane tabbedPane = (UITabbedPane) component; tabbedPane.setContent(content); } component.queueEvent(new ActionEvent(component)), } / / Metoda encodeBegin zapisuje początkowy element języka HTML I I wraz z klasą CSS wskazywaną za pośrednictwem atrybutu styleClass / 1 znacznika (jeśli taki atrybut w ogóle zdefiniowano).

384

JavaServer Faces 63. 64. 65 66 67 68. 69 70. 71. 72. 73. 74. 75.

76. 77

.

78. 79. 80. 81. 82. 83. 84. 85.

public void encodeBegin(FacesContext context. UlComponent component) throws Java.io.IOException { ResponseWriter writer = context.getResponseWriter(); writer.startElementC"table", component): String styleClass = (String) component.getAttributesO,get("styleClass"), i f (styleClass != null) writer writeAttributeC'class". styleClass. null); wri te r .wri te (" \n"); / / taki krok poprawia czytelność generowanego kodu HTML-a }

I I Metoda encodeChildren() jest wywoływana przez implementację JSFpo metodzie encodeBegin(). I I Elementy potomne względem komponentu mają postać komponentów UISelectItem / / ustawionych za pośrednictwem jednego lub wielu znaczników> bądź pojedynczego znacznika I I w ciele znacznika . public void encodeChildren(FacesContext context, UlComponent component) throws java.io.IOException {

I I Metoda encodeChildren jest wywoływana, nawetjeśli komponent tabbedpane I I nie obejmuje komponentów potomnych. i f (component.getChiIdCountO == 0) { return:

86.

87. 88. 89 90. 91. 92. 93. 94. 95. 96. 97. 98. 99. 100. 101. 102. 103. 104. 105. 106. 107 . 108. 109. 110.

111. 112. 113. 114. 115.

116. 117. 118.

}

ResponseWriter writer = context.getResponseWriter(); writer.startElementC'thead", component); writer.startElementC'tr", component); writer.startElementCth", component); writer.startElementC'table". component); writer.startElementC'tbody", component); writer.startElementC'tr", component); for (SelectItem item : com.corejsf.util.Renderers.getSelectltems(component)) encodeTab(context, writer, item, component); writer.endElementCtr"); writer.endElementC'tbody"); writer.endElement("table"); writer.endElementC'th"); writer.endElementCtr"); writer.endElementC'thead"); wri te r.wri te (" \n"); II taki krok poprawia czytelność generowanego kodu HTML-a }

I I Metoda encodeEndOjest wywoływana przez implementację JSFpo metodzie encodeChildrenQ. I I Metoda encodeEnd() zapisuje ciało tabeli i koduje zawartość komponentu tabbedpane / / w ramach pojedynczego wiersza tej tabeli. I I Treść panelu podzielonego na zakładki określamy albo wformie adresu URL wskazującego na stronę JSP, albo w formie nazwyfacety. Metoda encodeEnd() I I spraw’dza, czy wskazano facetę; jeśli tak, koduje ją; jeśli nie, dołącza stronę JSP. //

public void encodeEnd(FacesContext context. UlComponent component)

Rozdział 9. ■ Niestandardowe komponenty, konwertery i mechanizmy weryfikujące 119. 120. 121 122. 123. 124. 125. 126. 127. 128. 129. 130. 131. 132. 133. 134. 135. 136. 137. 138. 139. 140. 141. 142. 143. 144. 145. 146. 147. 148. 149. 150. 151. 152. 153. 154. 155. 156. 157. 158. 159. 160. 161. 162. 163. 164. 165. 166. 167. 168. 169. 170. 171. 172. 173. 174. 175.

385

throws java.io.IOException { ResponseWriter writer = context,getResponseWriter(); UITabbedPane tabbedPane = (UITabbedPane) component: String content = tabbedPane.getContent(), writer startElement("tbody", component); writer.startElementC'tr", component), writer.startElementC'td", component); i f (content != null) { UlComponent facet = component.getFacet(content); i f (facet != null) { i f (facet.isRenderedO) { facet.encodeBegin(context), i f (facet.getRendersChildrenO) facet.encodeChildren(context); facet.encodeEnd(context); } } else includePage(context, component), } writer.endElementC'td"); writer.endElement("tr"); writer.endElementC'tbody"); //

Zamyka elementy kolumny, wiersza i tabeli.

writer.endElement("ta b le "); encodeHiddenField(context, writer, component); } //

Metoda encodeHiddenFieldjest wywoływana na końcu metody encodeEndQ. Znaczenie ukrytego ijego wartości wyjaśniono przy okazji omawiania metody decode.

/ / pola

private void encodeHiddenField(FacesContext context, ResponseWriter writer, UlComponent component) throws java io.IOException { //

Zapisuje pole ukryte, którego nazwa odpowiada identyfikatorowi klienta.

w riter.startElement("input", component); writer.writeAttributeC'type", "hidden", null); wri te r .wri teAttri bute("name", component.getCli entId(context), nul1); writer.endElement("input"); }

I I Metoda encodeTab (wywoływana z poziomu metody encodeChildren) koduje element kotwicyjęzyka / / HTML z atrybutem onclick, który wymusza ustawianie wartości pola ukrytego zakodowanego przez I I metodę encodeHiddenField i wysłanie na serwer całegoformularza. Więcej informacji na temat / / samego pola ukrytego można znaleźć w komentarzu poświęconym metodzie decode. Metoda encodeTab / / dodatkowo zapisuje atrybut class dla poszczególnych zakładek; wartość tego atrybutu wskazuje albo I I na atrybut tabClass (wprzypadku niezaznaczonych zakładek), albo na atrybut selectedTabClass // (wprzypadku zakładki zaznaczonej). private void encodeTab(FacesContext context, ResponseWriter writer, Selectltem item, UlComponent component) throws java.io .IOException { String tabText = getLocalizedTabText(component,item.getLabel()); String content = (String) item.getValue();

386

Rozdział 9. ■ Niestandardowe komponenty, konwertery i mechanizmy weryfikujące

JavaServer Faces 176. 177 178 179. 180 181. 182 183 184. 185. 186 187. 188 189 190 191 192 193 194 195. 196 197. 198. 199

writer startElementC'td", component), writer startElement("a", component), writer.writeAttribute("href", "#", "href"); String clientld = component.getClientld(context); String formld = com.corejsf util Renderers,getFormId(context, component); writer.writeAttribute("oncl ic k ", //

Zapisuje wartość pola ukrytego, którego nazwa odpowiada identyfikatorowi klienta. "document forms[’" + formld + + content + " 1: " +

+ clientld + " ’] value=’ "

/ / Wysyłaformularz, do którego należy komponent panelu. "document.forms[ + formld + " 1] .submit(); ", nuli); UITabbedPane tabbedPane = (UITabbedPane) component, String selectedContent = tabbedPane.getContent(), String tabClass = nuli; i f (content.equals(selectedContent)) tabClass = (String) component.getAttributes() ,get("selectedTabClass"), else tabClass = (String) component.getAttributes() ,get("tabClass");

387

232. / / Metoda includePage wykorzystuje mechanizm rozdziału żądań serwletu do 233. / 1 dołączania strony właściwej dla wybranej zakładki. 234. 235 private void includePage(FacesContext fc, UlComponent component) { 236. External Context ec = fc .getExternal Context( ); 237 SenvletContext sc = (ServletContext) ec.getContextO: 238. UITabbedPane tabbedPane = (UITabbedPane) component; 239. String content = tabbedPane,getContent(); 240. 241. ServletRequest request = (ServletRequest) ec.getRequest(), 242. ServletResponse response = (ServletResponse) ec.getResponse(); 243 try { 244. sc.getRequestDispatcher(content).include(request, response); 245. } catch (ServletException ex) { 246 logger.log(Level.WARNING, "Nie można załadować strony. " + content, ex); 247. } catch (IOException ex) { 248. logger.log(Level.WARNING, "Nie można załadować strony " + content, ex), 249. } 250. } 251 }

200

201 202 203. 204. 205. 206. •207 208 209.

i f (tabClass != nul1) writer.writeAttribute("class", tabClass, null); writer.write(tabText); writer.endElementCa"); writer endElementCtd"); wri te r .wr i te (" \n"), I I taki krok poprawia czytelność generowanego kodu HTML-a }

0201482330310023235300312353233253024823 . .

na zakładki

Na rysunku 9.9 (we wcześniejszej części tego rozdziału) przedstawiono przykładową stronę aplikacji tabbedpane. Strukturę katalogów tej aplikacji przedstawiono na rysunku 9.11. Na listingu 9.18 przedstawiono kod strony index.jsp. Na listingach od 9.19 do 9.22 przedsta­ wiono odpowiednio deskryptor biblioteki znaczników, klasę znacznika, plik konfiguracyjny oraz arkusz stylów tej aplikacji.

210. 211. 212

213. 214. 215.

216. 217. 218. 219. 220.

Tekst właściwy dla zakładek naszego komponentu panelu możemy zdefiniować albo z wykorzystaniem kluczy pakietu komunikatów, albo bezpośrednio wformie tekstu wyświetlanego w zakładce. Na podstawie I / tego tekstu metoda getLocalizedTabText próbuje odnaleźć odpowiednią wartość w pakiecie komunikatów / / wskazanym za pośrednictwem atrybutu resourceBundle znacznika . Jeśli próba I I odnalezienia tej wartości zakończy się niepowodzeniem, metoda getLocalizedTabText zwraca łańcuch, I I który przekazano najej wejściu. //

Rysunek 9.11.

//

Struktura katalogów aplikacji ilustrującej działanie komponentu panelu podzielonego na zakładki

private String getLocalizedTabText(UIComponent tabbedPane, String key) { String bundle = (String) tabbedPane.getAttributes() .get("resourceBundle"), String localizedText = null;

f j

. Q MANIFEST.MF w e b - in f 11| 3 classes f ■(¡3 com f £ 3 corejsf ~mSi Util 1L 1

i f (bundle != nul1) { localizedText = com.corejsf.util.Messages.getString(bundle, key, null); } i f (localizedText == null) localizedText = key; / / Jeśli parametr key nie reprezentuje istniejącego klucza wpakiecie komunikatów, / / zwracamy łańcuch wejściowy w niezmienionej formie. return localizedText; }

j

i-- Q

:

Q

Messages.class Renderers. class

;..Q TabbedPaneRenderer. class ..D TabbedPane!ag.class Q ¡~

221.

222. 223. 224. 225. 226. 227 . 228. 229. 230 231.

¡ 3 tabbedpane.w ar f E 3 META-tNF

UITabbedPane.class

Q messages, properties

Q tabbedpane.tld Q faces-conflg.xm l

!. Q web.xm l f- | 3 images ¡~

Q jefferson.jpg

r D lincoln.jpg - D rooseveit.jpg L Q Washington,jpg Q stytes.css !"- E j index.html Q index.jsp

388

JavaServer Faces

Listing 9.18. Kodstrony tabbedpane/web/index.jsp 1. 2. taglib uri="http://java.sun.com/jsf/core" prefix="f" %> 3. 4. taglib uri="http://corejsf.com/tabbedpane" prefix="corejsf" %> 5. 6. 7. 8. 9. 10. 11. 12. 15. 16. 17. 18. 20. 21. 22. 23. 24. 25. 27. 28. 29. 30. 31. 32. 34. 35. 36. 37. 38. 39. 41. 42. 43. 44.

Klasa obsługująca znacznik, którą musimy sami zaimplementować, odpowiada za realizację trzech zadań: 1.

Wskazanie klasy konwertera.

2. Odczyt atrybutów znacznika. 3. Konfigurację obiektu konwertera z wykorzystaniem odczytanych atrybutów. W przypadku konwertera klasa odpowiedzialna za obsługę znacznika powinna dziedziczyć po klasie ConverterELTag. Jak się niedługo Czytelnicy przekonają, klasy obsługujące znaczniki niestandardowych mechanizmów weryfikacji muszą rozszerzać klasę bazową Val i datorELTag. | W starszych wersjach technologii JSF musieliśmy tworzyć podklasy klas Conver ►terTag i ValidatorTag. Począwszy od wersji JSF 1.2, stosowanie obu tych klas bazowych nie jest zalecane. Nasza klasa obsługująca znacznik musi definiować po jednej metodzie ustawiającej dla każ­ dego atrybutu znacznika. Przykład takiej metody przedstawiono poniżej: public class ConveaOeditCardTag extends ConverterELTag { private ValueExpression separator, public void setSeparaior(VaiueExpression newValue) j separator = newVaiue. }

Rozdział 9. ■ Niestandardowe Komponent», konwertery i mechanizmy weryfikujące ELContext el Context = FacesContext.getCurrentlnstance!) .getELContext(); converter setSeparator((String) separator.getValue(elContext)); return converter;

} Nasza metoda ustawia właściwość separator klasy konwertera CreditCardConverter. I wreszcie dla każdej klasy obsługującej znacznik musimy zdefiniować metodę release, która odpowiada za przywrócenie oryginalnych (domyślnych) wartości we wszystkich polach egzemplarza: public void release!) { separator = n u ll;

} Kompletny kod klasy znacznika przedstawiono na listingu 9.24.

Listing 9.24. Kod klasy custom-converter/src/java/com/corejsf/CreditCardConverterTag.java_________ 1. package com.corejsf, 2.

3. 4. 5. 6 7. 8. 9. 10. 11.

import import import import import import

ja vax.el.ELContext: javax.el.ValueExpression, javax.faces.context.FacesContext, javax.faces.convert.Converter; javax.faces.webapp.ConverterELTag; javax.servlet.jsp.JspException;

public class CreditCardConverterTag extends ConverterELTag { private ValueExpression separator;

12.

13. 14. 15. 16 17 18 19 20 21

public void setSeparator(ValueExpression newValue) { separator = newValue; }

22

}

23 24 25 26 27

public voidrelease!) separator = nul 1. f

public ConvertercreateConverter!) throws JspException { CreditCardConverter converter = new CreditCardConve rte r(). ELContext elContext =FacesContextget Current Instance!) .get El Context!). converter setSeparator((String)separatorgetValue!elContext)), return converter.

{

}

flU javax.faces.webapp.ConverterELTag (JSF 1.2) Aby skonfigurować egzemplarz konwertera z odpowiednimi atrybutami znacznika, należy nadpisać metodę createC o n verter. W ciele tej metody powinniśmy skonstruować obiekt konwertera i przypisać jego właściwościom atrybuty znacznika: public Converter createConverter() throws JspException { CreditCardConverter converter = new CreditCardConverter()

395

■ protected void createConverter!) Należy tę metodę nadpisać, jeśli chcemy utworzyć własny konwerter i uzależnić jego działanie od wartości atrybutów znacznika.

396

Rozdział 9. ■ Niestandardowe komponenty, konwertery i mechanizmy weryfikujące

JavaServer Faces ■

p u b lic void re sLo re Sta te(Fa cesC o n te x t con tex t. Object s t a t e ) O b je c t[ ] values = ( O b je ct[ ] ) s ta te . d a te S ty le = (S t r in g ) v a 1ues[0 ].

void releasee)

Zeruje stan danego znacznika, aby można go byio ponownie użyć.

397

{

lo c a le = (L o c a le ) v a lu e s [1]. p a tte rn = ( S t r in g ) va 1ues[2 ]

BBH javax.el.ValueExpression (JSF 1.2)

tim e S ty le = (S t r in g ) v a 1u e s [3 ]. timeZone = (TimeZone) valuesT4 ]. type = (S t r in g ) v a lu e s [5 ].

■ Object getValue(ELContext context)

Zwraca bieżącą wartość danego wyrażenia reprezentującego wartość.

BHB javax.faces.context.FacesContext (JSF 1.0) ■

Co więcej, interfejs StateHolder dodatkowo wymaga od programisty dodania właściwości transient. Jeśli ustawimy tę właściwość, dany obiekt nie zostanie zapisany. Właściwość transient jest odpowiednikiem słowa kluczowego transient stosowanego w mechanizmie serializacji Javy:

ELContext getELContext () (JS F 1.2)

Zwraca kontekst przetwarzania wyrażeń w danym języku wyrażeń.

p u b lic c la s s DateTmieConverter implements C onverter. StateH o ld e r {

Zapisywanie i przywracanie stanu

p r iv a te boolean tra n s ie n tF la g . // "transient" jest słowem zastrzeżonym p u b lic boolean i s Tra n s ie n t ( ) { retu rn tra n s ie n tF la g . }

Implementując konwertery i mechanizmy weryfikujące, musimy wybrać jedno z dwóch roz­ wiązań w zakresie zapisywania stanu. Prostszym wyjściem jest zdefiniowanie klasy konwertu­ jącej lub weryfikującej jako struktury serializowanej — wystarczy zaimplementować interfejs S e ria liz a b le i stosować się do typowych reguł serializacji w Javie. W przypadku konwertera numerów kart kredytowych mamy do czynienia z pojedynczym polem obiektu, który jest typu String, czyli typu podlegającego serializacji. W tej sytuacji musimy tylko zaimplementować interfejs Serial izable: pu blic c la s s C redu C ard C o nverter

implements C onverter. S e r ia l iz a b le {

}

Drugie rozwiązanie polega na opracowaniu konstruktora domyślnego i implementacji inter­ fejsu StateHolder. Takie podejście wymaga co prawda większego zaangażowania ze strony programisty, ale leż gwarantuje większą efektywność kodowania stanu obiektu. Z drugiej strony, w przypadku niewielkich obiektów (np. konwertera numerów kart kredytowych) lepszym wyjściem jest użycie standardowego mechanizmu serializacji. Kompletność naszych rozważań wymaga opisania interesującej nas techniki na przykładzie standardowego konwertera DateTimeConverter. W metodzie saveState interfejsu StateHolder konstruujemy obiekt serializowany, którego zadaniem jest opisywanie pola egzemplarza. Naturalnym wyborem jest tablica obiektów składująca w poszczególnych elementach reprezentowane pola egzemplarza. W metodzie restoreState odtwarzamy pola egzemplarza na podstawie obiektów tej tablicy. public class DateTimeConverter implements Converter, StateHolder { public Object saveState(FacesContext context) { Objectf] values = new 0bject[6]: valuesEO] = dateStyle; valuesEl] = locale: va1ues[2] = pattern, va1ues[3] = timeStyle, va J ues[4] = timeZone: va1ues[5] = type: return values:

p u b lic void s e tT ra n s ie n t( boolean newValue) { tra n s ie n tF la g = newValue.

}

Konwertery, mechanizmy weryfikacji i obiekty nasłuchujące zdarzeń, które nie imple­ mentują ani interfejsu Serializable, ani interfejsu StateHolder, są pomijane w procesie zapisywania widoku.

0

Oto prosty eksperyment, który jasno dowodzi, że konwertery powinny zapisywać " J swój stan. W pierwszej kolejności konfigurujemy aplikację custom-converter (w pliku ■ t i!'

lllillls . client

Z kodu kiasy CreditCardConverter należy teraz usunąć (lub objąć komentarzem) kon­ strukcję odwołującą się do interfejsu Serializable. W kolejnym kroku umieszczamy nowy przycisk na stronie result jsp: r Q faces-config.xml j Q web. xml | D styles.css

r D index.html | D index.jsp D result.jsp_____________________________

Kod strony result.jsp przedstawiono na listingu 9.25. Kod zmodyfikowanego konwertera i pliku konfiguracyjnego przedstawiono odpowiednio na listingach 9.26 i 9.27.

Listing 9.25. Kod strony custom-converter/web/result.jsp 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27 28. 29. 30. 31. 32. 33.

< /htm l>

Listing 9.26. Kod klasy custom-converter/src/java/com/corejsf/CreditCardConverter.Java

meta-inf

1. package com.corejsf; 2.

3 4. 5. 6. 7. 8. 9 10 11

import java io.Serializable, import import import import

javax.faces.component.UlComponent, javax.faces.context.FacesContext; javax.faces.convert.Converter, javax.faces.convert.ConverterException;

public class CreditCardConverter implements Converter, Serializable { private String separator,

12.

13. 14. 15 16. 17 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31 32. 33. 34. 35 36 37 . 38. 39.

40 . 41. 42. 43. 44 45. 46. 47. 48. 49. 50. 51. 52. 53. 54. 55.

// Właściwość separator: public void setSeparator(String newValue) { separator = newValue; } public Object getAsObject! FacesContext context, UlComponent component, String newValue) throws ConverterException { StringBuiIder builder = new StringBuilder(newValue); int i = 0; while (i < builder.length!)) { i f (Character.isDigit(buiIder charAt(i))) i ++; else buiIder.deleteCharAt(i), } return new CreditCard(builder.toStringO); } public String getAsString! FacesContext context, UlComponent component, Object value) throws ConverterException { / / Długość

13: xxxx xxx xxx xxx

/ / Długość 14: xxxxx xxxx xxxxx / / Długość 15: xxxx xxxxxx xxxxx

/ / Długość 16: xxxx xxxx xxxx xxxx / / Długość 22: xxxxxx xxxxxxxx xxxxxxxx i f ( ! (value instanceof CreditCard)) throw new ConverterException!); String v = ( (CreditCard) value).toString!); String sep = separator; i f (sep == null) sep = " "; in t[] boundaries = null; int length = v.length!); i f (length == 13) boundaries = new in t[] { 4, 7, 10 }, else i f (length == 14) boundaries = new in t[] { 5, 9 }; else i f (length == 15) boundaries = new in t[] { 4 , 10 }; else i f (length == 16)

399

400

JavaServerFaces 56. 57. 58. 59. 60. 61. 62. 63. 64 65. 66. 67. 68 . 69. 70. 71. 72 }

boundaries = new in t[] { 4 , 8, 12 }, else i f (length == 22) boundaries = new in t[] { 6 , 14 }, else return v, StringBuilder result = new StringBuilderO. int start = 0; for (int i = 0; i < boundaries length; i++) { int end = boundaries[ i ], result.append(v.substnng(start,end)); result.append(sep); start = end;

} result,append(v.substring(start)), return result.toString(); }

Listing 9.27. Plik konfiguracyjny custom-converter/web/WEB-INF/faces-config.xml 1. 2. 7. 8. /index.jsp 9 10. process 11. /result.jsp 12. 13. 14. 15. 16. /result.jsp 17. 18. back 19. /index.jsp 20. 21. 22. 23. 24. com.corejsf.Credi tCard 25. com.corejsf CreditCardConverter 26. 27. 28. 29 com.corejsf.CreditCard 30. com.corejsf.CreditCardConverter 31. 32. 33. 34. payment 35. com.corejsf.PaymentBean 36. session 37.

Rozdział 9. ■ Niestandardowe komponenty, konwertery i mechanizmy weryfikujące

401

38. 39. 40. 41. com.corejsf.messages 42. msgs 43. 44 45.

Znaczniki niestandardowych mechanizmów weryfikacji W poprzednich punktach omówiono sposób implementacji konwertera niestandardowego zapewniającego autorom stron JS F wygodę porównywalną z wygodą stosowania standar­ dowych znaczników JS F . W tym punkcie skoncentrujemy się na technice konstruowania niestandardowych mechanizmów weryfikacji. Kroki składające się na proces konstruowania niestandardowego mechanizmu weryfikacji są niemal identyczne jak w przypadku niestandardowych konwerterów: 1.

Opracowanie pliku TFD i odwołanie się do tego deskryptora w kodzie stron JSF.

2. Implementacja klasy odpowiedzialnej za obsługę znacznika. Klasa znacznika musi rozszerzać klasę bazową Val i datorELTag, uzyskiwać atrybuty zadeklarowane w pliku deskryptora TFD i przekazywać je do obiektu weryfikującego.

3. Implementacja klasy mechanizmu weryfikującego. Klasa weryfikująca musi implementować interfejs Val i dator. Metoda val i date powinna pracować w standardowy sposób i generować wyjątek Val idatorException w razie wykrycia błędu. Aby stworzyć możliwość zapisywania i odtwarzania stanu obiektów weryfikujących, należy dodatkowo zaimplementować interfejs Serial izable lub StateHolder. Wróćmy teraz do przykładu mechanizmu weryfikującego numery kart kredytowych (patrz listing 9.27). Każdy numer karty kredytowej chcemy poddawać trzem testom: 1.

Sprawdzającemu, czy użytkownik podał jakąś wartość.

2. Sprawdzającemu, czy podany numer jest zgodny ze wzorem Fuhna. 3. Sprawdzającemu, czy podany numer rozpoczyna się od prawidłowego przedrostka. Przedrostek numeru karty kredytowej określa jej rodzaj — na przykład przedrostki z prze­ działu od 51 do 55 zarezerwowano dla kart MasterCard, natomiast przedrostek 4 jest stosowany dla kart Visa. Moglibyśmy oczywiście opracować własny kod realizujący to konkretne zadanie, jednak zdecydowaliśmy się na implementację bardziej uniwersalnego (i bardziej przydatnego) mechanizmu weryfikującego dowolne wyrażenia regularne. Opisywany mechanizm weryfikacji można zastosować w następujący sposób: 33. 34. 35 36. 37. 38.

50. 51. 52. com.corejsf,messages 53. msgs 54 55. 56.

Listing 9.31. Deskryptor custom-validator/web/WEB-INF/validator.tld 1. 2. 7. l.l 8 validator 9. http://corejsf.com/validator 10. 11. validateRegex 12. com.corejsf.RegexValidatorTag 13. empty 14. 15. expression 16. 17. j ava.1ang.String 18. 19. 20. 21. errorSummary 22. 23. java.1ang.Stri ng 24. 25 26 27 enrorDeiail 28 29 java lang.Strmg 30 31 32 33

Listing 9.32. Kod strony custom-validator/web/index.jsp 1 chtml> 2 3 taglib ur~i ="hUp://java.sun.com/jsf/iuml" prefix-"h" °^> 4 5 6 7 8 9. 10

407

Wiem y już, jak implementować znaczniki niestandardowe dla naszych komponentów, kon­ werterów i mechanizmów weryfikacji. Omówiliśmy wszystkie kluczowe aspekty i problemy, przed którymi staje programista konstruujący własne znaczniki. Kod przedstawiony w tym rozdziale powinien stanowić dobry punkt wyjścia dla Czytelników zainteresowanych własnymi eksperymentami.

UJ

ja vax.faces.webapp.Va1i datorlag

■ void set Va 1idator icKStnng i d) Ustawia identyfikator danego obiektu weryfikującego. Ustawiony identyfikator jest później wykorzystywany do poszukiwania klasy odpowiedzialnej za weryfikację. ■ protected void createValidator() Powinniśmy tę metodę nadpisać, jeśli chcemy uzależnić działanie naszego mechanizmu weryfikacji od wartości właściwości przekazywanych za pośrednictwem atrybutów znacznika.

408

JavaServer Faces

10 Usługi zewnętrzne W tym rozdziale omówimy techniki uzyskiwania dostępu do usług zewnętrznych z pozio­ mu aplikacji JavaServer Faces. Przeanalizujemy sposoby nawiązywania połączeń z bazami danych, usługami katalogowymi i usługami sieciowymi. Skoncentrujemy się przede wszystkim na metodach izolowania logiki aplikacji od konfiguracji zasobów.

Dostęp do bazy danych za pośrednictwem interfejsu JDBC W poniższych punktach dokonamy krótkiego przeglądu interfejsu A P I technologii JD B C (od ang. Java Database Connectivity). Przyjmujemy, że Czytelnik dysponuje podstawową wiedzą o poleceniach języka SQ L (od ang. Structured Query Language). Szczegółowe wprowadzenie w świat interfejsu JD B C i języka SQ L można znaleźć w rozdziale 4. książki Core Java™ 2, vol. 21 autorstwa Caya Horstmanna i Gary’ego Cornelia. Dla ułatwienia nie­ zbędne podstawy omówiono także poniżej.

Wykonywanie wyrażeń języka SQL Aby wykonywać wyrażenia języka SQ L na bazie danych, musimy dysponować obiektem połączenia. Tego rodzaju obiekty można uzyskiwać na wiele różnych sposobów. Najbardziej eleganckim jest użycie źródła danych (ang. data source ): DataSource source = . . Connection conn = source.getConnection();

1 Polskie wydanie: Java 2. Techniki zaawansowane. Wydanie //, Helion, 2005 — przyp. tłum.

410

RozdziaMO. ■ Usługi zewnętrzne

JavaServer Faces W punkcie „Uzyskiwanie dostępu do zasobów zarządzanych przez kontener” omówimy techniki uzyskiwania źródeł danych w ramach kontenerów GlassFish i Tomcat. Na razie przyjmujemy, że źródło danych zostało prawidłowo skonfigurowane i że możemy od razu przystąpić do nawiązywania połączenia z odpowiednią bazą danych. Po utworzeniu obiektu klasy Connection należy skonstruować obiekt klasy Statement, który będzie nam potrzebny do przekazywania wyrażeń języka SQ L do bazy danych. Dla wyrażeń SQL-a aktualizujących bazę danych należy stosować metodę executeUpdate, natomiast zapytania zwracające zbiory wynikowe wymagają stosowania metody executeQuery. Statement stat = conn.createStatementO, s ta t.executeUpdate(" INSERT INTO Users VALUES ( 'troosevelt', ’jabberwock1)"). ResultSet result = stat.executeQuery!"SELECT * FROM Users");

Klasa ResultSet oferuje dość nietypowy protokół przetwarzania iteracyjnego. W pierwszej kolejności wywołujemy metodę next, aby przenieść kursor do pierwszego wiersza. (Metoda next zwraca wartość fal se, jeśli kolejne wiersze są niedostępne). Musimy następnie wywołać metodę getString, aby uzyskać łańcuch reprezentujący wartość interesującego nas pola. Przykład takiego rozwiązania przedstawiono poniżej; while (resu lt.next!)) { username = result.getStringC'username1') ; password = result.getString!"password");

} Po wykonaniu wszystkich operacji na bazie danych koniecznie należy zamknąć połączenie, z którego korzystaliśmy. Aby mieć pewność, że dane połączenie zostanie zamknięte nieza­ leżnie od okoliczności (a więc także w razie wystąpienia wyjątków), powinniśmy umieścić kod operujący na bazie danych w bloku try-fi nal ly: Connection conn = source.getConnection!); try {

} finally { conn.close!);

} Interfejs JD B C A P I jest oczywiście dużo bardziej rozbudowany, jednak początkowo powyższy materiał podstawowy powinien nam w zupełności wystarczyć. R S | W tym rozdziale skoncentrujemy się na technikach wykonywania wyrażeń języka ■ H SQL z poziomu aplikacji internetowej. Prezentowane podejście zdaje egzamin w przypadku lekkich aplikacji cechujących się stosunkowo niewielkimi wymaganiami w zakresie ilości składowanych danych. Bardziej skomplikowane aplikacje mogą wy­ magać zastosowania jakiejś technologii odwzorowywania obiektowo-relacyjnego, np. JPA (od ang. Java Persistence API) lub Hibernate.

411

Zarządzanie połączeniami Jednym z najtrudniejszych wyzwań, z którymi muszą się mierzyć twórcy aplikacji interne­ towych, jest zarządzanie połączeniami z bazami danych. Musimy właściwie zrównoważyć dwa sprzeczne dążenia. Po pierwsze, otwieranie połączeń z bazą danych jest kosztowne czasowo — nawiązanie połączenia, uwierzytelnienie i pozyskanie niezbędnych zasobów zwykle trwa co najmniej kilka sekund. W tej sytuacji nie możemy sobie pozwolić na otwie­ ranie nowego połączenia dla każdego żądania strony. Z drugiej strony, trudno byłoby utrzymywać ogromną liczbę otwartych połączeń z bazą danych. Połączenia zajmują cenne zasoby (zarówno po stronie programu klienckiego, jak i po stronie serwera bazy danych). Systemy zarządzania bazami danych zwykle ograniczają maksymalną liczbę jednocześnie obsługiwanych połączeń. Oznacza to, że nasza aplikacja nie może po prostu otwierać nowego połączenia za każdym razem, gdy jakiś użytkownik loguje się do systemu, i pozostawiać to połączenie otwarte do czasu wylogowania tego użytkownika. Może się przecież okazać, że nasz użytkownik odejdzie od komputera i nigdy się nie wyloguje. Jednym z najbardziej popularnych rozwiązań tego problemu jest utworzenie puli (ang. pool) połączeń z bazą danych. Pula połączeń zawiera wszystkie aktualnie otwarte połączenia z bazą danych. Aplikacje uzyskują niezbędne połączenia właśnie z tej puli. Niepotrzebne połączenia są niezwłocznie zwracane do puli, ale nie są zamykane. Oznacza to, że pula w znacznym stopniu minimalizuje opóźnienia czasowe związane z ustanawianiem połączeń z bazami danych. Implementacja puli połączeń z bazą danych nie jest łatwa i z pewnością nie powinna należeć do zadań programistów aplikacji. Począwszy od wersji 2.0, interfejs JD B C oferuje dość transparentną obsługę puli połączeń. Okazuje się, że obiekt klasy Corinection uzyskany z puli połączeń jest tak zmodyfikowany, aby jego metoda close powodowała zwrócenie obiektu do puli (zamiast zamknięcia połączenia). Samo konstruowanie i utrzymywanie puli, a także udostępnianie aplikacjom źródła danych, którego metoda getConnecti on zwraca obiekty połączeń z tej puli, należy do serwera aplikacji. Każdy serwer aplikacji cechuje się własnym mechanizmem konfigurowania puli połączeń z bazą danych. Szczegółowe rozwiązania w tym zakresie nie są częścią standardu Javy; tego rodzaju wskazówek próżno szukać nawet w specyfikacji interfejsu JD B C . W kolejnym podrozdziale omówimy sposób konfigurowania pul połączeń w ramach serwerów aplikacji GlassFish i Tomcat. Podstawowe reguły konfiguracji są identyczne w przypadku wszystkich serwerów aplikacji, jednak trudno nie zauważyć istotnych różnic w kwestiach szczegółowych. Kluczem do efektywnego utrzymywania puli jest niezwłoczne „zamykanie” wszystkich nie­ potrzebnych obiektów połączeń. W przeciwnym razie pula połączeń błyskawicznie wyczer­ pałaby się, co zmusiłoby nas do otwierania nowych fizycznych połączeń z bazą danych. Problemem prawidłowego zamykania połączeń zajmiemy się w kolejnym punkcie.

Eliminowanie wycieków połączeń Przeanalizujmy teraz prostą sekwencję wyrażeń: DataSource source = ... Connection corin = source.getConnection();

412

Rozdział 10. ■ Usługi zewnętrzne

JavaServer Faces Statement stat = conn.createStatement(); String command = "INSERT INTO Users VALUES ( ' troosevelt', ' jabberwock')"; stat.executeUpdate!command); conn.closeO;

Przedstawiony kod wygląda na zupełnie poprawny — otwieramy połączenie, wykonujemy proste polecenie, po czym niezwłocznie zamykamy niepotrzebne połączenie. Okazuje się jednak, że mamy tutaj do czynienia z fatalnym niedopatrzeniem. Jeśli któraś z wywoływanych metod doprowadzi do wygenerowania jakiegoś wyjątku, metoda close nigdy nie zostanie

//

413

zaleca się stosowanie odrębnych bloków try

try { Connection conn = source.getConnection!); try { Statement stat = conn.createStatementO; String command = "INSERT INTO Users VALUES ('troosevelt', 'jabberwock')"; stat.executeUpdate(command);

}

fin a lly { conn.closeO;

}

wywołana! } W takim przypadku poirytowany użytkownik może wielokrotnie ponawiać żądanie, każdo­ razowo powodując wyciek jednego obiektu połączenia. Aby uniknąć tego rodzaju sytuacji, zawsze należy umieszczać wywołanie metody close wewnątrz bloku fin a lly : DataSource source = ... Connection conn = source.getConnection(), try { Statement stat = conn.createStatementO; String command = "INSERT INTO Users VALUES ( 'troosevelt', 'jabberwock')"; stat.executeUpdate(command);

catch (SQLException) { //

zapisanie komunikatu o błędzie w dzienniku zdarzeń

} Wewnętrzny blok try gwarantuje, że dane połączenie zostanie zamknięte. Zewnętrzny blok try gwarantuje nam, że ewentualny wyjątek zostanie zarejestrowany w dzienniku zdarzeń. Możemy oczywiście opisać naszą metodę dodatkową konstrukcją throws SQLExcep ■ “ '♦tion i przenieść odpowiedzialność za obsługę ewentualnych wyjątków na metodę wywołującą. W wielu przypadkach właśnie takie rozwiązanie jest najlepsze.

}

fin a lly { conn.close!);

Stosowanie gotowych wyrażeń

} Ten prosty mechanizm całkowicie rozwiązuje problem wyciekających połączeń. Prezentowana reguła jest szczególnie efektywna, jeśli konstrukcji try - fin a lly nie łączymy z żadnym kodem obsługującym wyjątki, a zwłaszcza nie podejmujemy prób przechwytywania wyjątku SQLExcepti on w ramach tego samego bloku try: I I to rozwiązanie NIEjest zalecane Connection conn = n u ll; try { conn = source.getConnection(); Statement stat = conn.createStatementO; String command = "INSERT INTO Users VALUES ( 'troosevelt’ , 'jabberwock')"; stat.executeUpdate(command);

}

catch (SQLException) { //

Do najbardziej popularnych technik optymalizacji aplikacji wykorzystujących interfejs JD B C należy stosowanie klasy PreparedStatement. Wykorzystując gotowe (przygotowane wcześniej) wyrażenia, możemy znacznie przyspieszyć operacje na bazie danych w sytuacji, gdy nasz kod wielokrotnie wykonuje ten sam typ zapytania. Warto przy tej okazji przeanalizować przykład haseł użytkowników. Nasza aplikacja najprawdopodobniej będzie musiała wielokrotnie wykonywać zapytanie w postaci: SELECT password FROM Users WHERE username=...

Takie wyrażenie w pierwszym podejściu wymusza na systemie zarządzania bazą danych prekompilację danego zapytania (czyli przeprowadzenie analizy składniowej wyrażenia języka SQ L i wyznaczenie strategii przetwarzania zapytania). Wszystkie uzyskane w ten sposób informacje są składowane w obiekcie gotowego wyrażenia i wykorzystywane za każdym razem, gdy wykonujemy to zapytanie.

zapisanie komunikatu o błędzie w dzienniku zdarzeń

}

finally { conn.closeO; 11 BŁĄD

} W powyższym kodzie można wskazać dwie drobne usterki. Po pierwsze, jeśli wywołanie metody getConnecti on doprowadzi do wygenerowania jakiegoś wyjątku, zmienna conn nadal będzie miała wartość nuli, zatem nie będziemy mogli wywołać metody close. Co więcej, także samo wywołanie metody close może spowodować wygenerowanie wyjątku SQLExcepti on. Moglibyśmy oczywiście umieścić w klauzuli fi nal ly dodatkowy kod, jednak takie podejście wprowadziłoby sporo zamieszania. Warto w tej sytuacji użyć dwóch odrębnych bloków try:

Obiekt gotowego wyrażenia tworzymy za pomocą metody prepareStatement klasy Connecti on. Poszczególnym parametrom należy przypisać znak ?: PreparedStatement stat = conn.prepareStatement! "SELECT password FROM Users WHERE username=?");

Kiedy już będziemy gotowi do wykonania tak przygotowanego wyrażenia, powinniśmy w pierwszej kolejności ustawić wartości parametrów: stat.setStringd, name);

414

JavaServen Faces (Warto pamiętać, że pierwszy parametr jest reprezentowany przez indeks 1). Możemy teraz wykonać to wyrażenie, stosując standardowy mechanizm; ResultSet result = stat.executeQuery().

Na pierwszy rzut oka wydaje się, że gotowe wyrażenia nie mają zbyt dużego wpływu na efektywność pracy aplikacji internetowych. Musimy mieć na uwadze, że połączenie z naszą bazą danych jest zamykane bezpośrednio po realizacji żądania użytkownika. Każde gotowe wyrażenie jest ściśle związane z konkretnym połączeniem, zatem efekty pracy związane z jego przygotowywaniem tracimy z chwilą przerwania fizycznego połączenia z bazą danych. Jeśli jednak fizyczne połączenia z bazą danych są składowane w puli, nasze gotowe wyrażenia najprawdopodobniej będzie można wykorzystywać wielokrotnie (po ponownym uzyskiwaniu dostępnych połączeń). Wiele współczesnych implementacji puli połączeń oferuje mechanizmy składowania przygotowanych wcześniej wyrażeń w pamięci podręcznej.

Rozdział 10. ■ Usługi zewnętrzne

415

Konfigurowanie zasobów baz danych w ramach serwera GlassFish Serwer GlassFish oferuje wygodny interfejs administracyjny na bazie stron W W W , za po­ średnictwem którego możemy bez trudu skonfigurować źródło danych. Wystarczy wpisać w polu U R L przeglądarki internetowej adres http://localhost:4848 i zalogować się jako ad­ ministrator (domyślną nazwą użytkownika jest admin; domyślnym hasłem jest adminadmin). W pierwszej kolejności należy skonfigurować pulę połączeń z bazą danych. W tym celu musi­ my wybrać element Connection Pools z listy zadań po lewej stronie. Powinniśmy przypisać tworzonej puli nazwę, wybrać dla niej typ zasobów (javax.sql .DataSource) oraz określić właściwy typ systemu zarządzania bazą danych (patrz rysunek 10.1).

j f htrpv/fo'

Kiedy wywołujemy metodę prepareStatement, implementacja puli w pierwszej kolejności przeszukuje pamięć podręczną (bufor) wyrażeń, wykorzystując łańcuch zapytania w roli klucza. Jeśli uda się odnaleźć odpowiednie wyrażenie, zostanie ono wykorzystane ponownie. W prze­ ciwnym razie nowe wyrażenie zostanie utworzone i dodane do pamięci podręcznej.

■ M U . iM

s lii

S u n Ja v a ™ S y s te m

Obiektu klasy PreparedStatement nie można utrzymywać i wielokrotnie wykorzyL I Stywać poza zasięgiem pojedynczego żądania. Po zamknięciu połączenia skła­ dowanego w puli wszystkie związane z nim obiekty klasy PreparedStatement są do tej puli zwracane. Oznacza to, że obiektów klasy PreparedStatement w żadnym razie nie można wykorzystywać poza bieżącym żądaniem. Przeciwnie, powinniśmy wielokrotnie wywoływać metodę prepareStatement dla tego samego łańcucha zapytania, licząc na ponowne wykorzystania istniejącego obiektu wyrażenia.

Nawet jeśli nie interesuje nas problem wydajności, powinniśmy zwrócić uwagę na (K U mechanizm gotowych wyrażeń z innego ważnego powodu — ochrony przed atakami polegającymi na wstrzykiwaniu kodu SQL-a,

Konfigurowanie źródła danych W kolejnych punktach tego podrozdziału omówimy sposoby konfigurowania źródeł danych w ramach serwera aplikacji (na przykładzie serwerów GlassFish i Tomcal), a także techniki uzyskiwania dostępu do tych źródeł z poziomu aplikacji internetowej.

H

S p S ^ lll w

; D o m ain ; clomainl '

Common T a s k s ;

Żadna z tych operacji nie wymaga udziału programisty aplikacji. Rola programisty sprowadza się do żądania obiektów klasy PreparedStalement w nadziei, że przynajmniej w niektórych przypadkach implementacji puli uda się odnaleźć obiekty już istniejące.

:

U s e r: admin

A p p lic a t io n S e r v e r A d m in C o n s o le



Application Server > Resources >. jQ BC ..;> Connection Pools

-BJ Ja va

'

X if 'T

:

Application Sen/ei New

^ S

Applications Enterprise Applications

1 o f 2)

j

a

vVep Applications

, 1

S

EJB Modules

%\

a

Connectc Modules

S

Lifecycle Modules

f !

[~ c ^

1

O

App Client Modules

■\

" indicates r e a p e d noid G e n e r a l S e tt in g s T Nam e: R e s o u rc e Type-

|7avaTsq* Data Source

D a ta b a s e V en do r; [PostgreSQ L" 3

w J | W ee Sen/ices §§ Custom MBeans v ^

C o n n e c tio n P o o l (S t e p

identity the general settings tor the connection pool

a

j

Resources JD BC

P a n

i

|DBC Resources

ifeUr Cancel

1

p

f fln n * r t ia n P n n U ’ Igf

_CaiiFiowRoo'

w|

Rysunek 10.1. Konfiguracja puli połączeń w ramach serwera aplikacji GlassFish Na kolejnej stronie interfejsu administracyjnego możemy zdefiniować opcje tworzonego połączenia z bazą danych, w tym nazwę użytkownika i hasło (patrz rysunek 10.2). W kolejnym kroku należy skonfigurować nowe źródło danych. Nadajemy mu nazwę jdbc/mydb, po czym wybieramy skonfigurowaną przed chwilą pulę połączeń (patrz rysunek 10.3). I wreszcie musimy umieścić plik sterownika bazy danych (w przypadku bazy PostgreSQL będzie to plik o nazwie podobnej do postgresql-X.X-XXX.jdbc3.jar, np.: postgresql-8.2-507. ^ jdbc3.jar - który w trakcie tłumaczenia tej książki byl najnowszym dostępnym sterowni­ kiem) w podkatalogu domains/domainl/¡ib/exl naszej instalacji serwera GlassFish.

416

JavaServer Faces

Rozdziano. ■ usługi zewnętrzne

Rysunek 10.2. Określanie opcji połączenia z bazą danych

417

Konfigurowanie zasobów baz danych w ramach serwera Tomcat ■ H IS KBKSMI U s e r : adinin

S e r v e r : localhost

D o m ain : dom am i

Sun Java™ System A pplication Server Adm in Console

|

Non T r a n s a c t io n a l C o n n e c tio n s :

Application Server

? ffl Applications O

W pierwszej kolejności musimy odnaleźć plik conf/server.xml i zlokalizować w nim element opisujący kontener (maszynę wirtualną) naszej aplikacji internetowej:

T r a n sa c tio n Iso la tio n

Enterprise Applications

^ m O

T r a n s a c t io n Is o la tio n : F~

W eb Applications



EJB Modules



Is o la tio n L e v e l:

33 Connector Modules ffl Lifecycle Modules m

App Client Modules

P ro p e rtie s

1v H, Weo Services



| f Custom MBeans, v

W tym punkcie przeanalizujemy kolejne kroki procesu konfigurowania puli zasobów bazy danych w ramach kontenera Tomcat 5. Czytelnicy, którzy nie korzystają z serwera Tomcat, mogą ten punkt pominąć.

Additional Properties (7)

Resources |DBC ■w

V a lu e

jDBC R eso u 'ce s H

0

|dDC/_T.merP0ol

jpoa^jmbe'

iabc/_CailP'ow Roo

¡Password

icnx/_detauit

Wewnątrz tego elementu należy umieścić podelement De faul LContext określający zarówno szczegółowe ustawienia bazy danych (sterownik, adres U R L oraz nazwę i hasło użytkownika), jak i docelową specyfikację definiowanej puli.

[prepare Threshold

23 C o n n e c tio n P o o ls

9

_CailF'owPooi

0

_ I,m e r P 00 i

|Sefÿer\ame [Logiprirrieout

91 DeroyPooi 0

usei

PostqresPoo'

[databaseNarne"

|MS Resources 113 javaMa" Sess'ons

£ $

$ iSâ jND>

iffil Connęctors____________I Zakończono

Rysunek 10.3. Konfigurowanie źródła danych

n Seiver Platform Edition 5 0 Admin Consoie - Mo itfa Firefox

I Q r'ttp.//iocai1'io5t;4848/a>aan''in/aami>-'gui/TopFrame5et

Poniżej przedstawiono dość typowy przykład opisujący pulę połączeń z bazą danych PostgreSQL. Wartości, które wymagają od nas dostosowania do potrzeb konkretnej puli, wyróż­ niono pogrubieniem: taglib u n = "h ttD . //Java Sun com/jsf/n tm i " p re fix - "h " %>

4



5



67.

< t it le > < h : o u t p u t T e x t

v a lu e = " # {m s g s .t it le }" / > < / t it le >

424

Rozdziano. ■ Usługi zewnętrzne

JavaServer Faces 8

9.

10. 11 12 13 . 14. 15 16. 17. 18 19. 20. 21 22.





.--r:ñ080/tdap/index:.face ¡ »

¡W g ftf

i® 1

Opisaną aplikację opracowano w trzech wersjach — z danymi konfiguracyjnymi zdefiniowa­ nymi odpowiednio w pliku faces-config.xml, w pliku web.xml oraz na serwerze aplikacji. Wszystkie trzy wersje składają się z identycznych stron internetowych, których kod przed­ stawiono na listingach od 10.10 do 10.13. (Kod tych stron przedstawiono oczywiście tylko raz). Najważniejszą różnicą dzielącą te wersje jest sposób implementacji metody getRootContext klasy UserBean (patrz listing 10.14).

Uwierzytelnianie zakończone pomyślnie 1 'Vitai'»svdf-'wniku Herl'"n ®

fioov-i 1

1 Abv zdt.pralizowcii swoie lane osobowe lub zuuenir hasłt . kJbknu przvcisP «ktualizui1 r%tuas!i2y, 1 ! Vvytoyuj j

Listing 10.10. Kod strony ldap/web/index.jsp 1

I Zakończono

? Iü3 META-INF ! Q MANIFEST.MF f ¿3 WEB-INF

Struktura katalogów naszej przykładowej aplikacji

|

?

| | l |

( ¡3 classes

f E3 com $ [¡3 corejsf

! i

j- Q Nam e.class

|

j

T- Q UserBean. class

\

\

| " Q UserDirectoryBean. class

| |

; i

; L-

L Q m essages.properties Q face s-co n fig .x m l Q w eb . xm l

| - D index.htm l

\

Q

in d e x .jsp

j Q intern alE rror.jsp j-- Q loginError.jsp I

D signup.jsp

!' D sig.nupError.jsp j - D u p d a te .js p I



2



3

3. taglib uri="http7/java.sun.com/jsf/html" prefix="h" %> 4. 5. 6. 7. 8. 9.

Cl

1. 2. taglib uri="http://java.sun.com/jsf/core" prefix="f" %> 3. taglib uri="http://java.sun.com/jsf/html" prefix="h" %> 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. ! 14. 15. 16. 32. 33 34. 35. 36.

10 11 12. 13 < /p> 14. 15. 16. 17. 18. 19. 20. 21.

Listing 10.14. Kod klasy Idap/src/java/com/corejsf/UserBean.java package com.corejsf; import java.util.logging.Level; import java.util.logging.Logger; import javax.el ELContext; import javax.el.ExpressioriFactory; import javax.el.ValueExpression; import javax.faces.application.Application; 10 import javax.faces.context.FacesContext; 11 import javax.naming.NameNotFoundException; 12 import javax.nami ng.Nami ngExcepti on; 13 import javax.nami ng.di rectory.Attri butes; 14 import javax.naming.directory.BasicAttributes; 15 i mport javax.nami ng.di rectory.Di rContext; 16 17 public class UserBean { 18 private Name name; 19 private String id; 20 private String email; 21 private String password; 22 private Logger logger = Logger.getLogger("com.corejava"); 23 24 public UserBeanO { name = new NameO; } 25 26 public DirContext getRootContext() throws NamingException { 27 FacesContext context = FacesContext.getCurrentInstance(); 28 ELContext el Context = context,getELContext(); 29 Application app = context.getApplication(); 30 ExpressionFactory factory = app.getExpressionFactory(); 31 ValueExpression ex = factory.createValueExpression(elContext, 32 "#{userdir}", UserDirectoryBean.class); 33 UserDirectoryBean dir = 34 (UserDirectoryBean) ex.getValue(elContext); 35 return dir,getRootContext(); 36 37 38 public Name getNameO { return name; } 39 public void setName(Name newValue) { name = newValue; 40 41 public String getEmaiK) { return email; } 42 public void setEmail(String newValue) { email = newValue;

449

450

Rozdział 10. ■ Usługi zewnętrzne

JavaServerFaces 43. 44 45. 46. 47 48 49 50 51 52 53 54 55 56 57 58 59 60. 61 62 63 64. 65 66 67. 68 69. 70. 71. 72. 73. 74 75 76 77. 78. 79 80 81. 82. 83. 84. 85.

101

id = newValue; }

public String getPassword() { return password, } public void setPassword(String newValue) { password = newValue; } public String loginO { try { DirContext context =getRootContext(); try { String dn = "uid=" + id + " ,ou=people,dc=corejsf,dc=com"; Attributes userAttributes = context.getAttributes(dn); String cn = (String) userAttributes.get("cn") ,get(); name.parse(cn); email = (String) userAttributes.get("mai1 "),get(); byte[] pw = (byte[]) userAttributes.getC'userPassword"),get(); i f (password.equals(new String(pw))) return "login_success"; else return "login_fai1ure", } finally { context.closeO; } } catch(NamingException ex) { logger.log(Level.SEVERE, "loginAction", ex); return "login_error"; } } public String signupO { try { DirContext context = getRootContext(); try { String dn = "uid=" + id + " ,ou=people,dc=corejsf,dc=com"; try { context.lookup(dn); return "signup_fai1ure"; } catch (NameNotFoundException ex) {}

86

87. 88. 89. 90. 91. 92. 93. 94. 95. 96. 97. 98. 99.

logger.log(Level.SEVERE, "loginAction", ex); return "signup_error";

100.

public String getldO { return id; } public void setId(String newValue) {

Attributes attrs = new BasicAttributesO; attrs.put("objectClass", "inetOrgPerson"); attrs.putC'uid", id); attrs.putC'sn", name.getLast()); attrs.putC'cn", name.toString()); attrs.put( "mai1", email); attrs.put( "userPassword", password,getBytes()); context,createSubcontext(dn, attrs); } finally { context.close(); } } catch (NamingException ex) {

451

102.

}

103. 104. 105. 106. 107. 108 109.

return "signup_success"; }

110.

111. 112

113. 114. 115. 116. 117. 118. 119. 120. 121. 122.

123 124. 125. 126. 127. 128. 129. 130. 131. 132. 133. 134. 135. }

public String updateO { try { DirContext context = getRootContext(), try { String dn = "uid=" + id + " ,ou=people,dc=corejsf,dc=com"; Attributes attrs = new BasicAttributesO, attrs.putC'sn", name.getLast()); attrs. put ("cn", name.toStringO); a ttrs.put( "mai1", email); attrs.put("userPassword", password.getBytes()); context.modi fyAttributes(dn, DirContext.REPLACE_ATTRIBUTE, a ttrs ); } finally { context .closeO; } } catch (NamingException ex) { logger.log(Level.SEVERE, "updateAction", ex), return "internal_error"; } return "update_success"; } public String logout0 { password = return "logout_success"; }

Pierwsza aplikacja składa się między innymi z klasy UserDi rectoryBean (patrz listing 10.15) skonfigurowanej w pliku faces-config.xml (patrz listing 10.16). Druga aplikacja przeszukuje parametry inicjalizacji serwletu w trybie ad hoc. Trzecia wersja wykorzystuje technikę wstrzykiwania zasobów (z użyciem klasy przedstawionej na listingu 10.9 — patrz poprzedni punkt tego podrozdziału). I wreszcie, dla kompletności naszych rozważań, na listingu 10.17 przedstawiono kod źródłowy klasy Name wykorzystywanej przez klasę komponentu UserBean.

Listing 10.15. Kod klasy ldap/src/java/com/corejsf/UserDirectoryBean.java 1. 2. 3. 4. 5. 6. 7. 8. 9. 10.

package com.corejsf; import import import import import

java.util.Hashtable; javax.naming.Context; javax.naming.NamingException; javax.naming.directory.DirContext; javax.naming.directory.InitialDirContext;

public class UserDirectoryBean { private String u r l;

452

Rozdziano. ■ usługi zewnętrzne

JavaServer Faces 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22 23. 24. 25. 26. 27. 28. 29. 30.

private String managerDN; private String managerPW, public void setManagerDN(StringnewValue) {managerDN = newValue, } public void setManagerPassword(StringnewValue) { managerPW = newValue: } public void setURL(String newValue) { url = newValue: } public DirContext getRootContext() throws NamingException { Hashtable env = new Hashtable(): env.put(Context.SECURITY_PRINCIPAL. managerDN), env.put(Context.SECURITY_CREDENTIALS, managerPW), DirContext in itia l = new InitialDirContext(env), Object obj = in it ia l.1ookupCurl); i f ( ! (obj instanceof DirContext)) throw new NamingExceptionC'Brak kontekstu"); return (DirContext) obj: } }

Listing 10.16. Plik konfiguracyjny Idap/web/WEB-INF/faces-config.xml 1. 2. 7. 8. /index.jsp 9. 10. logi n_success 11. /welcome.jsp 12. 13. 14. logi n_error 15. /loginError.jsp 16. 17. 18. login_failure 19. /loginError.jsp 20. 21. 22. 23. /signup.jsp 24. 25. signup_success 26. /welcome.jsp 27. 28. 29. signup_failure 30. /signupError.jsp 31. 32. 33. signup_error 34. /signupError.jsp

35. 36. 37. 38 39. 40. 41. 42. 43. 44. 45. 46. 47 48 49 50. 51. 52. 53. 54 55. 56. 57. 58 59. 60. 61. 62. 63 64. 65. 66. 67 68 69. 70. 71. 72 73. 74. 75. 76 77 78 79 80. 81 82. 83. 84 85. 86. 87. 88. 89. 90 91

si gnup_cancel /i ndex.jsp /welcome.j sp update /update.jsp logout_success /index.jsp /update.jsp update_success /welcome.jsp update_cancel /welcome.jsp logi n /index.jsp si gnup /si griup.jsp internal_error /internalError.jsp user com.corejsf,UserBean session userdir com.corejsf.UserDirectoryBean application URL ldap://I ocal host:389

453

454

JavaServer Faces 92 93. 94 95 96 97. 98 99. 100

managerDN cn=Manager,dc=corejsf,dc=com managerPassword secret

Rozdziano. ■ usługi zewnętrzne 38. 39. 40. 41. 42 43. 44. 45. 46. }

455

builder.append(’ ' ) , i f (middle lengthO > 0) { builder.append(middle.charAt(O)); builder.appendO. "); } buiIder.append(last): return builder.toString(), }

1 01

102 103 104. com.corejsf.messages 105. msgs 106. 107 108.

Listing 10.17. Kod klasy Idap/src/java/com/corejsf/Name.java 1 package com.corejsf; 2 3 public class Name { 4 private String first; 5 private String middle; 6 private String last; 7 8 public NameO { firs t = middle = last = ""; } 9 10 public String getFirstO { return first, } 11 public void setFirst(String newValue) { firs t = newValue; } 12 public String getMiddleO { return middle: } 13 public void setMiddle(String newValue) { middle = newValue, 14 public String getLastO { return last; } 15 public void setLast(String newValue) { last = newValue; } 16 17 public void parse(String full Name) { 18 int firstSpace = full Name indexOf(' '), 19 int lastSpace = fulIName.Iastlndex0f(' '); 20 i f (firstSpace == -1) { 21. firs t = 22 middle = 23. last = fulIName, 24. 25. else 26 firs t = full Name, substrings, firstSpace); 27. i f (firstSpace < lastSpace) 28 middle = full Name.substring(firstSpace + 1, lastSpace), 29. else 30 middle = 31. last = ful IName.substring(lastSpace + 1, ful 1Name. lengthO); 32 33. 34 35 public String toStringO { 36 StringBuilder builder = new StringBuilderO; 37. builder.append(fi r s t );

Uwierzytelnianie i autoryzacja zarządzana przez kontener W poprzednich podrozdziałach koncentrowaliśmy się na technikach tworzenia aplikacji internetowych wykorzystujących katalog LD A P do lokalizowania danych o użytkownikach. Za właściwe wykorzystywanie tych informacji odpowiadała sama aplikacja, od której zależała decyzja o przyznaniu bądź uniemożliwieniu dostępu do określonych zasobów. W tym pod­ rozdziale omówimy alternatywne podejście polegające na uwierzytelnianiu zarządzanym przez kontener (ang. container-managed authentication). Prezentowany mechanizm przenosi odpowiedzialność za uwierzytelnianie użytkowników na serwer aplikacji. Zachowywanie spójności w procesach gwarantujących bezpieczeństwo danych (we wszystkich komponentach aplikacji internetowej) jest dużo prostsze, jeśli to kontener zarządza uwie­ rzytelnianiem i autoryzacją. Programista aplikacji może się wówczas koncentrować na wła­ ściwym przepływie pracy w ramach tworzonej aplikacji (bez konieczności zajmowania się obsługą przywilejów i ról poszczególnych użytkowników). Większość materiału poświęconego szczegółowym zabiegom konfiguracyjnym opracowa­ no co prawda z myślą o serwerach GlassFish i Tomcat, jednak pozostałe serwery aplikacji oferują podobne rozwiązania. Aby chronić określony zbiór stron internetowych, należy zdefiniować odpowiednie usta­ wienia kontroli dostępu w pliku web.xml. Na przykład poniższa konstrukcja tak ogranicza dostęp do wszystkich stron w folderze protected , aby mogły być przeglądane wyłącznie przez uwierzytelnionych użytkowników z przypisanymi rolami regi stereduser lub i nvi tedguest: /protected/* registereduser i nvitedguest

Rola jest przypisywana użytkownikowi w chw ili uwierzytelniania. Role są składowane w katalogu użytkowników (wraz z ich nazwami i hasłami).

456

JavaServer Faces

Rozdziano. ■ Uskigi zewnętrzne

J e ś li s e r w e r J a v a S e r v e r F a c e s s k o n f ig u r u je m y w t a k i s p o s ó b , a b y d la s t r o n J S F b y ł s to s o w a n y p rz e d ro s te k a d re s ó w

URL do

/faces,

b ę d z ie m y m u s ie li d o d a ć o d p o w ie d n i w z o r z e c

k o n s tr u k c ji z a b e z p ie c z a ją c y c h

k ła d u b y łb y t o w z o r z e c /

(w

p rz y p a d k u

p r z e d s t a w io n e g o

p rz y ­

faces/protected/*).

E S I A b y b e z p ie c z n ie p r z e s y ła ć d a n e u w ie r z y t e ln ia ją c e p o m ię d z y k lie n t e m B a l p o w in n iś m y w y k o r z y s ta ć te c h n o lo g ię S S L (o d a n g .

457

a s e rw e re m ,

Secure Sockets Layei).

S zczegó­

ło w e o m a w ia n ie t e c h n ik k o n fig u r a c ji s e r w e r a S S L w y k r a c z a p o z a z a k r e s te m a ty c z n y t e j k s ią ż k i. D o d a tk o w y c h

in fo r m a c ji n a te n

t e m a t n a le ż y s z u k a ć n a s t r o n a c h

in te r n e to ­

http://java.sun.com/developer/technicaiArticles/WebSeivices/appsen/8-l.h tml ( G l a s s F i s h ) i http://jakarta.apache.org/tomcat/tomcat-5.5-doc/sskhowto.htmi ( T o m c a t ) .

w ych

Musimy następnie określić sposób, w jaki użytkownicy mają uwierzytelniać swój dostęp do naszej aplikacji. Najbardziej elastycznym podejściem jest uwierzytelnianie za pośrednictwem stosownych formularzy — w tym celu wystarczy dodać do pliku web.xml następujący wpis: F0RM / logm ntml

< logm ■conf> BASlC

< f or m - e r r o r ■pd g e> /n od Uth

Podstawowy mechanizm uwierzytelniania można zbudować także, umieszczając w pliku

web.xml następującą konstrukcję konfiguracyjną:



Przedstawiona konfiguracja mechanizmu logowania za pośrednictwem formularza wskazuje na stronę internetową, na której użytkownik będzie mógł wpisać nazwę i hasło. W kwestii projektu i wyglądu strony logowania mamy oczywiście pełną swobodę, jednak kod tej strony musi definiować mechanizm wysyłający do strony j_securi ty_check żądanie obejmujące parametry nazwane j_usernarne oraz j_password. W tym celu wystarczy skonstruować nastę­ pujący formularz:

W takim przypadku przeglądarka wyświetla okno dialogowe hasła (patrz rysunek 10.19). Okazuje się jednak, że profesjonalnie projektowane witryny internetowe zdecydowanie częściej wykorzystują uwierzytelnianie za pośrednictwem formularzy W W W .

Rysunek 10.19. Podstawowy mechanizm uwierzytelniania

Nazwa użytkownika Hasło

Łączenie z: openidao Mazwa użytkownika: O Hasło:

trooseve '1

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

f j Zapamiętaj moje hasło



Funkcję strony z komunikatem o błędzie może pełnić dowolny dokument. Kiedy użytkownik żąda dostępu do chronionego zasobu, aplikacja powinna wyświetlić stronę logowania (patrz rysunek 10.18). Jeśli użytkownik wpisze prawidłową nazwę i hasło, aplikacja wyświetli żądaną stronę. W przeciwnym razie oczom użytkownika ukaże się strona z komu­ nikatem o błędzie.

Rysunek 10.18. Próba uzyskania dostępu do chronionego zasobu



Formularz logowania Plik

Edycja

Widok

Historia

Zakładki

1

Narzędzia

Pomoc

3 8080/accejscontrol/[ j r

■rf||| L Ę p jj V'ffpM , http://loc lhost:

Aby uzyskać dostęp do informacji chronionych, musisz się zalogować

1Nazwa użytkownika: ¡troosevelt

S i u i i ! _____ ¡ B ś '

Plik web.xml opisuje tylko zasoby będące przedmiotem konstrukcji ograniczających dostęp i obejmuje role, które mają takim dostępem dysponować. Plik konfiguracyjny web.xml w żaden sposób nie określa sposobu składowania nazw, haseł i ról użytkowników. Tego rodzaju infor­ macje konfigurujemy, opisując domenę (ang. realm) danej aplikacji internetowej. Mianem domeny określamy dowolny mechanizm lokalizujący nazwy, hasła i role użytkowników. Serwery aplikacji z reguły oferują obsługę wielu standardowych domen uzyskujących dostęp do informacji o użytkownikach składowanych w następujących źródłach:

\

■ katalogu LD A P, I fTajpgui ,j

■ relacyjnej bazie danych, ■ pliku (np. w pliku conf/tomcat~users.xml serwera Tomcat).

1Zakończono

Użytkownicy serwera aplikacji GlassFish mają do dyspozycji interfejs administracyjny, za pośrednictwem którego mogą skonfigurować domenę. W menu Configuration/Security/Realms należy wybrać opcję utworzenia nowej domeny nazwanej openldap. Wykorzystujemy domyśl­ ną nazwę klasy, po czym definiujemy następujące parametry połączenia (jako tzw. właści­ wości dodatkowe):

458

Rozdziano. ■ Usługi zewnętrzne

JavaServerFaces c lire c to ry lddp / /1 OC d l hos L 389 b a sed n ou=people.dc= corejsf.dc-con

459

rozwiązanie jest zalecanym sposobem definiowania domen właściwych dla poszczególnych aplikacji pracujących na serwerze Tomcat.

jdds-context ldapRealm sedrch p in d d n cn=Manager. d c = c o re js i, dc=com sedrch-bm dpdssw ord secret se d rch - f 1 1te r uid =%s group-bdse-dn ou=groups.dc^corejsf.dc=com group■tdrget en group-sedrch■f 1 1te r

D o m e n ę m o ż n a s k o n f ig u r o w a ć ta k ż e z a p o ś r e d n ic t w e m w

c h c ie li w y k o r z y s ty w a ć a p lik a c ję

uniqueMember=$d

Musimy też opracować plik konfiguracyjny WEB-INF/sun-web.xml (patrz listing 10.18). Prezentowany plik odpowiada za odwzorowywanie nazw ról w nazwy grup. W naszym przypadku nazwy obu struktur będą identyczne.

Listing 10.18. Plik konfiguracyjny accesscontrol/web/WEB-INF/sun-web.xml (tylko serwer GlassFish) 1 panel Grid COlumns ="2">

12



13



Po pomyślnym uwierzytelnieniu użytkownika następuje wyświetlenie strony przedstawio­ nej na rysunku 10.21. Zaprezentowana strona powitalna obejmuje nazwę zarejestrowanego użytkownika i umożliwia nam przetestowanie jego przynależności do poszczególnych ról.

Rysunek 10.21.

14 15



16



17



18



19

7.

462

Rozdziano. ■ Usługi zewnętrzne

JavaServer Faces

Rysunek 10.22. Struktura katalogów aplikacji ilustrującej działanie mechanizmów kontroli dostępu

¡3 f ! f j \ | I j |

accesscontrol.war C3 META-INF !- Q MANIFEST. MF Q WEB-INF t £3 classes I com i ^ Cu3 corejsf j ¡ - Q UserBean. class | [ Q messages, properties j Q faces-config.xrril

i| Q j f j

sun-web.xml

! Q web.xml protected ; Q welcome.jsp

Listing 10.23. Kod strony accesscontrol/web/noauth.html 1 2 3 Próba uwierzytelnienia zakończona niepowodzeniem 4. 5. 6. 7. Przykro nam -- próba uwierzytelnienia zakończyła się niepowodzeniem. Spróbuj ponownie. 8 9

; Q index.html j- Q login.html : Q noauth.html

8 9. 10. 11. 12 . 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26 27. 28. 29. 30 31. 32. 33. 34. 35. 36 37. 38. 39. 40. 41. 42. 43. 44 45 46. 47. 48.

Faces Servlet javax.faces.webapp.FacesServlet l Faces Servlet *.faces i ndex.html Protected Pages /protected/* regi stereduser invitedguest FORM openldap /logi n.html /noauth.html registereduser i nvi tedguest

Listing 10.24. Kod klasy accesscontrol/src/java/com/corejsf/UserBean.java 1 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40.

package com.corejsf; import import import import

java.util.logging.Logger; javax.faces.context.External Context; javax.faces.context.FacesContext; javax.servlet.http.HttpServletRequest;

public class UserBean { private String name; private String role; private Logger logger = Logger,getLogger("com.corejsf"). public String getNameO { i f (name == null) getUserData(); return name == null ? "" : name; } public String getRoleO { returnrole == null ? "" :role, } public void setRole(String newValue) { role =newValue; } public boolean isInRoleO { External Context context =FacesContext.getCurrentInstance() .getExternalContext(); Object requestObject = context,getRequest(); i f (¡(requestObject instanceof HttpServletRequest)) { logger.severe("obiekt żądania jest typu; " +requestObject.getClassO); return false; } HttpServletRequest request = (HttpServletRequest) requestObject; return request.isUserInRole(role); } private void getUserDataO { External Context context =FacesContext.getCurrentlnstanceO.getExternalContext 0 ; Object requestObject = context.getRequest(); i f (¡(requestObject instanceof HttpServletRequest)) { logger.severe("obiekt żądania jest typu- " +requestObject.getClassO); return; }

463

464

JavaServer Faces

Rozdział 10. ■ Usługi zewnętrzne

41

H tip S ervietR e q u e st request

42

name = r e q u e s t

43

= ( H tip S e rv le tR e q u e s t) requestObject

g e t R e m o t e U s e r t ).

[

44

}

465

Twórcy witryny Amazon.com zdecydowali się udostępnić te usługi podmiotom zewnętrznym zainteresowanym sprzedażą towarów swoim klientom za pośrednictwem tego sprawdzonego systemu. Przed uruchomieniem naszego programu będziemy musieli się zarejestrować na witrynie Amazon.com, gdzie uzyskamy darmowy klucz dostępu uprawniający nas do nawią­ zywania połączeń z oferowanymi usługami.

Listing 10.25. Plik właściwości accesscontrol/src/java/com/corejsf'/messages.properties 1

t u l e = U w i e r z y t e l m a m e z a k o ń c z o n e pom ys ł m e

2

youHaveAccess=Masz teraz dostęp do info rm acji chronionych!

3 4

yOurl)senName=Nazwa użytkownika memberO+Czlonek r o li

DBH javax.servle t.HttpServletRequest ■ String getRenioteUser() (Servlet2.2) Zwraca nazwę aktualnie załogowanego użytkownika (lub wartość nuli, jeśli żaden użytkownik nie jest załogowany).

■ boolean i sUserlnRole(String role) (Servlet2.2) Sprawdza, czy bieżący użytkownik ma przypisaną daną rolę.

Stosowanie usług sieciowych

Największą zaletą usług sieciowych jest ich neutralność językowa. My będziemy uzyskiwali dostęp do using Amazon Web Services z poziomu języka programowania Java, co wcale nie oznacza, że inni programiści nie mogą korzystać z tych usług z poziomu aplikacji tworzonych w językach C++ bądź PHP. Deskryptor w formacie W SD L opisuje usługi w sposób całkowicie niezależny od tego czy innego języka programowania. Na przykład definicja w formacie W SD L usługi Amazon E-Commerce Service (dostępna w intemecie pod adresem http://webserviees. ^a m a zo n . com/A WSECommerceService/AWSECommerceService.wsdl) opisuje operację

ItemSearch: 3 4. 5. 6 7 8. 9 10. 11. 13 14 15. 16 17. 18 19 20. 21. 22

472

Rozdziano. ■ Usługi zewnętrzne

JavaServer Faces 23 24 25 26 27. 28 29 30 31 32 33 34. 35 36 37 38 39. 40 41 42.



Listing 10.30. Kod strony amazon/web/error.jsp 1. 2 3. 4 5. 6 7 8 9. 10. 11 12. 13 14 15 16 17 18.



I wreszcie na listingu 10.31 przedstawiono pakiet komunikatów.

Listing 10.31. Plik komunikatów amazon/src/java/com/corejsf/messages.properties 1. 2. 3. 4 5. 6. 7. 8. 9 10

title=Aplikacja JSF korzystająca z usługi sieciowej firmy Amazon authorSearch=Przeszukiwanie książek oferowanych przez Amazon.com według nazwiska autora author=Autor format=Format search=Przeszukaj searchResult=Wyniki przeszukiwania internalError=Błąd wewnętrzny internalError_detail=lAlystąpił błąd wewnętrzny. \ Prosimy zgłosić usterkę naszym konsultantom technicznym. continue=Kontynuuj

11 12. 13 14. 15.

473

authorl=Główny autor title=Tytuł publisher=Wydawca pubdate=Data wydania back=Cofnij

Wiem y już, jak nasze aplikacje internetowe mogą nawiązywać połączenia z takimi usługa­ mi zewnętrznymi jak bazy danych, katalogi i usługi sieciowe. Poniżej przedstawiono kilka najważniejszych wniosków płynących z naszych doświadczeń, które warto mieć na uwadze podczas samodzielnych eksperymentów: ■ Pliki JA R należy umieszczać albo w katalogu WEB-INF/lib aplikacji internetowej, albo w katalogu bibliotek serwera aplikacji. Drugie rozwiązanie jest uzasadnione wyłącznie w przypadku bibliotek wykorzystywanych przez wiele aplikacji, np. w przypadku sterowników JD BC . ■ Serwery aplikacji z reguły ofemją swoim użytkownikom gotowe usługi umożliwiające składowanie połączeń z bazą danych w puli, mechanizmy domen uwierzytelniania itp. Przykładem wygodnego i przenośnego mechanizmu lokalizowania klas niezbędnych do uzyskiwania dostępu do tych usług jest wstrzykiwanie zależności. ■ Parametry konfiguracyjne można definiować w plikach konfiguracyjnych facesconfig.xml lub web.xml. Pierwszy z tych plików powinien definiować parametry właściwe tylko dla danej aplikacji internetowej; drugi jest właściwym miejscem dla definicji parametrów określanych w czasie wdrażania.

474

JavaserverFaces

11 AJAX Programiści aplikacji internetowych od niepamiętnych czasów są zmuszani do przetwarzania danych formularzy w warunkach braku mechanizmów niezbędnych do zapewniania poziomu interaktywności znanego z tradycyjnych aplikacji autonomicznych. Dopiero kiedy firma Google pokazała całemu światu usługę Google Maps, okazało się, że aplikacje internetowe mogą oferować na tyle bogate interfejsy użytkownika, aby skutecznie konkurować z aplika­ cjami autonomicznymi. Usługa Google Maps dodatkowo wywołała falę innowacji, której efekty już w niedalekiej przyszłości będą decydowały o sposobie pracy całego środowiska programistów aplikacji internetowych. W tym rozdziale dokonamy analizy części spośród tych innowacji na przy­ kładzie aplikacji JS F zbudowanej na bazie technologii A JA X (od ang. Asynchronous Java­ Script a n d XM L ) i oferującej bogaty interfejs użytkownika. Przy okazji zostaną omówione następujące zagadnienia: ■ podstawy techniki A JA X ; ■ metody implementacji techniki A JA X za pomocą serwletów w ramach aplikacji JSF; ■ stosowanie mechanizmów nasłuchiwania faz JS F na potrzeby bardziej złożonych scenariuszy korzystania z techniki A JA X ; ■ uzupełnianie danych formularzy i weryfikacja w czasie rzeczywistym; ■ uzyskiwanie dostępu do stanu interfejsu użytkownika z poziomu wywołania techniki A JA X ; ■ zapisywanie stanu klienta w aplikacjach zbudowanych na bazie techniki A JA X ; ■ biblioteka Direct Web Remoting; ■ opakowywanie mechanizmów A JA X w ramach komponentów JS F ; ■ stosowanie frameworku Ajax4jsf.

476

Rozdziani. ■ AJAX

JavaServer Faces

Poniżej przeanalizujemy sposób działania tego mechanizmu. W pierwszej kolejności defi­ niujemy początkowo pusty element di v. Wewnętrzny kod H TM L tego elementu aktualizujemy dopiero po wygenerowaniu żądania. Element di v wraz z przyciskiem wprawiającym cały ten mechanizm w ruch przedstawiono poniżej:

Podstawy techniki AJAX A JA X jest w istocie prostym, trzyetapowym procesem: 1.

477



Wywołanie adresu U R L z poziomu kodu języka JavaScript wykonywanego po stronie klienta.

2. Obsługa tego wywołania po stronie serwera i sporządzenie odpowiedzi.

3. Integracja tej odpowiedzi z modelem DO M (od ang. Document Object Model). Opisana procedura jest niemal identyczna jak w przypadku obsługi przez aplikację JS F zdarzenia kliknięcia przez użytkownika łącza, także powodującego powstanie żądania. Apli­ kacja JS F obsługuje to żądanie po stronie serwera, by ostatecznie zapisać odpowiedź zastę­ pującą odpowiedź na poprzednie żądanie. To samo (oprócz ostatniego kroku) dotyczy żądań A JA X . Najważniejsza różnica polega na tym, że żądania A JA X nie wymagają odświeżania całych stron, a jedynie aktualizacji ich części. Ta z pozoru nieduża zmiana umożliwia nam wykonywanie takich operacji związanych z interakcją z użytkownikiem, o jakich nawet się nie śniło twórcom tradycyjnych aplikacji internetowych.

Warto zwrócić uwagę na typ tego przycisku, czyli button — oznacza to, że mamy do czynienia ze zwykłym przyciskiem, a nie przyciskiem zatwierdzania formularza. Kiedy użytkownik naciska przycisk, aplikacja JS F nie wysyła na serwer formularza otaczającego ten przycisk, tylko wywołuje funkcję showDaleAndT i niejęzyka JavaScript, której kod przedstawiono poniżej: u u ż y t k o w n ik a i o d c ią ż a ją c e j s e r w e r .

/

. p,.í........

Biblioteki języka JavaScript W poprzednim podrozdziale zaimplementowano blisko 30 wierszy kodu języka JavaScript ilustrującego najprostszy z przypadków użycia techniki A JA X . Przedstawione rozwiązanie jest dość uproszczone i nie uwzględnia dość oczywistych ograniczeń występujących po stronie przeglądarki internetowej.

479

Aby ograniczyć ilość kodu języka JavaScript niezbędnego do obsługi żądań A JA X i aby zagwarantować prawidłowość przetwarzania tych żądań przez rozmaite przeglądarki interneto­ we, powinniśmy użyć którejś z bibliotek języka JavaScript oferujących gotowe implementuje szczegółowych rozwiązań i udostępniających swoim użytkownikom wygodne obiekty.

Biblioteka Prototype W dalszej części tego rozdziału będziemy się posługiwali biblioteką Prototype języka Java­ Script, której mechanizmy znacznie upraszczają proces operowania na wywołaniach A JA X . Prototype jest popularną biblioteką oferującą między innymi wbudowaną obsługę techniki A JA X . Poniżej jako przykład przedstawiono odpowiednik fragmentu kodu języka JavaScript z poprzedniego podrozdziału opracowany z wykorzystaniem biblioteki Prototype: function showDateAndTime() { new Ajax. Updater ( "d a te D IV ", // aktualizowany element div "d a teA n d T im e.ajax ",

//

{ method • "g e t" } ) ,

//

adres URL metoda protokołu HTTP

ł }

W przedstawionym fragmencie kodu języka JavaScript wykorzystano obiekt Ajax.Updater biblioteki Prototype do wykonania wywołania A JA X i (już po jego wykonaniu) aktualizacji wewnętrznego kodu H T M L elementu dateDIV. Warto podkreślić znaczne ograniczenie ilości kodu w porównaniu z listingiem z poprzedniego podrozdziału — konstrukcję złożoną z 30 wierszy udało się zastąpić kodem zaledwie dwuwierszowym. Oznacza to, że użycie biblioteki Prototype nie tylko umożliwiło nam blisko 94-procentową redukcję kodu, ale też zagwarantowało możliwość wykonywania tego kodu na wszystkich przeglądarkach inter­ netowych. Mamy więc do czynienia z wprost doskonałym zwrotem inwestycji. Więcej informacji na temat biblioteki Prototype można znaleźć na jej stronie domowej pod adresem http://www.pro to typejs. org/.

Biblioteka Fade Anything Technique Jedną z pożądanych praktyk w świecie współczesnych aplikacji internetowych jest tworzenie wizualnych wskazówek dla tych obszarów strony internetowej, którą aktualizujemy z w y­ korzystaniem techniki A JA X . Do najbardziej popularnych wizualnych wskazówek należy powolne przechodzenie pomiędzy dwiema barwami tła, zwykle od koloru żółtego do domyśl­ nego koloru tła. Biblioteka Fade Anything Technique obejmuje zaledwie 91 wierszy kodu języka JavaScript, które umożliwiają nam płynną zmianę barw elementów na stronie. Korzystając z tej biblioteki, możemy określać szybkość zmiany kolorów, barwę początkową i docelową oraz czas trwania tego procesu. Biblioteki Fade Anything Technique będziemy używać w pozostałych przykła­ dach prezentowanych w tym rozdziale (wszędzie tam, gdzie wykorzystamy technikę A JA X do aktualizowania elementów na stronie).

480

Rozdziału. ■ AJAK

JavaServer Faces

Uzupełnianie danych formularzy Jednym z prototypowych zastosowań techniki A JA X jest uzupełnianie formularzy, czyli rozwiązanie polegające na automatycznym dostosowywaniu wartości w polach formularza do informacji wpisywanych przez użytkownika w pozostałych polach tego samego formu­ larza. Możemy na przykład użyć techniki A JA X do właściwego reagowania na wartość wpisaną w polu kodu pocztowego (patrz rysunek 11.2).

481

Kjedy użytkownik przenosi kursor poza pole tekstowe kodu pocztowego, wysyłamy na serwer żądanie AJAX obejmujące bieżącą wartość tego pola (w formie parametru żądania). Pole tekstowe kodu pocztowego wiążemy z metodą odpowiedzialną za obsługę tego zdarzenia w następujący sposób: . 1

2 I1 1 IX IIS S 3 K o d pocztowy ;61 -298 ¡ Miasto

Po zakończeniu przetwarzania żądania biblioteka Prototype wywołuje funkcję zwrotną (wskazaną powyżej za pośrednictwem konstrukcji onComplete). Kod tej funkcji przedstawiono poniżej:

:B R a k Da n y C h

Województwo ¡B R ak Da N^CM

!, yyygJndane, i $

UzupeMmia danych torm ám y z w yk o ay^ m fn tefomfei AIÁX - fóozgła Rrefa* i s j j ^ L - S .

processZipCodeSelection(req) { van cityAndState = req.responseText.spli t ( ' . ' ) . setCityAndStateFields(cityAndState[0]. cityAndState [1]). Fat fade_elemente"form:city " ). Fat fade_element("form;state").

fu n ctio n

Zakcmq |

|

' ■«py/ ' OLí i hc^f m O/fot-r. ¡ K ^ - j ' j i C - V

" *-

f e i'

¿.... K o d pocztowy ¡61-297 ! Miasto W ojewództwo í

| 3

/jfflfc/f

htip.//' > ■:i-^> :i i ■i --t■«c-J-jf— .r. tr.— - r ~i i.

tr-

> ■ t

.i.ti

-r,!- -r. .q t „ ,r.

.1

-r

• JOtfvrilOQiJ il rlOv". Jr"'.1'.',Pil ir. yPul vWHPôilirH'lInil'i'-'l^,.

jo e .

■ i'-iii'i-

.. Version 1.1.4

■' r, ■>- ./¿.-ndęTO,;,,- j * ( p i f lg j y j

i g l e _____

492

ROZdZiałll. ■ AJAX

JavaServer Faces Komponent harmonijki frameworku Rico pod wieloma względami przypomina panel podzielo­ ny na zakładki, który dodatkowo udekorowano estetycznymi animacjami — kiedy klikamy nagłówek interesującego nas panelu, jego zawartość jest zwijana lub rozwijana (proces zwijania lub rozwijania jest animowany). Przykład użycia komponentu z rysunku 11.7 w kodzie języka H TM L przedstawiono poniżej:

function createAccordion() { new Rico.Accordion($("theDiv")): }

493

W kolejnym podpunkcie omówimy sposób opakowywania tego komponentu frameworku Rico w ramach komponentu JSF .

Hybrydowy Komponent harmonijki JSF-Rico Aplikacja przedstawiona na rysunku 11.8 wykorzystuje komponent hybrydowy, czyli kom­ ponent JS F opakowujący komponent języka JavaScript. W naszym przypadku funkcję kom­ ponentu języka JavaScript pełni komponent harmonijki frameworku Rico.

Rysunek 11.8. Komponent JSF opakowujący komponent harmonijki frameworku Rico

--compone j - j j * j

v j;v i

Poznan

Rlasa AccordlonRenderer generuje element scrip t, którego atrybut sre wskazuje na plik

nco-script.jsf z kodem języka JavaScript: p u b lic c la s s AccorpionRenderer extends Renderer {

W v j ewo dztwo Wielkop olskie

puDlic void encodeBegm (FacesContext fc. U [Component component) inrowS IOException { Responsewn te r w r it e r = fc getResponseWn t e n ).

K o d pocztowy 61 29"?

/ / zapisuje skrypt ładujący plik z kodem JavaScript wykorzystującym frameM’ork w r it e r w rite f"< S C rip t ty p e = 't e x t /javdSC ri pi

Rico

+ "snc=-'rico-script, j s f ' >"

- "") . I sty i eC 1ass-="dccord i on" paneiüâss-' accorchonPanel" neaderClass="accord ionPanelHeadec" contente lass="accordioriPanel Content ">

*/ public void validateZip(ActionEvent e) { / / Ponieważ nasza aplikacja działa zbyt szybko, aby użytkownik mógł / / dostrzec wskaźnik zajętości (po stronie widoku), spowalniamy cały proces:

try { Thread.sieep(250); } catch (InterruptedException el) { el.printStackTraceO; } Ullnput input = (UI Input )e.getComponent() / / komponentframeworku Ajax4jsf . getPa rent ( ) , / / komponent wejściowy i f (input != nul 1) { String zip = (String)input.getSubmittedValue(); i f (zip ! = nul 1) { //

ustawia właściwości miasta i województwa zgodnie wartością pola tekstowego kodu pocztowego:

/ / z przesłaną

setCityAndState(zip); //

weryfikuje zawartość pola kodu pocztowego:

FacesContext fc = FacesContext,getCurrentInstance(); i n p u t . v a li d a t e ( f c ) ;

//

iteracyjnie przeszukuje mechanizmy weryfikacji

i f ( ¡input.isValidO) setErrorMessage(fc, input);

*

* * *



Wymusza na implementacji JSF odświeżenie pól wejściowych miasta i województwa. Jeśli przesłana wartość danego komponentu wejściowego jest różna / / od mdl, implementacja JSF nie odświeży tego pola.

//

//

ci tyłnput.setSubmi ttedValue(nul 1);

507

508

JavaServer Faces

Rozdziału. ■ AJAX statelnput.setSubmittedValue(null); }

} private void setErrorMessage(FacesContext fc, UIInput input) { / / przypisuje

właściwości errorMessage pierwszy komunikat dla pola kodu:

Iterator it = fc.getMessages(input.getCiientld(fc)). i f (it.hasNextO) { FacesMessage facesMessage = (FacesMessage)it,next(); errorMessage = facesMessage.getSummary(); } private void setCityAndState(String zip) { String cityAndState = ZipcodeDatabase.getCityAndState(zip), i f (CityAndState != nuli) { // rozpoznano kod pocztowy String[] cityStateArray = cityAndState.splitC,"), setCity(cityStateAmay[0]); setState(cityStateArray[l]); } else { I I nieznany kod złożony z 6 znaków: i f (zip.lengthO == 5) { setCity ("brak danych dla kodu " + zip), setStateCbrak danych dla kodu " + zip); i else { I I nieznany kod złożony z niewłaściwej liczby znaków: setCity(nul 1); setState(nul 1); }

Musimy pamiętać, że znacznik a4j: support dodaje do cyklu życia JS F metodę nasłuchującą akcji. To dość oczywiste, jeśli weźmiemy pod uwagę, że odpowiedni atrybut tego znacznika nazwano actionListener. Okazuje się jednak, że skutki stosowania tego rodzaju metody wcale nie muszą być od razu widoczne. Implementacja JS F weryfikuje wartość naszego komponentu kodu pocztowego mniej więcej w połowie cyklu życia JSF, a konkretnie w fazie weryfikacji. Jeśli proces weryfikacji zakończy się niepowodzeniem, implementacja JS F natychmiast przystępuje do fazy wizualizacji odpo­ wiedzi, czyli ponownego wyświetlenia naszej strony (co jest standardowym zachowaniem aplikacji JS F w razie niepowodzenia weryfikacji). Musimy pamiętać, że sama implementacja JS F nie dysponuje żadnymi informacjami sugerującymi, że ma do czynienia z wywołaniem A JA X , zatem realizuje cykl życia aplikacji zupełnie tak, jakby użytkownik wysłał na serwer formularz obejmujący komponent kodu pocztowego. Jeśli proces weryfikacji w tej fazie zakończy się niepowodzeniem i jeśli wskutek odrzucenia danych wejściowych implementacja JS F przejdzie do fazy wizualizacji odpowiedzi, ewentualne metody nasłuchujące akcji (zwykle wywoływane bezpośrednio przed fazą wizualizacji odpowiedzi) właściwe dla tego wywołania zostaną pominięte. W tej sytuacji definiujemy metodę nasłuchującą akcji z ustawionym atrybutem immediate, aby implementacja JS F wywo­ łała ją już na początku cyklu życia (bezpośrednio po fazie stosowania wartości żądania) i przy­ stąpiła do fazy wizualizacji odpowiedzi. Oznacza to, że gdybyśmy zdefiniowali naszą metodę

nasłuchującą akcji bez atrybutu immediate=true w komponencie a4j: status, nasza metoda nasłuchująca nigdy nie byłaby wywoływana.

509

Skoro opanowaliśmy już podstawy interesujących nas mechanizmów, warto przystąpić do analizy bardziej złożonych, wręcz najeżonych pułapkami aspektów. Warto zwrócić uwagę na wywołanie metody val i date przez naszą metodę nasłuchiwania akcji dla komponentu kodu pocztowego. Jeśli nasz komponent wejściowy zostanie oznaczony jako nieprawidłowy, możemy być pewni, że proces weryfikacji zakończy się niepowodzeniem — w takim przypadku należy przypisać stosowny (wyświetlany następnie za pośrednictwem widoku) komunikat właściwości errorMessage komponentu wspomagającego. Podsumujmy nasze dotychczasowe spostrzeżenia: wykorzystujemy znacznik a4j: support do uzupełnienia cyklu życia JS F o metodę nasłuchującą akcji (wywoływaną po przeniesieniu kursora poza pole kodu pocztowego, kiedy framework Ajax4jsf zainicjuje wywołanie A JA X ). Naszą metodę nasłuchującą akcji, która weryfikuje dane wpisane w polu wejściowym, musimy zdefiniować z atrybutem irnmediate=true, aby implementacja JS F nie pomijała metody nasłu­ chującej w razie niepowodzenia weryfikacji. Warto przeanalizować kolejny element składowy analizowanej układanki: jeśli realizowany przez implementację JS F proces weryfikacji zakończy się pomyślnie dla wszystkich kompo­ nentów wejściowych, wspomniana implementacja przypisze wartość nuli wszystkim spraw­ dzonym komponentom (w miejsce wartości wpisanych przez użytkowników). Jeśli jednak weryfikacja jednego lub wielu komponentów wejściowych zakończy się niepowodzeniem, implementacja JS F pozostawi wpisane wartości w niezmienionej formie. W trakcie ponownego wizualizowania naszej strony (albo w wyniku niepowodzenia wery­ fikacji, albo wskutek braku reguł nawigacji właściwych dla akceptacji formularza) imple­ mentacja JS F sprawdza, czy komponenty wejściowe reprezentują wartości nuli; jeśli tak, następuje ponowny odczyt wartości komponentów. Jeśli jednak wysłane wartości są różne od nul 1 (co jasno wskazuje na niepowodzenie weryfikacji), implementacja JS F nie wypełnia ponownie odpowiednich pól przez odczyt właściwych wartości, tylko ponownie wyświetla wartości wysłane za pośrednictwem ostatniego żądania. W ten sposób implementacja JS F zachowuje błędne wartości w komponentach wejściowych, które spowodowały błędy weryfi­ kacji, co jest w pełni zgodne z naszymi oczekiwaniami. Ponieważ wywołujemy mechanizm weryfikujący ręcznie, wartości wpisane za pośrednictwem naszych komponentów wejściowych nigdy nie są zastępowane wartościami nuli w razie pomyślnej weryfikacji (w przeciwieństwie do sytuacji, gdy weryfikacja jest wywoływana przez implementację JS F ). Oznacza to, że sami musimy podjąć odpowiednie działania, aby po pomyślnej weryfikacji pola tekstowe miasta i województwa zawierały odpowiednio łań­ cuchy Poznań i Wi el kopol ski e. Do tego celu wykorzystamy dowiązania do komponentów, które umożliwią nam wywołanie metody setSubmittedVal ue(nul 1) dla wspomnianych komponentów. Analizowany przykład powinien ostatecznie przekonać Czytelników, że stosowanie frame­ worku Ajax4jsf nie zawsze jest takie proste, jak w przypadku przedstawionej wcześniej aplikacji uzupełniającej dane formularza. Korzystanie z tego frameworku wymaga sporej wiedzy praktycznej o cyklu życia JSF. Programiści zainteresowani stosowaniem tej techniki koniecznie powinni się więc zaopatrzyć w dobrą książkę o JS F — masz szczęście, właśnie trzymasz taką książkę w dłoniach. Musimy też rozumieć, jak framework Ajax4jsf implementuje technikę A JA X . Na przykład znacznik a4j . support tego frameworku nie wymusza końca cyklu życia JS F za pośrednictwem metody responseComplete wywoływanej dla kontek­ stu (w przeciwieństwie do naszych rozwiązań stosowanych wcześniej w tym rozdziale).

510

JavaServer Faces Przeciwnie, różnice względem tradycyjnego cyklu życia ograniczają się do istnienia dodat­ kowej metody nasłuchującej akcji. Warto dobrze ten model zrozumieć i przemyśleć przed pod­ jęciem decyzji o stosowaniu analizowanego frameworku. Aby nasze rozważania były kompletne, poniżej przedstawiono listingi naszego mechanizmu weryfikacji kodów pocztowych i bazy danych kodów pocztowych wykorzystywanej przez komponent wspomagający. Oto kod weryfikujący: package com.corejsf; import com.corejsf.util.Messages: import import import import

javax faces.context.FacesContext; javax.faces.component.UlComponent: javax.faces.validator ValidatorException; javax.faces.validator.Validator,

public class ZipcodeValidator implements Validator { public void validate(FacesContext fc, UlComponent c, Object zip) throws ValidatorException { String cityAndState = ZipcodeDatabase .getCityAndState((String)zip); i f (cityAndState == null) { throw new ValidatorException( Messages.getMessage("com.corejsf.messages", "badZip")),

12 Frameworki open-source Kiedy firma Sun po raz pierwszy wpadła na pomysł opracowania technologii JS F , jej pra­ cownicy liczyli na wsparcie szerokiej społeczności programistów rozwiązań typu open-source (oferowanych z otwartym dostępem do kodu źródłowego). Prace nad tym projektem trwały co prawda dłużej, niż początkowo zakładano, jednak warto pamiętać, że podobny przebieg miały początkowe fazy realizacji takich projektów jak Apache Shale, JBoss Seam czy Facelets. Wymienione projekty open-source (a także projekty realizowane na bazie technologii JavaServer Faces, np. AjaxFaces) nie tylko ułatwiają pracę programistów JS F , ale też kształtują przyszłość tej technologii. W tym rozdziale skoncentrujemy się na trzech najważniejszych innowacjach: ■ przepływie stron internetowych;

Poniżej przedstawiono kod naszej bazy danych: package com.corejsf, public class ZipcodeDatabase { public static String getCityAndState(String zip) { i f ("61-297",equals(zip)) return "Poznań,Wielkopolskie"; else return riul 1;

■ alternatywnych technologiach widoków; ■ integracji z technologią Enterprise JavaBeans (E JB ). Przepływ (obieg) stron internetowych (ang. web flo w ) jest bardziej rozbudowaną wersją domyślnego mechanizmu nawigacji w ramach aplikacji JSF. Stosując mechanizm przepływu stron, możemy dużo łatwiej definiować złożone interakcje z użytkownikami. Począwszy od wersji JS F 1.2 oraz JS P 2.1, różnice wynikające z niezgodności obu technologii zaczęły stopniowo zanikać, zatem programiści zainteresowani łączeniem tych standardów nie powinni obecnie napotykać poważniejszych problemów. Z drugiej strony, mimo tak po­ zytywnych zjawisk wciąż znaczna część społeczności programistów aplikacji JS F oczekuje całkowitego zastąpienia technologii JS P lekkim mechanizmem szablonów. Okazuje się, że technologię JS F zbudowano między innymi z myślą o takim scenariuszu. Czytelnicy, którzy do tej pory nie korzystali z technologii E JB 3.0, mogą być zaskoczeni dojrzałością tego standardu (uważanego niegdyś za jeden z najbardziej nieprzystępnych, a obecnie uważanego za jedną z najważniejszych technologii w świecie aplikacji). Modele komponentów E JB i JS F są niestety niezgodne, co stwarza przestrzeń dla frameworków implementujących zunifikowany model komponentów.

512

Rozdział 12. ■ Framework! open-source

JavaServer Faces W kolejnych podrozdziałach tego rozdziału przeanalizujemy trzy ważne innowacje przez pryzmat trzech implementujących je frameworków typu open-source: Shale, Facelets i Seam.

Przepływ stron WWW— pakiet Shale Twórcy frameworku Struts opracowali też pakiet Shale, czyli zbiór usług pracujących w warstwie ponad warstwą JS F . Pakiet Shale oferuje szereg elementów funkcjonalności, które znacznie ułatwiają codzienną pracę nad aplikacjami JSF : ■ mechanizm przepływu stron W W W ; ■ mechanizm zdalnych wywołań A JA X ; ■ model szablonów i dekorowanych widoków; ■ mechanizm weryfikacji danych po stronie klienta i serwera z wykorzystaniem funkcji Apache Commons Validator; ■ framework testujący z obsługą testów jednostkowych i testów integracyjnych; ■ mechanizm integrujący framework Spring, interfejs JN D I i framework Tiles; ■ kontrolery widoków (konkretną implementację koncepcji komponentu wspomagającego JS F ).

513

tran sitio n outcome="next" target="Yet Another State"/> tra n sitio n outcome="cancel" target="Exit"/>

W powyższym fragmencie kodu (wyodrębnionego z pliku konfiguracyjnego w formacie X M F ) zdefiniowano dialog nazwany Example Dialog z trzema stanami: Starting State, The Next State oraz Exit. Widoczne przejścia określają na potrzeby frameworku Shale sposób nawi­ gowania pomiędzy poszczególnymi stanami — poniżej przedstawiono odpowiednik takiego przejścia w kodzie strony JSP :

Jeśli przedstawiony powyżej dialog znajduje się w stanie The Next State i jeśli użytkownik kliknie przycisk, framework Shale wykorzysta wartość atrybutu outcome (w tym przypadku next) do wyboru kolejnego stanu (w tym przypadku Yet Another State). Drugie z przejść zdefiniowanych dla stanu The Next State określa, że kliknięcie przycisku właściwego dla akcji cancel powinno kończyć dany dialog.

Zaimplementowany w ramach pakietu Shale mechanizm przepływu stron internetowych umożliwia nam tworzenie szeregu interakcji pomiędzy użytkownikiem a naszą aplikacją. Wspomniany zbiór interakcji często jest określany mianem konwersacji z użytkownikiem, dialogu lub kreatora (ang. wizard). W e frameworku Shale obowiązuje określenie dialog, zatem właśnie nim będziemy się posługiwali w tym podrozdziale.

Ten zmyślony naprędce przykład dobrze ilustruje podstawy dialogów frameworku Shale. Na rysunku 12.1 przedstawiono dużo bardziej realistyczny przykład kreatora płatności. Za pośrednictwem tego kreatora możemy dokonać płatności przez internet, a cała procedura składa się z czterech kroków: wpisania informacji o płatniku, wyboru metody płatności, wyboru har­ monogramu płatności oraz podsumowania. Wszystkie te kroki przedstawiono na rysunku 12.1 (począwszy od górnego zrzutu ekranu).

Dialog frameworku Shale składa się z jednego lub wielu stanów. Poszczególne stany obejmują przejścia definiujące sposoby przekazywania sterowania pomiędzy poszczególnymi stanami. Każdy dialog obejmuje też stan specjalny, nazywany stanem końcowym (ang. end state), który przerywa dany dialog i zwalnia jego stan. Przykład definicji dialogu frameworku Shale przedstawiono poniżej:

Warto zwrócić uwagę na pewien specyficzny aspekt tego kreatora. Jeśli z listy rozwijanej metody płatności wybierzemy opcję Przelew bankowy, po czym klikniemy przycisk Dalej, nie zostaniemy skierowani bezpośrednio na stronę Harmonogram płatności (jak w scenariuszu przedstawionym na rysunku 12.1). Zamiast tej strony naszym oczom ukazuje się poddialog odpowiedzialny za zebranie niezbędnych informacji o przelewie bankowym.

tran sitio n outcome="next" target-'The Next State"/> tran sition outcome="cancel" target="Exit"/> 51mJ ] i_4nuLui_!

Harmonogrampłatności 1- dn„i-.iz-

Przelew bariKowy

Nazwa i adres banku :

Nazwa..b.aaku i m s u z ú s i s u Zatcczcii'o

Rysunek 12.1. Kreator płatności

Rysunek 12.2. Poddialog przelewu bankowego (tym razem pominięto panel podsumowania)

j

L

l l T

l

516

JavaServer Faces

Rozdział 12. ■ Frameworki open-source

Konfiguracja dialogu

517

Nawigacja w ramach dialogu

Dialogi domyślnie definiujemy w pliku X M L nazwanym /WEB-INF/dialog-config.xml. Po­ nieważ w analizowanym przykładzie wykorzystamy dwa dialogi, zdecydowaliśmy się użyć dwóch plików konfiguracyjnych zadeklarowanych w deskryptorze wdrożenia: org.apache.shale.di alog.CONFIGURATION /WEB-INF/di alogs/payment.xml, /WEB-INF/di alogs/wi re-transfer,xml

Gdybyśmy użyli pojedynczego pliku nazwanego /WEB-INF/dialog-config.xml, nie musieli­ byśmy go deklarować w naszym deskryptorze wdrożenia.

Inicjowanie dialogu

Dialogi Shale domyślnie są definiowane w pliku konfiguracyjnym w formacie X M L. Poniżej przedstawiono definicję dialogu Payment, do którego odwołujemy się w przedstawionym przed chwilą fragmencie kodu: ^transition outcome="success" target="Payee Information"/>

Kolejnym krokiem analizowanego procesu jest zainicjowanie dialogu (wejście w dialog). W naszym przypadku za inicjowanie dialogu będzie odpowiadało specjalne łącze (patrz rysunek 12.3).

Rysunek 12.3.



?J|§ Przykład u|ye

Na początku każdego dialogu framework Shale umieszcza obiekt nazwany dialog w zasięgu sesji; kiedy kończymy (przerywamy) dialog, Shale usuwa obiekt dialog z sesji i tym samym tworzy odrębny zasięg dialogu. W ramach obiektu di al og możemy składować dowolny inny obiekt — wystarczy nasz obiekt przypisać właściwości data obiektu dialog. W powyższym fragmencie kodu uzyskujemy dostęp do obiektu data celem związania pola tekstowego z właściwością name tego obiektu. Nasz obiekt data ma postać prostej kolekcji właściwości reprezentujących pola kolejnych stron naszego kreatora, co jest dość popularnym rozwiązaniem. W tej sytuacji trudno nie zadać sobie następującego pytania: jak to się dzieje, że nasz obiekt data jest wiązany z właściwością data dialogu? Podobnie jak dialog Payment omówiony w punkcie „Nawigacja w ramach dialogu” , także dialog Wire Transfer obejmuje stan akcji nazwany Setup i wykonywany przez framework Shale bezpośrednio po zainicjowaniu dialogu. Metoda tego stanu składuje obiekt data w tak samo nazwanej właściwości dialogu. Deklarację

Metoda setupPaymentDi alog tworzy dwa obiekty, które łącznie obejmują wszystkie właściwo­ ści wszystkich stron (paneli) dialogów Payment i Wire Transfer. Ponieważ Wire Transfer jest poddialogiem dialogu Payment, obiekt data właściwy dla przelewu bankowego składu­ jemy w ramach obiektu data właściwego dla dialogu płatności. Gdy nasz dialog jest aktywny, odpowiedni obiekt dialog (i powiązany z nim obiekt data) jest dostępny za pośrednictwem takich wyrażeń JS F jak #{di al og.data .jakaśidłaściwość}. W momencie przerywania dialogu framework Shale usuwa obiekt dialog z zasięgu dialogu i tym samym wyklucza możliwość dalszego operowania na tym obiekcie za pośrednictwem wyrażeń JSF. Warto pamiętać, że obiekt data dialogu można bez trudu przekształcić w mapę, co w praktyce prowadzi do powstania zupełnie nowego zasięgu.

n

tej metody przedstawiono poniżej:

i l

Ł a t w o z a u w a ż y ć , ż e z d e f in io w a n a w

pow yższym

AbstractFacesBean

fra m e w o rk u

ro z s z e rz a

^FacesBean

k la s ę

k o d z ie

k la s a

Di a 1ogLauncher Abstract

S h a le . K la s a b a z o w a

o fe r u je s z e r e g p rz y d a tn y c h m e to d , k tó r e m o g ą n a m

b a r d z o u ła t w ić p ra c ę

n a d a p lik a c ja m i t e c h n o lo g ii J S F ( c z ę ś ć s p o ś r ó d ty c h m e to d o p is a n o p o n iż e j) . N a p rz y ­

i a iog name="wi re Transfer Dialog" sfart="Setup">

k ła d

k la s a

Di al ogLauncher

w y k o r z y s tu je m e to d ę

AbstractFacesBean. setValue,

p r z y p is u je o k r e ś lo n e j w ła ś c iw o ś c i k o m p o n e n tu ła ń c u c h

k tó ra

p rz e k a z a n y n a je j w e jś c iu

(c o

c ie k a w e , z a m ia s t s t a łe j ła ń c u c h o w e j m o ż n a p r z e k a z a ć w y r a ż e n ie r e p r e z e n t u ją c e w a r t o ś ć ) .





526

RozdziaM2. ■ Frameworki open-sounce

JavaServer Faces nref =" s t y le s css" rel =" s ty 1es n e e t" type= "iext/css"/> 1 1 10 >An p l a i n ■v d m 1 Id

xhtml

f i 1e< / 1 i c 1e >

< table cel 1padding=" 2px ">



Rysunek 12.5.

^

Efekt zastąpienia znacznika XHTML komponentami JSF (w czasie wykonywania oprogramowania)

fccłytja

Widok _ Fłistoria

Zakładki

Narzędzia

Pomoc i » {¡ajjy

Hash Plik

Edycja

iSip

Widok ’ ^

Historia

',!■ f j |

Zakładki !

Narzędzia

http://locor-

Pomoc

:8u80/raceie,.login-i oupedup.isf | - ffe j'

#{msgs.passwordPrompt}

527

528

JavaServer Faces

Rozdział 12. ■ Frameworki open-source



529

Rysunek 12.6. Projektant warstwy graficznej aplikacji internetowej operuje na stronach w tej formie

■ ■ &{msgs.namePrompt}

j^usei parne

#{msgs.passwordPrompt}





W razie wystąpienia błędu w czasie wykonywania kodu Javy skompilowanej strony zostanie wyświetlona strona error.jsp. Opisywany mechanizm nie jest jednak zbyt przydatny z perspek­ tywy programistów JS F , ponieważ wskazana strona JS P (opisująca błąd) nie będzie wyko­ rzystywana do informowania użytkownika o błędach kompilacji stron lub przetwarzania wyrażeń (w obu przypadkach zostanie użyta standardowa strona o błędach). Lepszym rozwiązaniem jest użycie znacznika error-page w pliku konfiguracyjnym web.xml. W ramach tego znacznika możemy zdefiniować albo klasę wyjątku Javy, albo kod błędu protokołu HTTP. Przykład takiej konstrukcji przedstawiono poniżej: java.lang.Exception /excepti on.jsp 500 /error.jsp 404 /notfound.jsp

Jeśli musimy operować na więcej niż dwóch możliwych konfiguracjach strony, powinniśmy się posłużyć odpowiednim komponentem, np. panel Stack biblioteki komponentów Apache MyFaces (patrz strona internetowa http://myfacesMpache.org/tomahawk/panelStack.html). Komponent panel Stack pod wieloma względami przypomina panel podzielony na zakładki, który przedstawiono w rozdziale 9., z tą różnicą, że nie udostępnia zakładek. Zamiast klikać zakładki, możemy wybrać jeden z komponentów potomnych z poziomu kodu aplikacji.

Jeśli w czasie wykonywania aplikacji wystąpi wyjątek typu, dla którego zdefiniowano stronę o błędzie, odpowiednia strona zostanie wyświetlona. W przeciwnym razie zostanie wyge­ nerowana strona o błędzie nr 500 protokołu HTTP.

Komponent panel Stack wymusza przypisanie każdemu z komponentów potomnych unikato­ wego identyfikatora. Za pośrednictwem atrybutu sel ectedPanel wskazujemy identyfikator komponentu potomnego, który ma zostać wyświetlony:

Jeśli w czasie wykonywania aplikacji wystąpi błąd protokołu H TTP, dla którego zdefinio­ wano stronę o błędzie, właśnie ta strona zostanie wyświetlona. W przeciwnym razie oczom użytkownika ukaże się standardowa strona o błędzie.

584

Rozdział 13. ■ Jak to zrobić?

JavaServer Faces

P t | Jeśli błąd wystąpi w czasie, gdy nasza aplikacja będzie próbowała wyświetlić i L J i niestandardow ą stronę o błędzie, zam iast tej strony zostanie wyświetlona strona domyślna. Jeśli nie udaje nam się zmusić serwera do wyświetlenia naszej niestandar­ dowej strony o błędzie, koniecznie powinniśmy przejrzeć płiki dzienników w poszukiwaniu kom unikatów dotyczących naszej strony.

Rysunek 13.10. Przykład zmienionej strony o błędzie

PM

Edycja

Widok

Historia

Zakładki

■ *¡¿11■ ].

Narzędzia

Pomoc

http://localhost:8080/error/mdex.faces

Prz vk ro nam . ale podczas p ra ry progi amu vvysr^pil bla d w ew n^trzny

i

Goeriia

jp | |

Pro s im v o V ontaki z dzialem p o m o r v

technic znei p o d num erem 0 8u 1 C O S T A iv l

|

N a p ro s b ? naszego k on s’iltanta p io sim v sk opiow ac 1 wkleu. pomzszv rap o n do w ia d o m c s c i pocztv elektiom cznej

Jeśli użyjemy dyrektywy JS P errorPage, obiekt wyjątku będzie dostępny w strukturze mapy żądania (reprezentowany przez klucz " j av a x . servlet. jsp. jspFxcept ion"). Z drugiej strony, jeśli posłużymy się znacznikiem error page, w mapie żądania zostanie umieszczonych wiele obiektów w ten czy inny sposób związanych z danym błędem (patrz tabela 13.2). Możemy te wartości wykorzystać do wyświetlania informacji opisujących dany błąd.

.1w a x . s e r v Let . S e r v Let E x c e p t L o n : j a v a x . f c a c e s . e l . E v a l u a t L o n E x c e p t Lon: g e t t m g p r o p e r* . y p a s s w o r d tr o m L .ean o t t y p e co m . c o r e j s ± . U s e r B e a n : j a v a . l a n g . N u i I P o m t e r Exc-ept io n

Klucz

Wartość

Typ

javax.servlet.error.status_code

Kod błędu protokołu HTTP.

Integer

javax.servlet.error message

Opis błędu.

String

javax.servlet.error.excepti on_type

Klasa danego wyjątku.

Class

javax.servlet.error.excepti on

Obiekt wyjątku.

Throwable

javax.servlet.error.request_uri

Ścieżka do zasobu aplikacji, który napotkał dany błąd.

String

javax.servlet.error.servl et_name

Nazwa serwletu, który napotkał dany błąd.

String

Poniżej przedstawiono przykładową aplikację, którą zaimplementowano właśnie z wyko­ rzystaniem tej techniki. W e właściwości password komponentu UserBean celowo generujemy wyjątek Nul 1PointerException, aby nasza aplikacja wyświetliła raport o błędzie (patrz rysu­ nek 13.10). Na listingu 13.21 przedstawiono zawartość pliku konfiguracyjnego web.xml okre­ ślającego stronę informacji o błędzie errorDisplay.jsp. Jej kod przedstawiono na listingu 13.22. P U W kodzie strony errorDisplay.jsp wykorzystano znacznik f: subview. Takie rozwiązanie k i l skutecznie elim inuje anom alię w ystępującą w im plem entacji referencji do JSF —- użycie znacznika f wiew w kodzie strony o błędzie powoduje błąd asercji w kodzie fram eworku.

1 . 2 . Faces Servlet javax.faces.webapp.FacesServlet l

|. I

o r g . a p a c h e . j a s p e r . r u n t Line . P a g e C o n t e x r Imp I . h a n d l e P a g e E x c - e p t i o n >P a g e C o w e v t Lmp I . ja \ ! o r g . a p a c h e , j s p . i n d e x _ j s p . _ j s p S e r v i c e i m d e x _ j 3 p . j a v a : "78 . ! o r g . a p a c h e , j a s p e i . r u n t Lme . Ht r p J s p B a s e . s e r v i c e . H tf p J s p B a s e . j a v a : Lx .. • j a v a x . s e r v l e t . h t t p . H t t p S e r v l e t . s e r v i c e ; H t t p S e r v L e t . j a v a :8 5 6? o r g . a p a c h e . j a s p e r . s e r v l e t . J s p S e r v l e t U r a p p e r . s e r v i c e (J s p S e r v l e t . W r a p p e r . j a v a : 3 11) o r g . a p a c h e . j a s p e r . s e r v l e t . J s p S e r v l e t . 3 e r v i c e J s p F i l e (J s p S e r v l e t . j a v a : 3 u l ) o r g . ap a c L ie . j a s p e r . s e r v l e t . J s p S e r v l e t . s e r v i c e ( J s p S e r v l e t . j a v a : 2 48 ) j a v a x . s e r v l e t . h t t p . H t t p S e r v l e t . s e r v i c e ( H t t p S e r v l e t . j a v a : 8 56)

13. 14 15. 16 17. 18. 19. 20 .

Faces Servlet *.faces /i ndex.html

21.

22 .

23. 500 24 /errorDi splay.faces 25. 26.

Listing 13.22. error/web/errorDisplay.jsp 1 . 2. ,F 'a g e C o n r ex t Lmp 1 . j; }

Tabela 13.2. Atrybuty wyjątków serwletów

*-

%>

taglib uri="http://java.sun.com/jsf/html" prefix="h" %>

5. 6. 7 8. 9. 10 . 11. 12 .

P § 9 Gdybyśmy um ieścili znacznik validatorScript przed znacznikam i kom ponentów L a f l w ym agających w eryfikacji, m ogłoby się okazać, że kod przeszukujący drzew o kom ponentów nie m oże odnaleźć m echanizm ów weryfikujących! To m ało intuicyjne, wręcz dziwaczne zachowanie wynika wprost ze sposobu funkcjonowania odpowiedniego mechanizmu JSP, na bazie którego opracowano im plem entację referencyjną technologii JSF, Kiedy im plem entacja zbudowana na bazie technologii JSP po raz pierwszy wizu­ alizuje stronę JSF, drzewo kom ponentów jeszcze nie istnieje. Oznacza to, że proces wizualizacji przebiega równolegle z procesem konstruowania drzewa kom ponentów. Na rysunku 13.11 przedstawiono opis błędu wygenerowany w odpowiedzi na próbę wpisania przez użytkownika błędnego numeru karty kredytowej. Kod tej przykładowej strony JS F przedstawiono na listingu 13.26.

Rysunek 13.11. Weryfikacja numeru karty kredytowej po stronie klienta

tH Aplikacja testująca mecha??fem weryfikacji - Moálaíkefüx Puk

Edycja

Widok

HMona

:

Zakładki U ,.

Nareęctoa

__?? __J lg * i ® •

-

Pomoc

no:p.y/locaihost:8080..'Clieritside-vaiiV ^

o^ 3 ^

Prosimy wpisać dane o płatności: Kwota

¡0 00

j

Numei karty kredytowei

!?11 l i 11 n 1111111

j

Data ważności irmesiątfiokj U'/2009

¡ PneiyvQf?, j

[Aplikacja Sm aSm pl] 1

Array('"). ).

functionName="validatePaymentForm"/>

Zakończono

i

jg

^

^

Nieznanv typ kart/ kredytowei



t e

592

Rozdział 13. ■ Jak to zrobić?

JavaServer Faces

Listing 13.26. Kod strony

clientsidew

1 2 . 27. 28. 29. 30. 31. 32. 33. 34. 35 . 36. 37 . 38. 39. 40. 41.

Szczegółowe rozwiązania w metodzie writeValidationFunctions są ściśle związane ze spo­ sobem, w jaki zakodowano odpowiednie konstrukcje języka JavaScript w ramach projektu Commons Validator. Po pierwsze, metoda writeValidationFunctions generuje funkcję weryfikującą wywoływaną za pośrednictwem konstrukcji onsubmit formularza na naszej stronie: var bCancel = false: function nazwaFunkcji(form) { return bCancel || validateCreditCard(form); }

593

Jeśli nasz formularz obejmuje przycisk Anuluj lub Cofnij, atrybut onclick odpowiedniego elementu powinien przypisywać zmiennej bCancel wartość true, aby pominąć weryfikację. Funkcja val idateCreditCard jest punktem wejścia do kodu mechanizmu Commons Validator. Funkcja val idateCreditCard wymaga do prawidłowego działania funkcji nazwanej na zw a ^ F o r m u ł arza_creditCard, która konstruuje obiekt konfiguracji. Metoda writeVal idation ^Functions generuje kod dla funkcji credi tCard. Wspomniane rozwiązania szczegółowe są niestety dość zawiłe. Funkcja n a z w a F o r m u la r z a _ ^creditCard zwraca obiekt obejmujący po jednym polu egzemplarza dla każdego weryfi­ kowanego elementu formularza. Każde z tych pól reprezentuje trój elementową tablicę, w której składuje identyfikator elementu formularza, komunikat o błędzie wyświetlany w razie nie­ powodzenia weryfikacji oraz wartość właściwą dla danego mechanizmu weryfikującego. Ponieważ nasz mechanizm nie wykorzystuje tej wartości, będziemy wykorzystywali w tej roli łańcuch pusty. Nazwy pól egzemplarzy nie mają żadnego znaczenia. W ciele metody writeVal idation ^Functions korzystamy z elastyczności języka JavaScript i odwołujemy się do pól indek­ sowanych wartościami 0, 1, 2 itd. Poniżej przedstawiono odpowiedni przykład: function paymentForm_creditCard() { th i s [0] = new Array("paynientForm:primary", "Nieprawidłowy numer pierwszej karty kredytowej", ""), th is[1] = new Array("paymentForm:backup", "Nieprawidłowy numer dodatkowej karty kredytowej", "");

} Jeśli zdecydujemy się opracować własne funkcje języka JavaScript, będziemy mogli zapro­ jektować bardziej rozsądny mechanizm składowania parametrów.

Jak weryfikować dane po stronie klienta za pomocą mechanizmu Shale Validator? W poprzednim punkcie omówiono technikę tworzenia własnego znacznika mechanizmu weryfikacji wykorzystującego skrypt opracowany w ramach projektu Commons Validator. Czytelnicy, którym takie podejście odpowiada, nie muszą zawracać sobie głowy innymi rozwiązaniami w tym zakresie. W ramach projektu Apache Shale (patrz strona internetowa http://struts.apache.org/struts-shale) opracowano z myślą o weryfikacji danych specjalną bibliotekę znaczników niestandardowych. (Wspomniana biblioteka początkowo została opracowana na potrzeby pierwszej edycji tej książki, by później wejść w skład projektu Shale). Użycie mechanizmu Shale Validator wymaga wykonania następujących kroków: 1. Należy umieścić pliki biblioteki shale-corejar i commons-validator.jar w katalogu WEB-INF/lib naszej aplikacji internetowej (wraz z bibliotekami niezbędnymi do ich prawidłowego działania). W czasie, gdy pisano tę książkę, lista niezbędnych plików dodatkowych JA R obejmowała następujące pozycje: commons.logging.jar,

594

RozdziaM3. ■ Jak to zrobić?

JavaServer Faces commons .digester.jar, commons-beanutils.jar,commons-collections.jar ijakarta oro.jar. Aby dysponować odpowiednimi wersjami tych plików, należy pobrać plik shale-dependencies-data.zipwłaściwy dla stosowanej dystrybucji projektu Shale.

2. Należy zadeklarować bibliotekę znaczników:

W tym konkretnym przypadku zastosowano przedrostek s. Jak zawsze w tego rodzaju sytuacjach można użyć dowolnego innego przedrostka.

Tabela 13.3. Mechanizmy weryfikacji po stronie klienta frameworku Struts Hazwa mechanizmu weryfikującego

Parametry

Przeznaczenie

required

brak

Sprawdza, czy dane pole obejmuje znaki inne niż spacje.

maxlength

maxlength

Sprawdza, czy długość danego pola nie jest większa od wartości parametru maxi ength.

minlength

minlength

Sprawdza, czy długość danego pola nie jest mniejsza od wartości parametru mi nl ength.

byte

brak

Sprawdza, czy dane pole zawiera liczbę całkowitą z przedziału o d -128 do 127.

short

brak

Sprawdza, czy dane pole zawiera liczbę całkowitą z przedziału od -32 768 do 32 767.

integer

brak

Sprawdza, czy dane pole zawiera liczbę całkowitą z przedziału od -2 147 483 648 do 2 147 483 647.

float

brak

Sprawdza, czy dane pole zawiera liczbę zmiennopozycyjną.

intRange

min, max

Sprawdza, czy dane pole zawiera liczbę całkowitą z przedziabi od mi n do max. Należy zdefiniować oba parametry.

floatRange

min, max

Sprawdza, czy dane pole zawiera wartość zmiennopozycyjną z przedziału od mi n do max. Należy zdefiniować oba parametry.

date

datePatternStric t

Sprawdza, czy dane pole zawiera datę sformatowaną zgodnie ze wzorcem datePatternStrict.

email

brak

Sprawdza, czy dane pole zawiera składniowo poprawny adres poczty elektronicznej.

creditCard

brak

Sprawdza, czy dane pole zawiera numer karty kredytowej, który przechodzi test Luhna.

mask

mask

Sprawdza, czy wartość danego pola można dopasować do wyrażenia regularnego reprezentowanego przez mask.

3. Należy przypisać metodę weryfikującą atrybutowi onsubmi t znacznika formularza:

Atrybut type reprezentuje typ mechanizmu weryfikacji. Atrybutowi type należy przypisać jeden z typów wymienionych w tabeli 13.3. Atrybutowi arg powinniśmy przypisać argument komunikatu o błędzie. Z reguły będzie to nazwa odpowiedniego pola.

595

Parametry min, max, minlength, maxlength, mask oraz datePatternStrict należy definiować w zależności od stosowanej metody weryfikacji.

5. Czytelnicy zainteresowani dostosowaniem działania mechanizmów weryfikacji do potrzeb swoich aplikacji lub dodawaniem nowych reguł weryfikacji powinni się zapoznać z materiałem dostępnym na stronie internetowej

http://wiki.apache.org/struts/Shale/Validation. ■ ¡S i W razie niepow odzenia w eryfikacji m echanizm C om m ons V alid a to r w yśw ietla ( ¡ ¡ l i wyskakujące okno ze stosownym kom unikatem . Lepszym rozwiązaniem byłoby oczywiście um ieszczenie kom unikatu o błędzie obok pola, które spowodowało błąd. Można ten cel osiągnąć, stosując pakiet weryfikacji po stronie klienckiej autorstwa Cagataya Civicíego (patrz strona internetowa http://jsf-comp.sourceforge.net/components/ V » ciientvalidators/index.html). ■■■

■ :■ ■■ ■

-■ ■ ■ :-—¿-Li.

■-

—. I . —.-¿-i

' ' ""

-

*

>

*

Jak weryfikować relacje pomiędzy komponentami? W technologii JS F mechanizmy weryfikacji w założeniu mają sprawdzać zawartość poje­ dynczych komponentów. Praktyka pokazuje jednak, że często niezbędne jest testowanie relacji łączących wiele komponentów. Na przykład współczesne aplikacje internetowe często żądają od swoich użytkowników ponownego wpisywania haseł. W tej sytuacji naturalnym rozwią­ zaniem byłoby wyświetlanie komunikatu o błędzie weryfikacji w razie wykrycia niezgodności obu haseł. Można ten problem rozwiązać na dwa sposoby — bardziej wymagające rozwiązanie polega na zaprojektowaniu własnego, niestandardowego komponentu, który będzie prezentował dwa pola wejściowe: jedno dla hasła i drugie dla potwierdzenia hasła. Takie podejście jest dość eleganckie, ale wymaga sporo pracy.

596

JavaServer Faces Drugie rozwiązanie polega na weryfikacji danych tylko przez drugi ze spokrewnionych kompo­ nentów. Kiedy wartości lokalne komponentów poprzedzających komponent odpowiedzialny za weryfikację będą ustawione, nasz kod weryfikujący będzie mógł je odczytać. Tylko dla ostatniego komponentu będziemy musieli wykorzystać wartość przekazaną przez metodę weryfikującą. Najprostszym rozwiązaniem jest umieszczenie metody weryfikującej w komponencie wspo­ magającym, który oprócz tej metody będzie obejmował komponenty naszego formularza:

Rozdział 13. ■ Jak to zrobić?

W pierwszej kolejności skoncentrujemy się na bibliotekach JSF . Najlepszym rozwiązaniem jest stworzenie projektu obejmującego te biblioteki i jego wielokrotne wykorzystywanie w kolejnych projektach JSF. Poniżej opisano procedurę konfigurowania tego projektu bazo­ wego (nazwiemy go j s f 1ibs). 1. Z menu File należy kolejno wybrać opcje New i Java Project , po czym wpisać w oknie dialogowym nazwę js f 1i bs.

2. W oknie Java Settings kreatora projektu należy kliknąć kolejno zakładkę Libraries i przycisk Add External JARs... (patrz rysunek 13.12). Jeśli korzystamy z serwera GlassFish, możemy się ograniczyć do dodania pliku ja va eeja r składowanego w katalogu glassfish/lib. Jeśli stosujemy inną implementację JS F , powinniśmy dodać wszystkie wymagane pliki JA R .

public class BackingBean

{

private UIInput passwordField: private UIInput confirmField, public void validateConfirmField(FacesContext context, UlComponent component, Object value) { i f ( ¡passwordField.getLocalValue() .equals(value)) throw new ValidatorException(... ) :

597

Rysunek 13.12. Dodawanie bibliotek do projektu jsflibs

} Mechanizm weryfikacji należy następnie dołączyć do pola potwierdzenia hasła:

Bardziej konkretny przykład zastosowania tej techniki można znaleźć w rozdziale 6.

Programowanie W poniższych punktach omówimy wybrane problemy, z którymi muszą się mierzyć progra­ miści aplikacji JSF . Pokażemy, jak korzystać ze środowiska Eclipse, jak eliminować utrud­ nienia związane z typowymi zadaniami implementacyjnymi, jak inicjalizować aplikacje i jak pakować komponenty w ramach plików JA R wielokrotnego użytku.

Jak tworzyć aplikacje JSF w środowisku Eclipse? W środowisku Eclipse można z powodzeniem edytować kod stron JS F , a także kompilować kod komponentów JavaBeans, konwerterów, klas weryfikujących i komponentów interfejsu użytkownika. Zakładamy, że Czytelnik potrafi wykonywać podstawowe operacje w tym środowisku, zatem skoncentrujemy się tylko na szczegółach konfiguracyjnych właściwych

3. Należy teraz kliknąć zakładkę Order and Export i zaznaczyć dodane przed chwilą

dla programowania aplikacji JSF .

4. Całą procedurę kończymy, klikając przycisk Finish.

Przed przystąpieniem do właściwych działań musimy zainstalować biblioteki technologii JS F i narzędzia Ant. Instalowanie tych bibliotek osobno dla każdego projektu byłoby oczywiście niepraktyczne.

biblioteki (patrz rysunek 13.13).

Za każdym razem, gdy będziemy chcieli utworzyć nowy projekt JSF, powinniśmy w pierwszej kolejności otwierać standardowy kreator New Java Project. Po dojściu do ekranu Java Settings należy jednak kliknąć zakładkę Projects i dołączyć projekt js flib s (patrz rysunek 13.14). Od tej pory wszystkie niezbędne biblioteki będą automatycznie włączane do naszego nowego projektu.

598

Rozdział 13. ■ Jak to zrobić?

JavaServer Faces

Rysunek 13.13. Eksportowanie bibliotek do projektu jsflibs

| New Java Project

Jeśli zależy nam na możliwości łatwego korzystania z narzędzia Ant, powinniśmy umieścić plik build.xml w katalogu projektu. Takie podejście odbiega od praktyki stosowanej w naszych aplikacjach przykładowych, gdzie korzystano z pojedynczego pliku build.xml dla wszystkich aplikacji. Czytelnicy, którym odpowiada takie podejście, mogą umieścić następujący plik build.xml w katalogach wszystkich projektów środowiska Eclipse:

m

ii

J a v a Settings Define the Java build settings.

\

'P m jYttVf E

T b i w e f i 'V Ordei and Export

Build class path order and exported entries: (Exported entries are contributed to dependent projects)



[Bj e5isflibs/src [fj S U R E System Library [jre 1.6.0_03] igj 0

javaee.jar - .Q;\gfesfish\ltfe

599

I

Można też skopiować pliki build.xml i buildproperties do katalogów wszystkich projektów. Aby uruchomić Anta, należy prawym przyciskiem myszy kliknąć plik build.xml i z menu kontekstowego wybrać opcję Run As/Ant Build. Komunikaty tego narzędzia zostaną wyświe­ tlone w oknie konsoli. Zalecamy instalację modułu rozszerzenia XMLBuddy i uczynienie z niego domyślnego edytora kodu stron JSF. W spom niany m oduł można pobrać z witryny internetowej http://xmlbuddy.com. Ponieważ XMLBuddy ta k napraw dę je s t edytorem plików XML,

Q

stosow anie prawidłowej składni języka XML (patrz rozdział :

Rysunek 13.14. Włączenie projektu jsflibs do nowo tworzonego projektu

New Java Projecl

J a v a Settings

I S SourceI ^ p"-'1 i Required projects on

! < ■

I Litvaries [ S/ Order andExport!

L.

Opisana konfiguracja czyni z narzędzia Eclipse całkiem sprawne środowisko wytwarzania aplikacji JSF . Wielu programistów oczekiwałoby oczywiście czegoś więcej, np. automa­ tycznego uzupełniania kodu stron JS F , podkreślania błędnych konstrukcji w kodzie stron JS F i X M L, wizualnej edycji reguł nawigacji pomiędzy stronami, diagnozowania oprogra­ mowania itp. Wszystkie te mechanizmy będą obsługiwane przez rozszerzenia platformy W T P (od ang. Web Tools Platform) opracowane specjalnie z myślą o technologii JSF. Samą platformę W TP zbudowano na bazie wielu innych rozszerzeń środowiska Eclipse. W czasie, kiedy pisano tę książkę, procedura instalacji i zakres funkcjonalności wciąż były przedmiotem zmian. W szczególności projekt JS F Tools wciąż znajdował się we wstępnej fazie realizacji (pracowano dopiero nad wersją 0.5). Czytelników zainteresowanych samodzielnymi eksperymentami z przyszłymi elementami funkcjonalności zachęcamy do instalacji odrębnej kopii środowiska Eclipse i wykorzystania mechanizmu Software Update do pobrania odpowiednich rozszerzeń. Należy zaznaczyć rozszerzenie JS F Tools i umożliwić programowi instalacyjnemu odnalezienie wszystkich niezbędnych rozszerzeń.

Jak zmieniać lokalizację plików konfiguracyjnych? W przypadku niektórych aplikacji internetowych stosowanie własnych, niestandardowych plików konfiguracyjnych okazuje się lepszym rozwiązaniem niż korzystanie z plików facesconfig.xml lub web.xml. Problem w tym, że nie zawsze wiadomo, gdzie umieścić odpowiedni plik, ponieważ programiści często nie wiedzą, gdzie dany kontener W W W składuje pliki ich aplikacji. W praktyce kontener W W W w ogóle nie musi tych plików składować we własnych katalogach — może je odczytywać z pliku W A R .

600

JavaServer Faces

Rozdział 13. ■ Jak to zrobić?

Można ten problem rozwiązać, stosując metodę getResourceAsStream klasy External Context. Przypuśćmy na przykład, że chcemy odczytać plik app.properties składowany w katalogu WEB-INF naszej aplikacji. Odpowiedni kod przedstawiono poniżej: FacesContext context = FacesContext.getCurrentInstance(); External Context external = context.getExternalContext(), InputStream in = external.getResourceAsStream("/WEB-INF/app.properties"):

Jak komponenty JSF mogą uzyskiwać dostęp do zasobów dostępnych w pliku JAR?

String resourcePath = req.getPathInfo(): i f (resourcePath == nuli) return; InputStream in = getClass( ) .getResourceAsStream(resourcePath), i f (in == nul 1 ) return. OutputStream out = resp.getOutputStreamO; int ch; while ((ch = in.readO) != -1 ) out.write(ch) ;

Kod tego serwletu należy umieścić w tym samym pliku JA R , w którym znajdują się mechanizm odpowiedzialny za wizualizację naszego komponentu i interesujące nas zasoby (w tym przypadku obrazy). W izualizacji podlegają adresy U R L w formacie resource/ścieżkaDoZasobu.

Przypuśćmy, że opracowaliśmy bibliotekę komponentów i przekazujemy ją naszym klientom w formie pliku my component.jar. Załóżmy, że niektóre z mechanizmów odpowiedzialnych za wizualizację tych komponentów muszą generować łącza do obrazów lub kodu języka Java­ Script składowanego w ramach tego pliku JA R . Możemy oczywiście zasugerować klientom wyodrębnianie i przenoszenie tego pliku we wskazane miejsce, jednak takie podejście znacznie podnosiłoby ryzyko występowania błędów i obniżało atrakcyjność naszego produktu — dużo lepszym rozwiązaniem byłoby umieszczanie naszego pliku JA R w katalogu WEB-INF/lib. Rozwiązanie tego problemu przeanalizujemy na przykładzie komponentu datownika z gra­ ficznymi przyciskami zwiększania i zmniejszania wartości w polach tekstowych (patrz ry­ sunek 13.15). Mechanizm odpowiedzialny za wizualizację powinien wygenerować następujący kod języka H TM L: 0) r appendC.'); r append(s);

} W kolejnym przykładzie spróbujemy skonstruować mechanizm przetwarzający właściwości systemowe. Na przykład dla wyrażenia w postaci: syspropC'java.version’ ]

609

return r.toStringO ;

610

RozdziaM3. ■ Jak to zrobić?

JavaServer Faces Aby dodać do aplikacji JS F niestandardowy mechanizm przetwarzający wyrażenia, należy umieścić w pliku faces-config.xml (lub innym pliku konfiguracyjnym) następujący element:

4.

611

Mechanizm interpretujący nazwy pakietów komunikatów.

5. Mechanizm przetwarzający zasoby konfiguracyjne aplikacji (np. zawartość pliku faces-config.xml).

com corejsf ComponentIdResolver

6. Mechanizm interpretujący przejęte (spadkowe) zmienne.

Kompletną implementację obu przykładowych mechanizmów przetwarzających można znaleźć w kodzie źródłowym dołączonym do tej książki. K S J W technologii JSF 1 .1 modyfikowanie języka wyrażeń je s t bardziej skom plikowane, ■¡fej Implementacja JSF 1 .1 obejmuje konkretne podklasy klas abstrakcyjnych V ariable ^ R e s o lv e r i P ropertyR esolver, K lasa V a r iableR esolver odpow iada za przetw arza­ nie podwyrażenia początkowego, natom iast klasa PropertyResolver musi prawidłowo interpretować operator kropki lub nawiasów kwadratowych. Gdybyśmy chcieli dodać do języka wyrażeń jakieś własne zm ienne, musielibyśmy opra­ cować własny mechanizm przetwarzający te zmienne i wskazać go w pliku konfiguracyjnym — przykład takiego rozwiązania przedstawiono poniżej: com.corejsf.CustomVariableResolver «/vari able-resolver>

7. Mechanizm interpretujący przejęte właściwości. 8. Mechanizmy dodane za pośrednictwem metody Appl i ca 11on. addEL Resol ver.

Diagnostyka i rejestrowanie zdarzeń Wykrywanie i rozwiązywanie problemów w aplikacjach JS F bywa dość kłopotliwe. Aby nasze aplikacje działały prawidłowo, musimy zadbać o mnóstwo szczegółów. Zdarza się, że komunikaty o błędach są trudne do zlokalizowania bądź wręcz niedostępne. Nawet naj­ drobniejsze literówki mogą uniemożliwiać uruchomienie aplikacji lub powodować, że jej użytkownik utknie w martwym punkcie. W tym podrozdziale przedstawimy szereg wskazówek, które mogą ułatwić programistom eliminowanie podobnych sytuacji.

Jak rozszyfrować ślad stosu?

02» #

,E L P E

3ob>-O- F O r : ■'■'■l.iemeniflti.ir' i ^ .02 "0 i r .-..^. r , e .r..[iier..cniaii..r 1 -_u. n"3-f ■S.'u-■'oniexr details mulcted .n.tidiizi'iu 2uri • >'a>.aSc'">=' r a- “ ■ ■ ■ u )^ ■ t=r.tRtiri i ;■. 0/ r.o riua' aj n.e. |sef. er| at l.tOulZi.tPdri| .detail-..

-«ste*> umai«rł «»ei

20117 1 i9

,9 52 31-

iClfSj,-t'-'1 tlda1. ..luizne.a . rri'padiD-10 _ rrIre =

1 1016

it>|F ij

,1.1.7 ,

i 9 On i-'0"tainpł "f0D

, I

L iJ

,a„a rat“ '

'ooie

1

L.-.eij|l"i ...ct, rn Idu|t Iriufn'-e'dUiZI -r' 'Uijd' e'-d' isoweg ar i.'riijrr'tje.gu'zi aetan. ■

VÈ i

Cj unordered ust

¿T

ll.jl

ÉT

Palette S> » C HTMl................. V

O

4

j j

i#- Í P

,_j| ,,... [j* O'dered ust

I.iid^-is- k,pt| uetans

1 1 1015

I 1013

Po uruchomieniu aplikacji wybierz z menu Window opcję HTTP Monitor.

1

IMF . ■

•ci. a- eme.

4.

. r>'„e .di['= i" _ n. i' e ad' -iar" e~r" in a = •adif'' i.

iM! ■

•a.-a f'ttd'

3. Zaznacz pole wyboru Use HTTP monitor.

Rysunek 13.20.

1 1018

1 1019

kontekstowego opcję Properties.

rh.oa,j|fi-1 n ri.rpajl'.H.iiP=ii,ai!..,ri,j'i.' —

■MF ■

ri..u a.jiL'f 1'i

Kliknij zakładkę Runtime i rozwiń węzeł Servers.

2. Kliknij prawym przyciskiem myszy pozycję GlassFish i wybierz z menu

1028

ntdinw .et . „nt,g

jspService

Programiści, którzy pracują w środowisku NetBeans, mają do dyspozycji znacznie lepszy mechanizm „szpiegowania” komunikatów protokołu HTTP (patrz rysunek 13.20). Użytkownicy środowiska NetBeans 5.5 mogą ten cel osiągnąć, wykonując następujące kroki:

..jetdii ,

,..,pi_,T.el.ia1,0,.

jspService

rmcdiji i d11.p—r. iai- . p-"i,ji-

E *• ■ it'in -'

1--

_jspService

_r,.ro .,J1[ 1: 1u _ ri.l-„jMd...e=in-„ a"-'-_ r,

••t'-'

_jspService

_ThreadlD=10,_T hread Na in e=rna rn__ JVVS ;

2007-1 -1 9 08 39 58 136 _ThreadlD=10,_ThreadNarne=inain,

INFO

I 1030 #

619

..



____

wtulę (e hasMoret lem e nist))

1 S t r in g n = ( S t r i n g ) e n ex tE 1ement( ). S t m n g [ ] v = request getPa rame terVal u e s ( n ) . for ( i n t

i =0

jav a u t i l

v != nuli && i < v length,

logging Logger global

i- ^ )

i n f o ( “ name=" - n + " >value=" + v [ i ] ) .

Monitor protokołu HTTP wyświetla kompletne zestawienie komunikatów przekazywanych pomiędzy przeglądarką a serwerem W W W i — co dla nas szczególnie ważne — dekoduje żądania. Taka możliwość jest szczególnie cenna w sytuacji, gdy konstruujemy własne komponenty niestandardowe.

Rozdział 13. ■ Jak to zrobić?

JavaServer Faces

Jak włączyć tryb rejestrowania zdarzeń związanych z pracą kontenera JSF?

^

Sun Java(TM) System Application Server Platform Edition 9,0 Admm Console - Mozilla Firefox Plik

Edycja



* ♦

Widok • f ?

Historia Q

Zakładki

Narzędzia

.



User: admin Server: iocalhost Domain: domainl

Implementacja referencyjna technologii JS F obejmuje niezliczone operacje rejestrowania zdarzeń, których dane wynikowe mogą być bardzo pomocne w procesie lokalizowania problemów w naszych aplikacjach.

Sun Java™ System Application Server Admin Console ifO Common Tasks ü

Jeśli korzystamy z serwera Glass Fish, możemy w wygodny sposób zarządzać rejestrowaniem zdarzeń za pośrednictwem interfejsu administracyjnego. W tym celu musimy się zalogować na stronie http://loca/host:4848/asadmin. Domyślną nazwą użytkownika jest admin, natomiast domyślnym hasłem administratora jest adminadmin.

Logging

F F S Ż F 5 Servei

W S

i M-l-

'

g j f - r . , - -t

p- lHÜ 3 a

,i, ,

T... M- .51j11-

S Life- ■■'C- f"l.„JulP Ü3 App Client Modules

Należy następnie kliknąć kolejno węzeł Application Server, zakładkę Logging i podzakładkę Log Levels. W dolnej części wyświetlonej strony znajduje się tabela zatytułowana Additional Module Log Level Properties. Właśnie tam należy dodać nowy wpis nazwany javax. enterpri se. ^ re s o u rc e .webcontainer. j s f i z poziomem FINEST (patrz rysunek 13.21).



W e Li Se r-i ces

§§j Custom MBeans ^

Module Log Levels

-I't'l' .ÎI- !' E 1B r u i1. ji—

ir.,j

M'lei

* TiNtSI -n.,,s,n i I IrJEP1 Mnij-r il—■ ° 111, , .Ti * r 11JE Minimal veiu L i » * »

l O N FIG Messages related to se rve r c onil g urati o n INFO Messages related to setvet configuration ot server status, excluding errors WARNING Warnings, including exceptions S E V E R E Events that interfere with normal program execution

Resources Configuration

Admin:

Configuration: Connector: CORBA:

Przedstawione ustawienia spowodują wyświetlenie ogromnej ilości informacji. Zakres tych danych można ograniczyć, wybierając interesujące nas elementy potomne. Mechanizm reje­ strowania zdarzeń wyróżnia następujące przyrostki: .context .lifecycle

.renderkit tag!ib

Jeśli na przykład interesuje nas monitorowanie faz cyklu życia, powinniśmy przypisać poziom FINEST nazwie javax. enterprise, resource, webcontai ner . j s f . 1i fecycl e. Jeśli korzystamy z serwera Tomcat, powinniśmy skonfigurować dane wyjściowe rejestrowania zdarzeń z wykorzystaniem standardowego mechanizmu biblioteki ja v a . uti 1 . 1 oggi ng:

¡ÍÑFO in f o

n fo

Deployment: EJ8 Container: JarvaMail: JAXR;

.application .config

fe 13 [ ,feJ 0 |T 3 E * ™ ““ [¡3 p fd F c T ” ~ 3 ¡I . .y [T F “”™ . .tíSi ¡7 ~ ~ ~"T3 fÎNFO y ¡INFO

Classtoader:

Przeszukując dzienniki zdarzeń, koniecznie należy ustawić poziom rejestrowania wyższy niż domyślny poziom INFO. Odpowiedni przykład przedstawiono na rysunku 13.22.

n fo

n fo n fc

JAXRPC: MS:

n fo

JTA:

jT rT T T "

JTS:

|7 r _ " ~

MDB Container:

¡ I n fo

Naming:

¡ I n fo -

Persistence:

pNFO

. s

..

-Djava. u t il.logging.config.fi] e=tomcatI conf/ logging.properties

W systemach U N IX i Linux należy użyć następującej składni: CATALINA_OPTS="-Djava.uti1.1 oggi ng.config.fi 1e=tomcat/conf/1ogging.properti es"



...... ,W

Root:

..B ........ y

Security:

¡INFO

Seif Management:

[INF0 ......y

Server: Utii:

|1 n f o ~

Verifier:

¡I n f o ™ "

Web Container:

fïïF o -

. y ’ 7TB

Additional Module Log Level Properties (1) Delete Properties

W systemie Windows powinniśmy zastosować taką składnię: set CATALINA_OPTS=-Djava.uti1.1oggi ng.confi g.fi 1e=tomcat\conf\1 ogging.properti es

@

T 3

SftftJ:

1- W pierwszej kolejności należy zmodyfikować skrypt catalina.sh lub catalina.bat dostępny w katalogu tomcat/bin. Na początku tego skryptu powinniśmy dodać wiersz przypisujący zmiennej CATALINA_0PTS następującą definicję parametru:

k\

;'U Name

i--!

; {vaKeTterpnse resource?

j X , || fl

| Value FIN EST

(Jak zawsze w takich sytuacjach element tomcat reprezentuje ścieżkę do katalogu, w którym zainstalowano serwer Tomcat, czyli np. /usr/local/jakarta-tomcat-5.5.19 lub c: \jakarta-tomcat-5.5.19).

2. Plik logging.properties należy skopiować z podkatalogu jre/lib pakietu Java SD K do katalogu tomcat/conf.

Rysunek 13.21. Aktywowanie trybu rejestrowania zdarzeń w ramach kontenera JSF (na przykładzie serwera GlassFish)

H ü

Pomoc

] 2 H L Í http!//łocalho5t:4848/a$adrnin/admingui/TopFrameset

-

620

621

622

JavaServer Faces

Rozdział 13. ■ Jak to zrobić?

623

■ W pierwszej kolejności należy raz jeszcze sprawdzić reguły nawigacji, aby mieć absolutną pewność, że zdefiniowano je prawidłowo dla danej strony. ■ Najczęstszym powodem niemożności przejścia do kolejnej strony aplikacji internetowej jest występowanie jakiegoś błędu weryfikacji lub konwersji. Łatwo to sprawdzić, umieszczając w kodzie tej strony znacznik . ■ Jeśli powyższe kroki nie pozwoliły nam zlokalizować źródła błędu, należy skorzystać z narzędzia śledzenia faz. Prostą implementację takiego mechanizmu przedstawiono co prawda w rozdziale 7., jednak w praktycznych zastosowaniach warto rozważyć użycie bardziej dojrzałego narzędzia FacesTrace (patrz strona internetowa http://facestrace.sourceforge.net). Wspomniane narzędzie prezentuje fazy w estetycznej formie graficznej (patrz rysunek 13.23). Aplikacja testująca da^ąpie rt

Rysunek 13.23. Narzędzie FacesTrace w akcji

Plik

Edycja

I

Widok

I

I

MM M M M M

i weryfikując

Historia

^

''Xih ""*"'■""■’ ■ ■ “

Prosimy wpisać inform acje o płatwności: u.

[ 'd ta wrazii s n - m i r

Rysunek 13.22. Zmiana poziomu rejestrowania zdarzeń w przeglądarce dzienników serwera GlassFish

i

Zakład*: ■ Narzędzia

514ciX

'th1 t'

LiS: ■ tu

i'-isO- dVTM-titJ: ■= u t

\-Ju- nuD

lucnii

[ P tz ę iy -p r? i

Racist. Trace »O C E S S

3. W kolejnym kroku powinniśmy zmodyfikować plik

nMf

tomcat/conf/logging.properties. Należy odnaleźć wiersz: java u t i l

logging ConsoleHandien level

V'tJW

RFOUESi lFECi-ClE

86nw

£'Q °i> 1«*«-

= INFO

vauaattQns* ż*i.sei

-

uociat/» 118 Moae1

i zastąpić poziom I NFO poziomem F 1NEST. Na końcu tego pliku należy dodatkowo umieścić następujący wiersz:

AOOSiCdtiG'’' 0 Tisec

i;s

vaujf

javax e n t e r p r i s e resource webconiamer j s f =f lNFS t

com ęułi facai. dDDf.icatfon ApDiscaiso^Associaceii1le328ae

i-oi^.snri.f a c e s conł..iDlansr

(W technologii JS F 1.1 należy dodać wiersz com.sun faces level =F 1NEST).

'W-eDaDROassi.oaaeFde'egate pw rnoQ'»mnei, /WEe-^rcfasseif n«x> se-rvi®* etassioati«*

,S!OX lV»E nfjque«! z o m sun ractiv.AppiicacionimD'

AopiiCttOOP SCOPP

4. Należy ponownie uruchomić serwer Tomcat i uruchomić aplikację JSF. Zapisy

ofq.dpacbe' c ate m a w e l c o m e , ¿LES org a p ac n e Ł ataB na.resources

dziennika znajdują się w pliku tomcat/logs/catalina.out.

com ^

faces, z n S " i , RfiQuES't. SERVICED iun,face;> O n e ’' im efmaaiizador c o m .suiirtacai. -«tml base C j-Svax faces, s i charter

Wyłączenie rejestrowania zdarzeń w ramach kontenera JS F wymaga przywrócenia wartości 1NFO zmiennej corn.sun faces level (w pliku tomcat/eonf/Iogging.properties). Jeśli serwer Tom cat odnajdzie bibliotekę Apache Log4J w ścieżce do klas, wykoL J rzysta w łaśnie tę bibliotekę rejestrow ania zdarzeń, chyba że do zm iennej środo­ wiskowej CATALINAJ3PTS dodam y następującą definicję: -Dorg.apache.commons.1oggi ng.Log=org.apache.commons.1oggi ng.impl.Jdkl4Logger

co«11.jyji fqces, 'ogicaiV'ewMap

Aaremt Ctess, 583 , 616 , 301 A a4j:status, 498 a4j:support, 498, 500, 506 AbstractFacesBean, 519 setValueQ, 519

accept, 105, 107 acceptcharset, 105, 107 Accept-Language, 53 accesskey, 105 AccordionRenderer, 495 action, 25, 27, 44, 75, 77, 80, 122, 257, 362 action state, 517 ActionEvent, 75 actionListener, 75, 96, 122, 256, 257, 263, 264, 362 ActionLogger, 265 ActionSource, 329, 352, 353, 367 ActionSource2, 328, 329, 367 Active Directory, 430 ActiveXObject("Microsoft.XMLHTTP"), 477 addAction(), 367 addActionListener(), 367 addDataModelListener(), 201 addValidator(), 367 addValueChangeListenerQ, 367 ADF Faces, 550 adnotacje cykl życia, 63 wstrzykiwanie zasobów, 420 adres URL, 30, 62 afterPhase(), 274, 485, 606 AJAX, 274, 475 Ajax4jsf, 497 Direct Web Remoting, 488 Fade Anything Technique, 479 harmonijki, 491 komponenty, 490 kontrolki przeciągnij i upuść, 491 mechanizm nasłuchujący, 485 proces, 476 propagowanie stanu widoku po stronie klienta, 487 Prototype, 479 Rico, 491 serwlety, 476

626

Skorowidz

JavaServerFaces

AJAX uzupełnianie danych formularzy, 480, 498 weryfikacja danych w czasie rzeczywistym, 482, 502 XHR, 476 XMLHttpRequest, 477, 478 żądania, 476, 486 Ajax.Request, 481 Ajax.Updater(), 479 Ajax4jsf, 497 cykl życia, 501 implementacja mechanizmu uzupełniania danych formularzy, 498 implementacja mechanizmu weryfikacji w czasie rzeczywistym, 502 AjaxPhaseListener, 485 AjaxServlet, 478 akcje, 256 aktualizacja wartości modelu, 43 algorytm nawigacji, 92 aliasBean, 549 alt, 105 alternatywne metody wizualizacji, 39 Amazon Web Services, 464 anchor, 107 Ant, 34, 599 Apache Log4J, 622 Apache MyFaces, 548 Apache Shale, 607 Apache Tiles, 291, 305 instalacja pakietu, 305 kafelki, 305 kafelki zagnieżdżone, 313 kontroler kafelków, 313 parametryzacja kafelków, 308 rozszerzanie kafelków, 309 stosowanie pakietu, 306 aplety, 559 aplikacje internetowe, 15, 23 JavaServer Faces, 21 LDAP, 445 applet, 560 Application, 328, 330, 353, 368 addELResolver(), 611 applicationScope, 73 apply request values, 43 APPLY REQUEST VALUES, 276, 606 architektura model-widok-kontroler, 38 ArrayDataModel, 187, 193 ArrayList, 54, 55 ASP.NET, 15 ataki XSS, 118

atrybuty, 329 actionListener, 263 binding, 100, 102 border, 135 converter, 100, 103, 210 HTML 4.0, 104 id, 100, 101 immediate, 267 przekazywane, 104, 105 rendered, 101, 103 required, 100, 220 requiredMessage, 221 style, 103 styleClass, 101 tabindex, 283 usemap, 558 validator, 100, 103 validatorMessage, 221 value, 49, 100, 102, 144 valueChangeListener, 100, 263 zdarzenia języka DHTML, 104 attribute, 96, 97, 98, 242, 271, 342 Attribute, 437 Attributes, 437 auth-constraint, 455 autocomplete, 112 automatyzacja procesu kompilacji, 34 autoryzacja zarządzana przez kontener, 455

B backing beans, 47, 60 BaseDialogPage, 520, 521 BasicAttributes, 435, 437 baza danych, 183 JDBC, 409 konfiguracja zasobów w ramach serwera GlassFish, 415 konfiguracja zasobów w ramach serwera Tomcat, 417 SQL, 409 użycie, 420 bean, 20, 45 beforePhase(), 274, 606 beverage, 144 bezpośrednie komponenty poleceń, 268 bezpośrednie komponenty wejściowe, 266 bezstanowy protokół, 61 bgcolor, 160, 170 biblioteki, 292, 312 Commons, 551 Direct Web Remoting, 488 JSTL, 582 MyFaces, 548 znaczników, 25, 95

biblioteki języka JavaScript, 478 Fade Anything Technique, 479 Prototype, 479 BigDecimal, 209, 210 Biglnteger, 209, 210 Binary Data, 563 Binary Servlet, 562 binding, 60, 98, 100, 101, 102, 107, 119, 139, 210, 221 błędy, 39, 154 konwersja, 211 krytyczne, 154 Boolean, 210 BOOLEAN detail, 215 border, 105, 135, 160, 170 breakpoints, 614 buffer, 549 build.xml, 35, 599 byte, 595

c c:if, 582 c:import, 301 calendar, 549 caption, 172 captionClass, 160, 170 captionStyle, 160, 170 CATALINA_OPTS, 620, 622 cellpadding, 160, 170 cellspacing, 160, 170 ChangeLocaleBean, 271 chapterKeys, 294 charset, 105, 122 ciasteczka, 61 Clay, 529 close(), 412, 436 cn, 428 cols, 111, 112 column, 99, 166 columnClasses, 160, 170, 181 columns, 159, 160 com.corejsf, 20, 21 com.corejsf.Contact, 541, 543 com.corej sf. CreditCardConverterTag, 394 com.corejsf.DirContextFactory, 441 com.corejsf.messages, 50 com.corejsf.SpinnerTag, 340 com.corejsf.TableData, 168 com.corejsf.UploadFilter.repositoryPath, 554 com.corejsf.UserBean, 46, 115 com.corejsf.util, 232 com.corejsf.util.Messages, 232, 238 com.corejsf.util.Renderers, 359, 369 commandButton, 99, 103, 120, 121, 122, 123

627

commandlink, 97 commandLink, 99, 120, 122, 123, 124, 127,269, 283 Commons, 551 Commons Validator, 594 ComponentELTag, 343 ComponentldResolver, 608 Connection, 410, 413 Connection Pools, 415 Contact, 541, 543 container-managed authentication, 455 contentClass, 308 Context, 436, 458 Context.SECURITY_PRINCIPAL, 442 contextDestroyed(), 606 contextlnitialized(), 606 context-param, 30, 440 convert, 228 convertClientId(), 356, 357 convertDateTime, 96, 208, 210 converter, 96, 100, 101, 103, 119, 210, 227 Converter, 210, 229 converter-class, 227 ConverterELTag, 394, 395 ConverterException, 225, 229 converter-id, 227 converterMessage, 101,214 convertNumber, 96, 208 cookie, 73 cookies, 61 coords, 105 core, 25 corej sf:chart, 561 corejsf:creditCardValidator, 587 corej sf: spinner, 325, 326 CREATE TABLE, 423 createAccordion(), 492 createConverter(), 368, 369, 394, 395 createMethodBinding(), 353 createStatement(), 410 createSubcontext(), 435, 436 createValidator(), 407 createValueBinding(), 353 creditCard, 595 CreditCard, 225, 227, 228 CreditCardConverterTag, 394 creditCardValidator, 587 CreditCardValidator, 589 Cross-Site Scripting, 118 CSS, 103 CURRENCY_detail, 215 currencyCode, 209 currencySymbol, 209 CustomerBean.all(), 183

628

JavaServer Faces

cykl życia aplikacji JSF, 42, 250 aktualizacja wartości modelu, 43 fazy, 42 przywracanie widoku, 43 stosowanie wartości żądania, 43 weryfikacja danych, 43 wizualizacja odpowiedzi, 43 wywołanie aplikacji, 44 D dane binarne, 561 osobowe, 445 użytkownik, 20 dane konfiguracyjne, 438 konfiguracja komponentu, 438 konfiguracja kontekstu zewnętrznego, 440 konfiguracja zasobu zarządzanego przez kontener, 441 data source, 409 dataList, 549 DataModel, 187, 201 getWrappedData(), 187 dataScroller, 549 DataSource, 409, 415 dataTable, 99, 102, 165, 296, 549 date, 595 Date, 207 DATE_detail, 215 dateStyle, 209 DateTime, 210 DateTimeConverter, 396 datownik, 325, 346 daty, 207 dc, 428 debuger, 31,614 decode(), 336, 356, 555, 571 DefaultContext, 417 default-locale, 53 deferred-method, 341, 350 deferred-value, 341, 350, 394 definiowanie komponent JSF, 329 para nazwa-wartość, 97 reguła nawigacji, 27 właściwość, 48 deklaracja DOCTYPE, 26 deklaracja pakietu komunikatów, 50 dekodowanie, 40, 334 żądania, 41 deleteNames(), 189 description, 89 destroySubcontextQ, 435, 436

Skorowidz DHTML, 104 diagnostyka, 611 debuger, 614 FacesTrace, 623 parametry przekazane za pośrednictwem strony, 617 pliki dziennika, 616 punkty wstrzymania, 614 ślad stosu, 611 ślad stosu z piekła rodem, 613 tryb rejestrowania zdarzeń związanych z pracą kontenera JSF, 620 diagram składni definicji, 66 dialog, 512 inicjowanie, 516 konfiguracja, 516 nawigacja, 517 poddialogi, 522 zależność od kontekstu, 520 dialog-config.xml, 516 DialogFauncher, 519 dialogFauncher.setupPaymentDialogO, 517 dir, 105, 107, 119, 170 DirContext, 436, 441 DirContextFactory, 441 Direct Web Remoting, 488 directory deployment, 615 disabled, 105 disabledClass, 132, 135 DiskFileUpload, 554 display-name, 89 div, 201 długość łańcucha, 219 dn, 428 DOCTYPE, 26 document.getElementByldO, 477, 481, 576 doFogin(), 422 dołączanie aplety, 559 konwertery, 210 rozmieszczenie, 305 dołączanie treści, 300, 302 aplikacje JSP, 300 kontekst aplikacji JSF, 301 DOM, 476 domena, 457 dostęp do bazy danych, 409 dostęp do informacji składowanych w katalogach FDAP, 433 dostęp do komunikatów o błędach zapisanych w pakiecie komunikatów, 230 dostęp do łańcucha komunikatów, 50 dostęp do zasobów zarządzanych przez kontener, 419

dostosowywanie komponenty, 46 wygląd stron o błędach, 583 DOUBFE, 215 DOUBFE_detail, 215 DoubleRangeValidator, 220, 222 DoubleRangeValidator.MAXIMUM, 222 DoubleRangeValidator.MINIMUM, 222 DoubleRangeValidator.NOT_IN_RANGE, 222 DoubleRangeValidator.TYPE, 222 drag-and-drop, 491 driverClassName, 418 drzewo katalogów, 429 DTD, 30 duże zbiory danych, 570 DWR, 488 Dynamie HTMF, 104 dynamie reloading, 615 dynamiczne ponowne ładowanie, 615

629

endElement(), 332, 334 ENUM,215 Enum.valueOf(), 68 ENUM_detail, 215 ENUM_NO_CFASS, 215 ENUM_NO_CLASS_detail, 215 error.jsp, 583 errorClass, 155 error-code, 583 errorMessage, 504 error-page, 583 errorPage, 584 errorStyle, 155 escape, 118, 119, 139 EvaluationException, 612 exception-type, 583 executeQuery(), 410 executeUpdateQ, 410 Extensions, 601, 607 ExtemalContext, 336, 441

E Eclipse, 31, 596 build.xml, 599 dodawanie bibliotek do projektu, 597 eksportowanie bibliotek, 598 New Java Project, 597 projekt, 597 tworzenie aplikacji JSF, 596 włączanie projektu, 598 EditableValueHolder, 328, 329, 336, 353, 367, 486 edycja komórki tabeli, 177 model tabel, 188 EJB, 535 EJB3, 535, 541 ekran logowania, 19, 20 ekran powitalny, 20 elementy, 138 formularze, 108 składowe aplikacji, 20 eliminowanie wycieków połączeń, 411 EFResolver, 608 email, 595 empty, 74 enabledClass, 132, 135 encode(), 573 encodeBegin(), 330, 331, 356, 555 encodeChildrenO, 330, 333, 356, 375, 376 encodeEnd(), 330, 356, 376, 377, 379 encodeTab(), 379 encoding, 40 enctype, 107 end state, 512

F factionListener, 264, 375 fattribute, 97, 242, 271 atrybuty, 98 fconvertDateTime, 208, 210 atrybuty, 209 fconverter, 227 fconvertNumber, 208 atrybuty, 209 f:facet, 97, 171 atrybuty, 98 floadBundle, 50 f:param, 97, 270 atrybuty, 98 fselectltem, 134, 136, 138, 139, 374 atrybuty, 139 fselectltems, 138, 140, 374 f: setPropertyActionListener, 272 fsubview, 301, 584 EvalidateDoubleRange, 220 EvalidateLength, 219, 220 EvalidateLongRange, 220 f:validator, 220, 240, 241 EvalueChangeListener, 264, 360 f:verbatim, 560 f:view, 25,301,584 fabryka, 544 Facelets, 524, 525 Clay, 529 jsfc, 526 kompozycje stron złożonych z szablonów, 531 stosowanie znaczników technologii JSF, 529

630

JavaServer Faces

Facelets szablony, 532 treść, 532 układ, 532 zastępowanie znaczników komponentami JSF, 526 znaczniki niestandardowe, 533 faces-config, 28, 273 faces-config.xml, 20, 27, 28, 34, 46, 53, 64, 117, 240, 438, 599 facesContext, 73 FacesContext, 234, 328, 330, 333, 336, 352, 396, 441,486 renderResponse(), 251 responseComplete(), 251 FacesEvent, 252 FacesMessage, 225, 229, 232 FacesTrace, 623 facet, 96, 97, 98, 376 Fade Anything Technique, 479 fatalClass, 155 fatalStyle, 155 fazy, 274 fazy cyklu życia aplikacji, 42 fileUpload, 549 FileUploadRenderer, 555 filtr modelu danych, 192 filtr serwletów, 601 filtrowanie, 192 finally, 412 fmdComponent(), 102 fmdCreditCardValidators(), 589 first, 169, 170 flash scope, 607 float, 595 floatRange, 595 footerClass, 160, 170, 171, 172 for, 155 form, 99, 107 atrybuty, 107 Form.getStatePrompt(), 102 formaty numerów kart kredytowych, 227 form-eiTor-page, 456 form-login-config, 456 form-login-page, 456 formularz logowania, 42 formularze, 41, 106 AJAX, 480 anchor, 107 atrybuty, 106 elementy, 108 identyfikator, 107 kontrolki, 109 metoda GET, 107 metoda POST, 107

Skorowidz pola tekstowe, 111 przesyłanie, 107 skrypty JavaScript, 108 weryfikacja poprawności danych, 206 zapisywanie stanu klienta, 107 frame, 160, 170 frameworki JSF, 11 open-source, 511 from-action, 91, 93 from-outcome, 27, 78 from-view-id, 78, 90 ft:trace, 623 funkcje JavaScript, 605

G generowanie dane binarne, 561 zdarzenia akcji, 382 znaczniki, 330 get, 24, 48 GET, 107 get(), 437 getActionURL(), 568 getAllO, 434, 437 getApplication(), 352 getAsObject(), 225, 229 getAsString(), 225, 229 getAttributes(), 243, 333, 351, 433, 436 getAvailableIDs(), 570 getAWSECommerceServicePortQ, 467 getBean(), 519 getChapterKeys(), 296 getCityAndStateForZip(), 490 getClientldO, 332, 333 getComponent(), 252 getComponentType(), 344 getConnected(), 48 getConnection(), 409, 411 getContextClassLoader(), 230 getConvertedValue(), 356, 359, 360 getConverter(), 368 getConverterWithType(), 360 getCurrentInstance(), 352 getELContext(), 396 getElementById(), 477, 576 getErrorMessage(), 589 getExtemalContext(), 336, 441 getFacesMessage(), 230 getFacet(), 377 getID(), 437 getIncrementedValue(), 335 getlnitParameterQ, 440, 441

getISOCountries(), 203 getJSFState(), 487 getLanguageCode(), 271 getLocale(), 53, 234 getName(), 25 getNewValue(), 252, 368 getObjectlnstance(), 442, 443 get01dValue(), 252, 368 getPassword(), 24, 47 getPhaseId(), 252, 274 getRemoteUser(), 464 getRendererType(), 344, 355 getRendersChildren(), 333, 356, 357, 377 getRequestMap(), 605 getRequestParameterMap(), 271, 336 getResponseWriter(), 333 getRootContext(), 447 getRowCount(), 201 getRowData(), 201 getRowlndex(), 201 getScoreComponent(), 60 getStatePrompt(), 102 getString(), 230, 410 getType(), 368 getValidators(), 486 getValue(), 378, 396, 520 getValueExpression(), 368 getViewRoot(), 234 getWrappedData(), 187, 188, 201 GlassFish, 17, 22 konfiguracja zasobów baz danych, 415 globalOnly, 155 Google Maps, 475, 550 graphiclmage, 99, 118, 119 gridClass, 308 groupingUsed, 209 grupowanie wzajemnie powiązanych stron, 607 grupy elementów, 141 H h: column, 166 atrybuty, 170 h:commandButton, 25, 120, 121, 123 atrybuty, 122 h:commandLink, 97, 120, 123, 127, 269 atrybuty, 122 h:dataTable, 102, 165, 296 atrybuty, 169, 170 bgcolor, 170 border, 170 captionStyle, 170 cellpadding, 170 cellspacing, 170

columnClasses, 170 dir, 170 first, 169, 170 footerClass, 170 frame, 170 h:column, 166 headerClass, 170 rowClasses, 170 rows, 170 rules, 170 summary, 170 UIData, 166 value, 166 var, 170 h:form, 25 atrybuty, 107 h:graphiclmage, 118 atrybuty, 119 h:input, 323 h:inputHidden, 114, 246 atrybuty, 112 h:inputSecret, 25, 111, 113 atrybuty, 112 hiinputText, 25, 102, 111, 179 atrybuty, 112 h.'inputTextarea, 111, 114 atrybuty, 112 hmiessage, 154, 211, 213 atrybuty, 155 h:messages, 154, 219 atrybuty, 155 h:outputFormat, 52, 97, 118 atrybuty, 119 h:outputLink, 120, 124, 125 atrybuty, 124 h:outputText, 25, 118, 120, 179 atrybuty, 119 h:panelGrid, 159 atrybuty, 160 h:panelGroup, 161 atrybuty, 161 h:selectBooleanCheckbox, 131, 133, 146, 178 atrybuty, 132 hiselectMany Checkbox, 131, 133, 134 atrybuty, 132 h:selectManyListbox, 131, 135, 136, 141, 145 atrybuty, 132 h:selectManyMenu, 131, 135, 137, 146 atrybuty, 132 hiselectOneListbox, 131, 135 atrybuty, 132 h:selectOneMenu, 106, 131, 135, 137 atrybuty, 132 h:selectOneRadio, 131, 132, 134, 144

631

632

Skorowidz

JavaServer Faces

harmonijki, 491 Rico, 491 Harmonogram płatności, 513 HashMap, 141 hasMore(), 437 header, 73 headerClass, 160, 170, 171, 172, 308 header-menu-content, 309 headerValues, 73 Hibernate, 535 hierarchiczna baza danych, 428 hiperłącza, 378 hot deployment, 615 hreflang, 105 HTML, 23 HTML 4.01 Transitional, 27 HtmlBasicInputRenderer, 360 HTTP, 61,545 HttpServletRequest, 459, 464 HttpSession, 62 HttpSessionListener, 606 hybrydowy komponent harmonijki JSF-Rico, 493 I IBATIS, 535 ICEfaces, 550 icon, 89 id, 98, 100, 101, 107, 119, 139 IDE, 16 identyfikator formularza niezbędny do wygenerowania struktury document, forms [id], 604 identyfikatory, 101 image, 122, 294 image map, 558 immediate, 111, 112, 122, 267, 269 immediate component, 267 implementacja klasy niestandardowych konwerterów, 225 klasy niestandardowych mechanizmów weryfikacji, 237 mapa obrazów, 558 mechanizm uzupełniania danych formularzy, 498 mechanizm weryfikacji w czasie rzeczywistym, 502 niestandardowe konwertery, 393 niestandardowe mechanizmy weryfikacji, 393 obsługa wysyłania plików na serwer, 550 pula połączeń z bazą danych, 411 znaczniki komponentów niestandardowych, 339 include, 301 index.html, 20, 30 index.jsp, 19, 20, 30, 39 INFO, 616 infoClass, 155 informacje, 154

infoStyle, 155 inicjalizacja dialog, 516 lista, 65 mapa, 65 oryginalny stan środowiska, 605 InitialDirContext, 433, 436 init-param, 440 initParam, 73 initXHR(), 477 innerHTML, 488 input, 25 inputDate, 549 inputHidden, 99, 112, 246 inputHtml, 549 inputSecret, 99, 111, 112, 113 inputText, 99, 102, 111, 112, 179 inputTextarea, 99, 111, 112 instalacja oprogramowania, 16 integer, 595 INTEGER, 215 INTEGER detail, 215 integerOnly, 209 Integrated Development Environments, 16 interfejs użytkownika, 46, 547 internacjonalizacja, 50 intRange, 595 invalidate(), 62 invitedguest, 455, 459 invoke application, 44 INVOKE APPLICATION, 277 isConnected(), 48 isNextButtonEnabled(), 520 isRendered(), 377 isRowAvailable(), 201 isTransient(), 382 isUserInRole(), 459, 464 isValueReference(), 353 itemDescription, 139 itemDisabled, 139 itemLabel, 139 ItemSearch, 466 itemSearch(), 466 ItemSearchRequest, 466 ItemSearchResponse, 468 itemValue, 139 izolowanie kodu języka JavaScript od mechanizmów wizualizacji, 495

J jar, 22 JAR, 23, 600 Java, 561 Java bean, 23

Java BluePrints, 21, 550 Java Database Connectivity, 409 Java EE, 15,419 Java EE 5, 17 Java Naming and Directory Interface, 24, 419 Java Plug-in, 561 Java SDK, 13 Java SE Development Kit, 16 Java Studio Creator, 550 java.sql.ResultSet, 187 java.text.MessageFormat, 231 java.text.SimpleDateFormat, 208 java.util.Locale.getlSOCountriesQ, 203 java.util.logging, 617, 620 java.util.logging.ConsoleHandler.level, 622 java.util.Properties, 50 java.util.TimeZone.getAvailablelDs(), 570 JavaBean, 47 javac, 23 JavaScript, 108 JavaServer Faces, 11, 15 JavaServer Pages, 11, 15 JavaServer Pages Standard Template Library, 49 javax.el.ValueExpression, 368, 396 javax.enterprise.resource.webcontainer.jsf, 622 javax.enterprise.resource.webcontainer.jsf.lifecycle, 620 javax.faces.application.Application, 353, 368 javax.faces.application.FacesMessage, 229 javax.faces.component.ActionSource, 353, 367 javax.faces.component.ActionSource2, 367 javax.faces.component.EditableValueHolder, 336, 353,367 j avax.faces .component. StateHolder, 3 82 javax.faces.component.UIComponent, 243, 332, 336, 346, 353, 368, 377 javax.faces.component.LIIInput.Conversion, 216 javax.faces.component.UIViewRoot, 234 javax.faces.component.ValueHolder, 339, 368 javax.faces.CONFIG_FILES, 64 javax.faces.context.ExtemalContext, 336, 441 javax.faces.context.FacesContext, 234, 333, 336, 352,396, 441,486 j avax. faces.context. Response Writer, 333 javax.faces.convert.ConverterException, 229 javax.faces.DoubleRange, 240 javax.faces.event.EditableValueHolder, 486 j avax. faces.event. FacesE vent, 252 javax.faces.event.MethodExpressionActionListener, 368 javax. faces, event. MethodExpression Value ^ChangeListener, 367 javax.faces.event.ValueChangeEvent, 252, 368 javax.faces.Input, 355 javax.faces.Length, 240

633

javax.faces.LongRange, 240 javax.faces.model, 187 javax.faces.model.DataModel, 187 javax.faces.model.Selectltem, 139, 378 javax.faces.model.SelectltemGroup, 143 j avax. faces .render. Renderer, 356 javax.faces.validator.MethodExpressionValidator, 367 javax.faces.validator.Validator, 221, 237 javax.faces.ViewState, 487 javax.faces.webapp.ConverterELTag, 395 javax.faces.webapp.UIComponentELTag, 346 javax.faces.webapp.UIComponentTag, 353 javax.faces.webapp.ValidatorTag, 407 javax.naming.Context, 436 javax.naming.directory.Attribute, 437 javax.naming.directory.Attributes, 437 javax.naming.directory.BasicAttributes, 437 javax.naming.directory.DirContext, 436 javax.naming.directory.InitialDirContext, 436 javax.naming.NamingEnumeration, 437 javax.servlet.error.exception, 584 javax.servlet.error.exceptionjype, 584 j avax.servlet, error, message, 5 84 javax.servlet.error.request uri, 584 javax.servlet.error.servlet_name, 584 javax.servlet.error.status_code, 584 javax.servlet.HttpServletRequest, 464 javax.servlet.jsp.jstl.Result, 187 javax.sql.DataSource, 415 JAX-WS, 466 JDBC, 409 Connection, 410 eliminowanie wycieków połączeń, 411 gotowe wyrażenia, 413 obiekt połączenia, 409 PreparedStatement, 413,414 pula połączeń, 411 ResultSet, 410 stosowanie gotowych wyrażeń, 413 wycieki połączeń, 411 wykonywanie wyrażeń języka SQL, 409 zarządzanie połączeniami, 411 źródło danych, 409 jdbc/mydb, 419 JDK, 16 jednowierszowe pola tekstowe, 111 język JavaScript, 108 SQL, 409 XHTML, 525 XML, 26 JND1, 24,419, 441,607 JPEG, 561 jscookMenu, 549

634

Skorowidz

JavaServer Faces

jsessionid, 62 JSF, 11, 15,535,541 JSF 1.0, 11 JSF 1.1,349 JSF 1.2, 16, 17,28 JSF Expert Group, 11 JSF HTML, 98, 99 jsfc, 526 jsflibs, 597 JSP, 11, 15,49 JSP 2.1, 511 jsp: forward, 31 jsp:include, 301 jsp:plugin, 561 JSTL, 49, 291,582 jsValueChangeListener, 549 K kafelki, 305 zagnieżdżone, 313 karty kredytowe, 227 katalog LDAP, 428 katalogi, 21 META-INF, 21 web, 21 key-class, 66 klasy ActionSource, 353 Application, 353 ComponentELTag, 343 Connection, 410 Converter, 229 ConverterELTag, 395 ConverterException, 229 DataModel, 187, 201 EditableValueHolder, 336, 353, 367 ELResolver, 608 ExtemalContext, 336 FacesContext, 234, 333, 336, 352, 396 FacesEvent, 252 FacesMessage, 229, 232 FileUploadRenderer, 555 implementacja komponentów niestandardowych, 325 InitialDirContext, 436 MessageFormat, 231 NamingEnumeration, 434 PreparedStatement, 413, 414 Property Resolver, 610 ResponseWriter, 333 ResultSet, 410 Selectltem, 141 SelectltemGroup, 142, 143

ServletContext, 440 SimpleDateFormat, 208 SortFilterModel, 194 TableData, 168 UlCommand, 329 UlComponent, 243, 327, 332, 336, 346, 353 UIComponentBodyTag, 349 UIComponentELTag, 346 UIComponentTag, 349, 353 UIData, 192 UIInput, 60, 329 UlOutput, 60, 329 UITabbedPane, 380 UIViewRoot, 234 UserBean, 21, 24, 47 ValidatorELTag, 343 ValueChangeEvent, 252 ValueExpression, 396 VariableResolver, 610 klasy CSS, 135 klasy obiektów, 428 klucze mapy, 141 kod HTML, 23 kod źródłowy aplikacji, 21 kod źródłowy biblioteki, 624 kodowanie, 40, 330 hard-core’owe, 15 style CSS, 377 kolejka komunikatów, 154 komentarze, 615 XML, 615 komórki tabeli danych, 177 kompilacja aplikacji, 22 Ant, 34 automatyzacja procesu, 34 build.xml, 35 komponenty, 16, 23, 45, 547 ADF Faces, 550 AJAX, 550 aliasBean, 549 bezpośrednie, 265 biblioteka MyFaces, 548 buffer, 549 calendar, 549 ChangeLocaleBean, 271 dataList, 549 dataScroller, 549 dataTable, 549 dostęp do wartości, 47 dostęp do zasobów dostępnych w pliku JAR, 600 encyjne, 540 facet, 329 fileUpload, 549 ICEfaces, 550

identyfikatory, 101 inputDate, 549 inputHtml, 549 interfejs użytkownika, 16 Java, 23 Java BluePrints, 550 JavaBean, 47 jscookMenu, 549 J S F ,174 jsValueChangeListener, 549 klasa, 28 konfiguracja, 64, 438 newspaperTable, 549 panelNavigation, 549 panelNavigation2, 549 panelStack, 549, 582 popup, 549 potomne, 329 prezentowanie, 582 RegisterForm, 147 rssTicker, 549 saveState, 549, 606 schedule, 549 stron, 60 stylesheet, 549 tabbedPane, 549 tree, 549 tree2, 549 treeColumn, 549 UIData, 166 ukrywanie, 582 user, 27 UserBean, 47 ustawianie wartości właściwości, 65 validatorScript, 589 value, 49 wejściowe, 60 wiązanie definicji, 66 właściwości, 23, 47 wspomagające, 47, 60 wyjściowe, 60 zarządzające danymi użytkownika, 20 zasięg, 28, 61 zasięg aplikacji, 62 zasięg sesyjny, 61 zasięg żądania, 62 komponenty AJAX, 490 hybrydowe komponenty, 491 hybrydowy komponent harmonijki JSF-Rico, 493 izolowanie kodu języka JavaScript od mechanizmów wizualizacji, 495 przekazywanie atrybutów znaczników JSP do kodu języka JavaScript, 496

635

komponenty niestandardowe, 39, 323 ActionSource, 329 ActionSource2, 329 Application, 330 atrybuty, 329 decode(), 336 definiowanie, 329 dekodowanie, 334 EditableValueHolder, 329, 336 encodeBeginQ, 330 encodeChildren(), 330 encodeEnd(), 330 ExtemalContext, 336 FacesContext, 330, 333, 336 generowanie znaczników, 330 getExtemalContext(), 336 getRequestParameterMap(), 336 implementacja znaczników, 339 interfejsy, 329 klasy implementujące, 325 kodowanie, 330 komponenty facet, 329 komponenty JavaBeans, 327 komponenty potomne, 329 kontener nazewnictwa, 333 obiekty nasłuchujące, 329 przetwarzanie danych wejściowych, 327 przetwarzanie wartości żądania, 334 ResponseWriter, 333 setConverter(), 339 setSubmittedValue(), 336 stosowanie konwerterów, 337 UlCommand, 329 UlComponent, 327, 332, 336 Ullnput, 329 UlOutput, 329 ValueHolder, 329, 339 wizualizacja, 327 wyrażenia reprezentujące wartość, 329 zestaw narzędzi, 328 znaczniki, 327, 330 komponenty wspierające, 60 binding, 60 wiązanie komponentu interfejsu użytkownika, 60 komponenty zarządzane, 45 @PostConstmct, 63 @PreDestroy, 63 adnotacje cyklu życia, 63 dostosowywanie, 46 konfiguracja, 46, 64 konwersja łańcuchów, 68 składnia wyrażeń reprezentujących wartości, 69 ustawianie wartości właściwości, 65 wiązanie definicji komponentów, 66 zasięg, 61

636

JavaServer Faces

komunikacja z serwerem, 369 komunikaty, 50, 154 błędy, 154 błędy krytyczne, 154 informacje, 154 ostrzeżenia, 154 wyświetlanie, 156 komunikaty o błędach, 211, 215, 611 błędy weryfikacji, 221, 222 dostęp do komunikatów zapisanych w pakiecie komunikatów, 230 modyfikacja treści, 214 niestandardowe komunikaty, 214 komunikaty obejmujące zmienne, 51 konfiguracja dialog, 516 komponent, 64, 438 kontekst zewnętrzny, 440 mechanizm logowania, 456 serwer LDAP, 430 serwlet, 29 ustawienia regionalne aplikacji, 52 zasoby baz danych w ramach serwera GlassFish, 415 zasoby baz danych w ramach serwera Tomcat, 417 zasoby zarządzane przez kontener, 441 źródło danych, 414 kontekst katalogu LDAP, 433 kontekst zewnętrzny, 440 kontener nazewnictwa, 333 kontenery serwletów, 61 kontroler kafelków, 313 kontrolki formularza, 109 kontrolki przeciągnij i upuść, 491 konwersacja z użytkownikiem, dialogu, 512 konwersja danych, 38, 205, 206 błędy, 211 converter, 210 daty, 207 f:convertDateTime, 208 f:convertNumber, 208 komunikaty, 211 komunikaty o błędach, 215 konwertery, 216 liczby, 207 niestandardowy konwerter, 211 przekazywanie konwerterom atrybutów, 242 raportowanie błędów, 229 treść komunikatów o błędach, 214 wyświetlanie komunikatów o błędach, 211 wyświetlanie wszystkich komunikatów o błędach, 213 konwersja łańcuchów, 68, 70 konwersja na obiekt, 210

Skorowidz konwertery, 102, 207, 337 atrybuty, 209 binding, 210 dołączanie, 210 stosowanie, 207 konwertery niestandardowe, 225, 234, 393 aplikacja, 397 ConverterELTag, 395 FacesContext, 396 przywracanie stanu, 396 ValueExpression, 396 zapisywanie stanu, 396 znaczniki, 393 kreator, 512 kreator płatności, 514 książka adresowa, 535

L label, 112 lang, 105, 107, 119 languageCode, 272 layout, 135, 155, 161 Layout, 132 LDAP, 428 Active Directory, 430 aplikacje, 445 atrybuty, 428 Attribute, 437 Attributes, 437 BasicAttributes, 437 baza danych, 428 Context, 436 DirContext, 436 dodawanie wpisów, 434 dostęp do informacji, 433 drzewo katalogów, 429 edycja atrybutów wpisu, 435 elementy drzewa, 428 InitialDirContext, 436 katalogi, 428 klasy obiektów, 428 konfiguracja serwera, 430 kontekst katalogu, 433 NamingEnumeration, 434, 437 OpenLDAP, 430 połączenie z serwerem, 433 przeglądanie drzewa katalogów, 432 serwer, 430 Sun Java System Directory Server, 430 Unikatowa nazwa, 429 usuwanie wpisów, 435 uwierzytelnianie, 431 użytkownicy, 430

ldapadd, 431 LdapCtxFactory, 443 Length Validator, 220, 222 LengthValidator.MAXIMUM, 222 LengthValidator.MINIMUM, 222 liczby, 207 life cycle, 42 lifecycle, 273 lineDirection, 135 LinkedHashMap, 141 List, 65 ListDataModel, 187 list-entries, 65, 66 listy, 65, 135, 146 wielokrotnego wyboru, 136 load(), 50 loadBundle, 96 locale, 53, 209 locale-config, 53 localhost, 62 logAbandoned, 418 logika biznesowa, 23 login(), 422 login.war, 22 loginAction, 421 login-config, 456 logout.jsp, 78 logoutAction, 421 logowanie, 456 lokalizacja, 373 lokalizacja pakietu komunikatów, 51 lokalizacja plików konfiguracyjnych, 599 LongRangeValidator, 220, 222 LongRangeValidator.MAXIMUM, 222 LongRangeValidator.MINIMUM, 222 LongRange Validator.NOT_IN_RANGE, 222 LongRangeValidator. TYPE, 222 lookup(), 436 l-value, 70 1-wartość, 70

l ładowanie klas, 50 łańcuchy, 206 łańcuchy komunikatów, 50 łącza, 120, 269 łącza poleceń, 126

M managed-bean, 46, 47, 64 managed-bean-class, 28, 46, 64, 438 managed-bean-name, 46, 64, 438

managed-bean-scope, 28, 46, 64 managed-property, 65, 438 Map, 65, 66 mapa atrybutów, 97 mapa facet, 97 mapa obrazów, 558 usemap, 558 map-entries, 66 mapowanie serwletów, 29 mapy, 65 mask, 595 maxActive, 418 maxFractionDigits, 209 maxldle, 418 maxIntegerDigits, 209 maxlength, 105, 595 mechanizmy logowanie, 456 nasłuchiwanie akcji, 265 podział na strony, 570 rendering, 40 Shale Validator, 593 śledzenie faz, 277 uwierzytelnianie, 457 weryfikacja danych, 102, 218, 223 menedżer encji EJB, 543 menu, 135, 146 menuClass, 308 menultems, 142 message, 99, 154, 211 MessageFormat, 52, 231 messages, 99, 154 Messages, 232 META-INF, 21 MethodBinding, 351 MethodExpression, 350, 362 MethodExpressionActionListener, 368 MethodExpressionValidator, 367 MethodExpressionValueChangeListener, 367 metoda GET, 107 metoda POST, 107 metody, 75 dostępowe, 24 get, 24 nasłuchujące akcji, 256 nasłuchujące zdarzeń, 263 set, 24 ustawiające, 48 zwracające, 48 Microsoft Active Directory, 430 minFractionDigits, 209 minlntegerDigits, 209 minlength, 595 model, 38

637

638

JavaServer Faces

model DOM, 476 modele tabel, 187 edycja, 188 filtrowanie, 192 sortowanie, 192 model-widok-kontroler, 38 modifyAttributesQ, 435, 437 modyfikacja treści standardowych komunikatów o błędach, 214 monitor HTTP, 619 monolityczne strony JSF, 295 multipart/form-data, 550, 551 MVC, 38 MyFaces, 548, 582 H name, 98 NamingEnumeration, 434, 437 narzędzia projektowania wizualnego, 32 nasłuchiwanie akcji, 265 native2ascii, 51 navigation-case, 78, 89, 91 navigation-rule, 78, 87, 89, 92 nawiasy kwadratowe, 71 nawigacja, 27, 77, 257 algorytm, 92 from-action, 91 from-view-id, 90 navigation-case, 91 navigation-rule, 92 przekierowanie, 89 redirect, 89 reguły, 27 symbole wieloznaczne, 90 to-view-id, 89 zaawansowane techniki, 88 nawigacja dynamiczna, 79 action, 80 przycisk akceptacji formularza, 79 reguły, 87 rozgałęzienia, 80 wyrażenia odwołujące się do metody, 79 nawigacja statyczna, 77 action, 77 reguły, 78 nazwa komponentu, 27 NetBeans, 31,614 newspaperTable, 549 next(), 437 niestandardowe komponenty, 323 niestandardowe komunikaty o błędach, 214 niestandardowe konwertery, 224, 323

Skorowidz niestandardowe mechanizmy weryfikacji, 224,323,393 implementacja, 237 RegexValidatorTag, 403 ValidatorTag, 407 znaczniki, 401 niestandardowy znacznik weryfikacji po stronie klienta, 587 NOT IN RANGE, 221 NullPointerException, 612 null-value, 65 Number, 210 NUMBER_detail, 215 NumberConverter, 214 numery kart kredytowych, 227 o obieg stron internetowych, 511 obiekty, 45 biznesowe, 47 nasłuchujące, 106, 329 wyniki biblioteki JSTL, 187 XMLHttpRequest, 477, 478 ObjectFactory, 442, 443 obramowanie, 135 obrazy, 118 JPEG, 561 obsługa błędy, 39 błędy konwersji, 326 metody nasłuchujące zmian wartości, 360 narzędzia, 39 style CSS, 377 wyrażenia wskazujące na metody, 361 wysyłanie plików na serwer, 550 obsługa zdarzeń, 249, 281 actionListener, 263, 264 bezpośrednie komponenty poleceń, 268 bezpośrednie komponenty wejściowe, 266 FacesEvent, 252 komponenty bezpośrednie, 265 metody nasłuchujące akcji, 256 PhaseListener, 274 przekazywanie danych z interfejsu użytkownika na serwer, 269 ValueChangeEvent, 252 valueChangeListener, 263, 264 ValueChangeListener, 264 oddzielanie logiki interfejsu użytkownika od logiki biznesowej, 258 odwzorowywanie przedrostki, 29 serwlety, 29

ograniczanie komunikacji z serwerem, 369 onblur, 106 onchange, 106 onclick, 106, 107, 121,574 ondblclick, 106, 107 onfocus, 106, 107 onkeydown, 106, 107 onkeypress, 106, 107 onkeyup, 106, 107 onmousedown, 106, 107 onmousemove, 106, 107 onmouseout, 106, 107 onmouseover, 106, 107 onmouseup, 106, 107 onreset, 106, 107 onselect, 106 onsubmit, 106, 107 opener, 576 OpenLDAP, 430 operatory, 74 arytmetyczne, 74 empty, 74 logiczne, 74 relacyjne, 74 trój argumentowy operator selekcji, 74 optgroup, 143 org.apache.shale.faces.AbstractFacesBean, 519 org.apache.struts.tiles.Controller, 313 oryginalny stan środowiska, 605 ostrzeżenia, 154 ou, 428 outject, 545 outputFormat, 97, 99, 118, 119 outputLabel, 99 outputLink, 99, 120, 124, 125 outputText, 99, 118, 119, 120, 179

P page beans, 60 pageDirection, 135 PagerRenderer, 571 pakiet Apache Tiles, 305 pakiet Shale, 512 pakiety komunikatów, 50 deklaracja, 50 komunikaty obejmujące zmienne, 51 lokalizacja, 51 ustawienia regionalne, 52 pakowanie zbiom znaczników w ramach pliku JAR, 604 panele, 159 panelGrid, 159, 160 zakładki, 373, 387

panelGrid, 99, 159, 160 panelGroup, 99, 161 panelNavigation, 549 panelNavigation2, 549 panelStack, 549, 582 param, 96, 97, 98, 270 Param, 73 parametry puli połączeń, 418 parametryzacja kafelków, 308 param Values, 73 PartialResolution, 609 pasek przewijania, 201 password, 24, 418, 584 pattern, 209 PATTERN TYPE, 215 PERCENT_detail, 215 phase events, 273 Phaseld.ANY PHASE, 275, 276 Phaseld.APPLY REQUEST VALUES, 274 phaseListener, 96 PhaseListener, 274, 606 placehorders, 52 pliki build.xml, 35 deskryptor TLD, 340 dziennik, 616 faces-config.xml, 27, 28, 64, 438 index.html, 30 index .jsp, 30 JAR, 23, 600 jsf, 24 jsp, 24 konfiguracyjne, 20 properties, 50 server.xml, 438 WAR, 21 web.xml, 29 welcome .jsp, 26 poddialogi, 522 podstawowe znaczniki JSF, 96 podwidoki, 291, 302 podział na strony, 570 PO JO, 541 pola hasła, 111 pola tekstowe, 25, 111 stosowanie, 114 pola ukryte, 107, 114, 373, 378 pola wyboru, 133, 146 połączenia z bazą danych, 183 z serwerem LDAP, 433 pomijanie procesu weryfikacji danych, 221 pomoc podręczna, 550 poolPreparedStatements, 418

639

640

JavaServer Faces

popup, 549 POST, 41, 107 PreparedStatement, 413, 414 prepareStatement(), 413, 414 prependld, 107 prezentacja, 23 duże zbiory danych, 570 prezentowanie komponentów, 582 PrintWriter.writeQ, 495 proces konfiguracji komponentu, 439 process validations, 43 process Aj axCall(), 477, 478 processRealtimeValidation(), 486 processZipCodeSelection(), 481 programowanie, 596 Eclipse, 596 sterowane zdarzeniami, 16 programowe tworzenie komponentów, 102 projektowanie interfejs użytkownika aplikacji internetowej, 547 wizualne, 32 propagowanie stanu widoku po stronie klienta, 487 PropertyEditor, 69 Property Resolver, 610 protokoły HTTP, 61 SOAP, 464 Prototype, 479 przeciągnij i upuść, 491 przeglądarka książek, 292, 294 przekazywanie atrybutów znaczników JSP do kodu języka JavaScript, 496 przekazywanie danych z interfejsu użytkownika na serwer, 269 przekazywanie konwerterom atrybutów, 242 przekierowanie, 89 przepisywanie adresów URL, 62 przepływ stron internetowych, 511,512 przetwarzanie facet, 376 iteracyjne, 410 wartości żądania, 334 wyrażenia wskazujące na metody, 362 znaczniki potomne typu Selectltem, 375 żądania AJAX, 476 przewijanie zawartości tabeli danych, 201 dodatkowe łącza, 202 pasek przewijania, 201 przycisk akceptacji formularza, 79 przyciski, 120 przyciski opcji, 133, 146 przyciski poleceń, 121 przyciski typu włączony-wyłączony, 121

Skorowidz przywracanie oryginalny stan środowiska, 605 stan, 379, 396 widok, 43 pula połączeń z bazą danych, 411 Tomcat, 418 punkty wstrzymania, 614 push button, 121 put(), 437 Q queue(), 252 Quiz liczbowy, 54 QuizBean, 54

R raportowanie błędów konwersji, 229 readonly, 105 Realm, 458 redirect, 89 redisplay, 111, 112, 113 RegexValidatorTag, 403 registereduser, 455, 459 RegisterForm, 147 reguły nawigacji, 25, 78, 87 definiowanie, 27 rejestrowanie mechanizm weryfikacji, 240 zdarzenia, 611 rei, 105 relacje pomiędzy komponentami, 595 relacje weryfikacji łączące wiele komponentów, 243 release(), 346, 396 remove Abandoned, 418 removeAbandonedTimeout, 418 removeDataModelListener(), 201 render response, 43 RENDER RESPONSE, 606 rendered, 101, 103, 104, 107, 119, 582 Renderers, 360 getConvertedValue(), 359, 360 getSelectItems(), 376 rendering, 40 renderResponse(), 251, 268 rendersChildren(), 375 request value, 206 requestScope, 73 required, 100, 101, 111, 112, 220, 595 requiredMessage, 101,221 resource injection, 419 resource-bundle, 51

responseComplete(), 251, 486, 568 ResponseWriter, 328, 330, 332, 333 restoreState(), 380, 381, 382, 396 Result, 187 ResultDataModel, 187 ResultSet, 187,410 ResultSetDataModel, 187, 201 rev, 105 Rico, 491 Rico.Accordion, 492 rico:accordion, 494, 497 ricoiaccordionPanel, 494 rodziny komponentów, 355 role-name, 455 rowClasses, 160, 170, 181, 182 rows, 105, 111, 112, 170 rozgałęzienia, 80 rozmieszczenia elementów na stronie, 291 rozszerzanie języka wyrażeń technologii JSF?, 607 rozszerzanie kafelków, 309 rssTicker, 549 Ruby on Rails, 607 rules, 160, 170 r-value, 70 r-wartość, 70

s s: validatorS cript, 594 saveState, 549, 606 saveState(), 380, 381,382, 396 ScalarDataModel, 187 schedule, 549 Seam, 535, 607 Contact, 541 fabryka, 544 grupowanie wzajemnie powiązanych stron, 607 integracja z modelem danych JSF, 544 komponenty encyjne, 540 konfiguracja aplikacji, 539 koniec konwersacji, 546 konwersje, 607 książka adresowa, 535 menedżer encji EJB, 543 stanowe komponenty sesyjne, 541 wstrzyknięcie, 542 zasięg konwersacji, 545 zasięg zdarzenia, 541 security-constraint, 455 select, 136 selectBooleanCheckbox, 99, 131, 132, 133, 178 selectedPanel, 582 selectedTabClass, 377 selectltem, 96, 134, 136, 138

641

Selectltem, 139, 140, 141, 142, 375, 378 SelectltemGroup, 142, 143 selectltems, 96, 138, 140 selectMany, 144 selectManyCheckbox, 99, 131, 132, 133, 134 selectManyListbox, 99, 131, 132, 135, 136, 141, 145 selectManyMenu, 99, 131, 132, 135, 137 selectOneListbox, 99, 131, 132, 135 selectOneMenu, 99, 106, 131, 132, 135, 137 selectOneRadio, 99, 131, 132, 134, 144 selekcja, 130 selektywne prezentowanie komponentów, 582 sendRequest(), 477 separatory, 25 Serializable, 396 serializacja, 396 server.log, 616 server.xml, 417, 438, 459 servlet, 601 ServletContext, 440 ServletContextListener, 606 servlet-mapping, 30 serwer GlassFish, 17, 22 LDAP, 428 OpenLDAP, 430 serwlety, 29 filtrujące, 551 konfiguracja, 29 mapowanie, 29 odwzorowywanie, 29 web.xml, 29 sesja, 61 sessionCreated(), 606 sessionDestroyed(), 606 sessionScope, 73 set, 24, 48 setAction(), 352, 353 setActionListener(), 353 setAsText(), 69 setBoolean(), 351 setConverter(), 337, 339, 359 setLocale(), 53 setName(), 25, 65 setPassword(), 47, 65 setPhase(), 275 setPhaseId(), 252 setProperties(), 344, 346, 350, 351, 362, 496 setPropertyActionListener, 96, 272 setPropertyResolved(), 608 setRendererType(), 344 setRowIndex(), 201 setScoreComponent(), 60 setSelectltemsQ, 144

642

JavaServer Faces

setSeverity(), 229 setString(), 351 setSubmittedValueQ, 336 setTransient(), 382 setValid(), 336, 337 setValidator(), 353 setValidatorld(), 407 setValue(), 520 setValueBinding(), 351, 353 setValueChangeListener(), 353 setValueExpression(), 346 setWrappedData(), 188, 195,201 Shale, 512 BaseDialogPage, 520, 521 dialog, 512 dialog-config.xml, 516 inicjowanie dialogu, 516 konfiguracja dialogu, 516 kreator, 512 nawigacja w ramach dialogu, 517 poddialogi, 522 stan akcji, 517 stan końcowy, 512 stan widoku, 517 zależność od kontekstu dialogu, 520 zasięg dialogu, 518 Shale Validator, 593 shape, 105 ShoppingCartBean, 62 short, 595 showDateAndTime(), 477 showDetail, 155 showSummary, 155 Simple Object Access Protocol, 464 SimpleDateFormat, 208 size, 105, 136 składnia wyrażeń reprezentujących wartości, 69 składowanie komponentu zarządzanego poza zasięgiem żądania, 606 skrypty JavaScript, 108 ograniczanie komunikacji z serwerem, 369 slapd.conf, 430 sn, 428 SOAP, 464, 466 sortByFirst(), 194 sortByLast(), 194 SortFilterModel, 194 sortowanie, 192 spinnerLib, 601 SpinnerTag, 340 sprawdzanie długości łańcuchów, 219 SQL, 409 SQLException, 412 SSL, 457

Skorowidz stan, 379 stan akcji, 517 stan końcowy, 512 stan widoku, 484, 517 standardowe komunikaty o błędach weryfikacji, 222 standardowe mechanizmy weryfikacji, 218 stanowe komponenty sesyjne, 541 startElement(), 332, 333 StateHolder, 380, 382, 396 Statement, 410 stosowanie komponent panelu podzielonego na zakładki, 387 konwerter, 337 łącza poleceń, 126 pola tekstowe, 114 pola ukryte, 378 wartość żądania, 43 zewnętrzny mechanizm wizualizacji, 354 strona błędu uwierzytelniania, 421 strona index.jsp, 27 strona JSF, 24 strona powitalna, 30 struktura katalogów aplikacji, 21 Struts, 595 style, 103, 105, 107, 118, 119 style CSS, 103,377 styleClass, 101, 107, 118, 119, 155, 180, 283,377 stylesheet, 549 submitted value, 206 subview, 96, 301 summary, 160, 170 Sun Java Studio Creator, 33 Sun Java System Directory Server, 430 sun-web.xml, 442, 458 supported-locale, 53 symbole wieloznaczne, 90 symbole zastępcze, 52 System.err, 616 System.out, 616 SystemPropertyResolver.PartialResolution, 609 szukanie dodatkowych komponentów, 547

ś ścieżka interfejsu JNDI, 420 ślad stosu, 611,613 z piekła rodem, 613 śledzenie fazy, 277 sesja, 61, 62 środowiska wytwarzania, 31 Ant, 34 automatyzacja procesu kompilacji, 34 Eclipse, 31

narzędzia projektowania wizualnego, 32 NetBeans, 31 Sun Java Studio Creator, 33

T tabbedPane, 549 TabbedPaneRenderer, 376, 382 tabClass, 377 tabele bazy danych, 183 Result, 187 ResultSet, 187 zbiory wynikowe, 187 tabele danych, 165, 166 atrybuty, 170 CSS, 180 DataModel, 187 edycja komórek, 177 edycja modeli tabel, 188 filtrowanie, 192 h:dataTable, 165 kolumny, 181 komórki, 177 komponenty JSF, 174 modele, 187 nagłówki, 171 podpisy, 171 przewijanie, 201 SortFilterModel, 194 sortowanie, 192 stopki, 171 style, 180 style kolumn, 181 style wierszy, 182 TableData, 168 wiersze, 182 tabele HTML, 165 tabindex, 105, 283 Table, 166 TableData, 168 deleteNamesQ, 189 taglib, 95, 340 Tapestry, 529, 535 target, 105, 107 techniki przewijania, 201 techniki wytwarzania aplikacji internetowych, 15 technologie EJB, 535 Facelets, 525 JSF, 15, 16 tekst, 118 tekstowe pola wejściowe, 111 telefon komórkowy, 25 tile controllers, 313

643

tiles, 305 tiles: import Attribute, 308 tiles:insert, 306 TIME_detail, 215 timeStyle, 209 timeZone, 209 title, 105, 107, 1 19, 283 TLD, 340 Tomcat, 417 konfiguracja zasobów baz danych, 417 parametry puli połączeń, 418 tooltip, 155 toStringO, 55 to-view-id, 78, 89 tradycyjny obiekt Javy, 541 tree, 549 tree2, 549 treeColumn, 549 TreeMap, 141 trój argumentowy operator selekcji, 74 tryb rejestrowania zdarzeń związanych z pracą kontenera JSF, 620 try-fmally, 410, 412 tworzenie aplikacje JSF w środowisku Eclipse, 596 aplikacje LDAP, 445 niestandardowy znacznik weryfikacji po stronie klienta, 587 wyskakujące okna, 574 zdarzenia akcji, 382 type, 105, 122, 209

u ui:composition, 533 ui:defme, 533 ui:include, 533 UlCommand, 328, 329, 355 UlComponent, 243, 327, 328, 332, 336, 346, 353, 368 getAttributes(), 351 UIComponentBodyTag, 349 UIComponentELTag, 326, 328, 344, 346 UIComponentTag, 349, 353 uid, 428 UIData, 166, 192,355 UIForm, 355 UIGraphic, 355 Ullnput, 40, 42, 60, 222, 328, 329, 355, 360 getConvertedValue(), 360 REQUIRED, 222 UIMessage, 355 UIMessages, 355

644

Skorowidz

JavaServer Faces

UlOutput, 60, 329, 355 UlPanel, 355 UISelectBoolean, 355 UlSeleetltem, 375 UISelectMany, 355 UlSelectOne, 355 UlSpinner, 328,331,337,355 encodeBegin(), 332 UITabbedPane, 328, 376, 380, 381 UIViewRoot, 53, 234, 608 układy, 292 ukryte pola wejściowe, 246 ukrywanie komponentów, 582 umiędzynarodowienie, 39, 51, 56 unikatowa nazwa, 429 update model values, 43 UploadFilter.java, 552 uploadlmage.jsp, 557 UploadRenderer.java, 555 UploadTag.java, 556 url, 418 URL, 30, 62 usemap, 558 user, 27 user.name, 40 UserBean, 21, 24, 32, 46, 47, 421, 422 UserBeanjava, 20, 21 UserDirectoryBean, 451 username, 418 usługi frameworka JSF, 37 usługi sieciowe, 464 @WebServiceRef, 467 Amazon Web Services, 464 artefakty klienta, 467 deskryptor, 465 komponenty, 464 neutralność językowa, 465 opis usługi, 464 SOAP, 464, 466 WSDL, 464, 465 usługi zewnętrzne, 409 ustawianie wartości właściwości, 65 ustawienia regionalne, 51, 52 definiowanie, 53 utrzymywanie sesji, 61 uwierzytelnianie, 457 uwierzytelnianie zarządzane przez kontener, 455 domena, 457 SSL, 457 użytkownicy, 456 uzupełnianie danych formularzy, 480 Ajax4jsf, 498

v validate!), 239, 242 validateCreditCard!), 593 validateDoubleRange, 96, 220 validateLength, 96, 219, 220 validateLongRange, 96, 220 validator, 75, 96, 100, 101, 103, 220, 240, 362 Validator, 221,237,239, 242 validator-class, 240 ValidatorELTag, 343 ValidatorException, 238, 239, 401 validator-id, 240 validatorld, 240 validatorMessage, 101,221 validatorScript, 587, 589, 591 ValidatorTag, 403, 407 value, 25,49, 65,98, 100, 101, 102, 111, 119, 122, 124, 139, 166 wiązanie atrybutu, 144 ValueBinding, 350 ValueChangeEvent, 252, 368 valueChangeListener, 75, 96, 100, 101, 111, 112, 249, 251, 263, 264, 360, 361, 362 ValueChangeListener, 264 value-class, 65, 66 ValueExpression, 344, 350, 368, 396 ValueHolder, 329, 339, 368 valueOf(), 68 var, 170 VariableResolver, 73, 610 verbatim, 96, 560 Verify Project, 614 view, 73, 96, 301 view state, 517 ViewState, 487

w waluta, 52 WAR, 21,614 wamClass, 155 wamStyle, 155 warstwa prezentacji, 23 wartości, 49, 102 lokalne, 206 właściwości, 65 wymagane, 220 wyrażenia przetwarzane w czasie wykonywania, 341 wysłane, 206 żądanie, 206

wdrażanie aplikacje JavaServer Faces, 29 katalogi, 615 „na gorąco”, 614 web, 21 web flow, 511 Web Service Description Language, 464 web services, 464 web.xml, 20, 29, 30, 599 WEB-INF, 340 Weblets, 601 web-resource-collection, 455 Web work, 535 welcome.jsp, 20, 26 welcome-file, 31 weryfikacja danych, 39, 43, 205, 587 Commons Validator, 594 creditCardValidator, 587 długość łańcucha, 219 fvalidateDoubleRange, 220 EvalidateLength, 220 fvalidateLongRange, 220 fvalidator, 220 klasy niestandardowych mechanizmów, 237 komponenty JavaBeans, 242 komunikaty o błędach, 221 komunikaty o błędach weryfikacji, 222 mechanizm, 223 niestandardowe mechanizmy, 224 niestandardowy znacznik weryfikacji po stronie klienta, 587 obiekt konwertera, 242 po stronie klienta, 587 pomijanie procesu weryfikacji, 221 przedziały liczbowe, 219 rejestrowanie mechanizmów, 240 relacje weryfikacji łączące wiele komponentów, 243 requiredMessage, 221 Shale Validator, 593 standardowe mechanizmy, 218, 220 Struts, 595 ukryte pola wejściowe, 246 validate!), 242 Validator, 221,237 ValidatorException, 238 validatorMessage, 221 wartości wymagane, 220 wyświetlanie komunikatów o błędach, 221 weryfikacja danych w czasie rzeczywistym, 482 Ajax4jsf, 502 weryfikacja relacji pomiędzy komponentami, 595

wiązanie atrybut value, 144 definicja komponentów, 66 widok, 38, 524 JSF, 302 XHTML, 525 width, 105 wielowierszowe pola tekstowe, 111 window.document.getElementByld!), 481 window.open(), 574 window.openerFormld, 577 wizualizacja, 354 odpowiedzi, 43, 63 strony, 40 warunkowa, 103 właściwości, 23 komponenty, 47 wzorzec nazewniczy, 48 writeAttribute(), 332, 334 writeValidationFunctions(), 589, 592 WSDL, 464, 465 wskazywanie konwerterów, 227 wstrzykiwanie zależności, 467 wstrzykiwanie zasobów, 419 @WebServiceRef, 467 adnotacje, 420 wybór opcji, 130 wycieki połączeń, 411 wygląd stron o błędach, 583 wyjątki serwletów, 584 wykonywanie wyrażeń języka SQL, 409 wykresy, 561 wykrywanie błędów, 613 wyrażenia odwołujące się do metod, 75, 79 obsługa, 361 wyrażenia reprezentujące wartości, 49, 329 listy, 71 1-wartość, 70 mapy, 71 nawiasy kwadratowe, 71 obiekty predefiniowane, 73 odwołania do map i list, 71 operatory, 74 rozwiązywanie wyrazu początkowego, 72 r-wartość, 70 składnia, 69 wyrażenia odwołujące się do metod, 75 złożone wyrażenia, 74 wyskakujące okna, 574 wysyłanie plików na serwer, 550 DiskFileUpload, 554 FileUploadRenderer, 555 serwlet filtrujący, 551

645

646

Skorowidz

JavaServer Faces

wysyłanie plików na serwer UploadFilter.java, 552 uploadlmage.jsp, 557 UploadRenderer.java, 555 UploadTag.java, 556 wysyłanie plików ze wskaźnikiem postępu, 550 wysyłanie żądania AJAX, 481 wyświetlanie komunikaty, 156 komunikaty o błędach, 211 komunikaty o błędach weryfikacji, 221 mapa obrazów, 558 obraz, 118 tekst, 118 wytwarzanie błyskawiczne, 15 wywołanie aplikacji, 44 wywołanie konwerterów z poziomu zewnętrznych mechanizmów wizualizacji, 359 wzorzec nazewniczy właściwości, 48 wzór Luhna, 239

X XHR, 476, 478 XHTML, 525 XML, 26 XMLBuddy, 28, 599 XMLHttpRequest, 477 XSS, 118

z zaawansowane techniki nawigacji, 88 zależność od kontekstu dialogu, 520 zapisywanie stanu, 379, 396 zapytania SQL, 409 zarządzanie dane konfiguracyjne, 438 połączenia bazodanowe, 411 zasięg aplikacji, 73, 438 zasięg błyskowy, 607 zasięg dialogu, 518 zasięg komponentów, 61 adnotacje cyklu życia, 63 zasięg aplikacji, 62 zasięg sesji, 61 zasięg żądania, 62 zasięg konwersacji, 545 zasięg sesji, 73 zasięg zdarzenia, 541 zasięg żądania, 73 zbiory wynikowe, 187

zdarzenia, 104, 249 akcje, 249, 256 cykl życia, 250 DHTML, 104, 106 fazy, 249, 273 komponenty poleceń, 249 komponenty wejściowe, 249 metody nasłuchujące akcji, 256 po stronie serwera, 268 zmiana wartości, 249, 251 zewnętrzne mechanizmy wizualizacji, 354 obsługa metod nasłuchujących zmian wartości, 360 obsługa wyrażeń wskazujących na metody, 361 wywoływanie konwerterów, 359 zewnętrzne źródła danych, 47 zgodne zasięgi komponentów, 68 ziarno, 46 zintegrowane środowisko programowania, 16, 31 ZipCodeDirectory, 490 zmiana lokalizacja plików konfiguracyjnych, 599 wartości, 251 zmienne, 25 znaczniki, 19 applet, 560 atrybuty, 100 atrybuty wspólne, 100 c:import, 301 f:actionListener, 264 f: attribute, 242, 271 f:convertDateTime, 208 f:convertNumber, 208 fiparam, 270 f:selectltem, 138 fselectltems, 138, 140 fsetPropertyActionListener, 272 fisubview, 301 f:valueChangeListener, 264 f:view, 25, 301 h:column, 166 h:commandB utton, 120, 121 h:commandLink, 120 h:dataTable, 165 h:form, 25 h:graphiclmage, 118, 119 hunputHidden, 114 hunputSecret, 111, 113 h:inputText, 111, 179 h:inputTextarea, 114 h:message, 154, 211 h:messages, 154 h:outputFormat, 118 h:outputLink, 120, 124 h:outputText, 118, 179

h:panelGrid, 159, 160 h:panelGroup, 161 h:selectBooleanCheckbox, 131, 133, 178 h:selectManyCheckbox, 131, 133 h:selectManyListbox, 131, 135, 141 h:selectManyMenu, 131, 135, 137 h:selectOneListbox, 131, 135 h:selectOneMenu, 131, 135, 137 h:selectOneRadio, 131, 144 HTML, 25 JSF, 96 JSF HTML, 98, 99 jspiinclude, 301 JSTL, 582 komponenty niestandardowe, 339 konwertery niestandardowe, 393 managed-bean-class, 28 managed-bean-scope, 28 metody nasłuchujące zdarzeń, 263 niestandardowe mechanizmy weryfikacji, 401 podstawowe, 25 przedrostki, 25 selekcja, 130 tiles: importAttribute, 308 tilesunsert, 306 znaczniki niestandardowe, 339 ActionSource, 353 Application, 353 atrybuty dowiązań do metod, 351 ciało, 349 ComponentELTag, 343 definiowanie klas obsługujących znaczniki w technologii JSF 1.1, 349 Editable ValueHolder, 353 Facelets, 533

FacesContext, 352 implementacja, 339 JSF 1.1,349 klasa obsługująca znacznik, 343 MethodBinding, 351 plik deskryptora TLD, 340 taglib, 340 UlComponent, 346, 353 UIComponentBodyTag, 349 UIComponentELTag, 346 UIComponentTag, 349, 353 ValidatorELTag, 343 znaczniki standardowe JSF, 95 biblioteka znaczników, 95 taglib, 95 związki, 101

ź źródło danych, 409 dostęp do zasobów zarządzanych przez kontener, 419 GlassFish, 415 JNDI, 419 konfiguracja, 414 Tomcat, 417

2 żądania, 334 AJAX, 476, 486 GET, 107 POST, 41

647
Geary D. Cay S. Horstmann - Core JavaServer Faces. Wydanie II

Related documents

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

7 Pages • 111 Words • PDF • 675.2 KB

2 Pages • 23 Words • PDF • 69.7 KB

1,646 Pages • 773,881 Words • PDF • 108.2 MB

8 Pages • 3,150 Words • PDF • 611.2 KB

1 Pages • 129 Words • PDF • 38.8 KB

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

355 Pages • 214,413 Words • PDF • 145.3 MB

8 Pages • PDF • 4.5 MB

92 Pages • 50,256 Words • PDF • 10.7 MB

3 Pages • 2,824 Words • PDF • 72.8 KB

322 Pages • PDF • 43.6 MB